mirror of
https://github.com/indig0fox/Arma3-AttendanceTracker.git/
synced 2025-12-08 09:51:47 -06:00
Merge branch 'feature/track-sessions' into main
This commit is contained in:
Binary file not shown.
@@ -1,91 +0,0 @@
|
||||
[
|
||||
["OnUserConnected", {
|
||||
params ["_networkId", "_clientStateNumber", "_clientState"];
|
||||
|
||||
[format ["(EventHandler) OnUserConnected fired: %1", _this], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
|
||||
private _userInfo = (getUserInfo _networkId);
|
||||
_userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"];
|
||||
if (_isHC) exitWith {};
|
||||
|
||||
[
|
||||
"ConnectedServer",
|
||||
_playerID,
|
||||
_playerUID,
|
||||
_profileName,
|
||||
_steamName
|
||||
] call attendanceTracker_fnc_logServerEvent;
|
||||
|
||||
(AttendanceTracker getVariable ["allUsers", createHashMap]) set [_networkId, _userInfo];
|
||||
}],
|
||||
["OnUserDisconnected", {
|
||||
params ["_networkId", "_clientStateNumber", "_clientState"];
|
||||
|
||||
[format ["(EventHandler) OnUserDisconnected fired: %1", _this], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
|
||||
private _userInfo = (AttendanceTracker getVariable ["allUsers", createHashMap]) get _networkId;
|
||||
if (isNil "_userInfo") exitWith {};
|
||||
|
||||
_userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"];
|
||||
if (_isHC) exitWith {};
|
||||
|
||||
[
|
||||
"DisconnectedServer",
|
||||
_playerID,
|
||||
_playerUID,
|
||||
_profileName,
|
||||
_steamName
|
||||
] call attendanceTracker_fnc_logServerEvent;
|
||||
}],
|
||||
["PlayerConnected", {
|
||||
params ["_id", "_uid", "_name", "_jip", "_owner", "_idstr"];
|
||||
|
||||
[format ["(EventHandler) PlayerConnected fired: %1", _this], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
|
||||
private _userInfo = (getUserInfo _idstr);
|
||||
if (isNil "_userInfo") exitWith {};
|
||||
|
||||
_userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"];
|
||||
|
||||
(AttendanceTracker getVariable ["allUsers", createHashMap]) set [_playerID, _userInfo];
|
||||
|
||||
if (_isHC) exitWith {};
|
||||
|
||||
[
|
||||
"ConnectedMission",
|
||||
_playerID,
|
||||
_playerUID,
|
||||
_profileName,
|
||||
_steamName,
|
||||
_jip,
|
||||
roleDescription _unit
|
||||
] call attendanceTracker_fnc_logMissionEvent;
|
||||
}],
|
||||
["PlayerDisconnected", {
|
||||
// NOTE: HandleDisconnect returns a DIFFERENT _id than PlayerDisconnected and above handlers, so we can't use it here
|
||||
params ["_id", "_uid", "_name", "_jip", "_owner", "_idstr"];
|
||||
|
||||
[format ["(EventHandler) HandleDisconnect fired: %1", _this], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
|
||||
private _userInfo = (AttendanceTracker getVariable ["allUsers", createHashMap]) get _idstr;
|
||||
if (isNil "_userInfo") exitWith {
|
||||
[format ["(EventHandler) HandleDisconnect: No user info found for %1", _idstr], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
_userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"];
|
||||
|
||||
if (_isHC) exitWith {};
|
||||
|
||||
[
|
||||
"DisconnectedMission",
|
||||
_playerID,
|
||||
_playerUID,
|
||||
_profileName,
|
||||
_steamName,
|
||||
_jip
|
||||
] call attendanceTracker_fnc_logMissionEvent;
|
||||
|
||||
|
||||
false;
|
||||
}]
|
||||
];
|
||||
@@ -1 +0,0 @@
|
||||
(parseSimpleArray ("AttendanceTracker" callExtension "getTimestamp")) select 0;
|
||||
BIN
@AttendanceTracker/addons/AttendanceTracker.pbo
Normal file
BIN
@AttendanceTracker/addons/AttendanceTracker.pbo
Normal file
Binary file not shown.
@@ -18,9 +18,12 @@ class CfgFunctions {
|
||||
class eventHandlers {};
|
||||
class callbackHandler {postInit = 1;};
|
||||
class log {};
|
||||
class logMissionEvent {};
|
||||
class logServerEvent {};
|
||||
class writeConnect {};
|
||||
class writeDisconnect {};
|
||||
class timestamp {};
|
||||
class getMissionHash {};
|
||||
class getWorldInfo {};
|
||||
class missionLoaded {};
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -21,11 +21,16 @@ addMissionEventHandler ["ExtensionCallback", {
|
||||
false;
|
||||
};
|
||||
|
||||
diag_log format ["Raw callback: %1: %2", _function, _data];
|
||||
|
||||
// Parse response from string array
|
||||
private "_response";
|
||||
try {
|
||||
// diag_log format ["Raw callback: %1: %2", _function, _data];
|
||||
_response = parseSimpleArray _data;
|
||||
if (_response isEqualTo []) then {
|
||||
throw "Failed to parse response as array";
|
||||
};
|
||||
} catch {
|
||||
[
|
||||
format ["Callback invalid data: %1: %2: %3", _function, _data, _exception],
|
||||
@@ -41,18 +46,40 @@ addMissionEventHandler ["ExtensionCallback", {
|
||||
[_response#0, _response#1, _function] call attendanceTracker_fnc_log;
|
||||
if (_response#0 == "SUCCESS") then {
|
||||
missionNamespace setVariable ["AttendanceTracker_DBConnected", true];
|
||||
|
||||
// close any null disconnect values from previous mission
|
||||
"AttendanceTracker" callExtension ["fillLastMissionNull", []];
|
||||
|
||||
// log world info
|
||||
private _response = "AttendanceTracker" callExtension [
|
||||
"logWorld",
|
||||
[
|
||||
[(call attendanceTracker_fnc_getWorldInfo)] call CBA_fnc_encodeJSON
|
||||
]
|
||||
];
|
||||
|
||||
// log mission info and get back the row Id to send with future messages
|
||||
private _response = "AttendanceTracker" callExtension ["logMission", [
|
||||
[AttendanceTracker getVariable ["missionContext", createHashMap]] call CBA_fnc_encodeJSON
|
||||
]];
|
||||
AttendanceTracker_missionId = parseNumber _response;
|
||||
|
||||
|
||||
// log world info
|
||||
private _response = "AttendanceTracker" callExtension ["logWorld", [
|
||||
[call attendanceTracker_fnc_getWorldInfo] call CBA_fnc_encodeJSON
|
||||
]];
|
||||
private _response = "AttendanceTracker" callExtension [
|
||||
"logMission",
|
||||
[
|
||||
[AttendanceTracker getVariable ["missionContext", createHashMap]] call CBA_fnc_encodeJSON
|
||||
]
|
||||
];
|
||||
};
|
||||
};
|
||||
case "writeMissionInfo": {
|
||||
if (_response#0 == "MISSION_ID") then {
|
||||
AttendanceTracker_missionId = parseNumber (_response#1);
|
||||
};
|
||||
};
|
||||
case "writeAttendance": {
|
||||
if (_response#0 == "ATT_LOG") then {
|
||||
(_response#1) params ["_eventType", "_netId", "_rowId"];
|
||||
private _storeIndex = ["SERVER", "MISSION"] find _eventType;
|
||||
((AttendanceTracker getVariable ["rowIds", createHashMap]) getOrDefault [
|
||||
_netId,
|
||||
[nil, nil]
|
||||
]) set [_storeIndex, _rowId];
|
||||
};
|
||||
};
|
||||
default {
|
||||
@@ -0,0 +1,160 @@
|
||||
[
|
||||
["OnUserConnected", {
|
||||
params ["_networkId", "_clientStateNumber", "_clientState"];
|
||||
|
||||
[format ["(EventHandler) OnUserConnected fired: %1", _this], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
|
||||
private _userInfo = (getUserInfo _networkId);
|
||||
if (isNil "_userInfo") exitWith {
|
||||
[format ["(EventHandler) OnUserConnected: No user info found for %1", _networkId], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
_userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"];
|
||||
if (_isHC) exitWith {
|
||||
[format ["(EventHandler) OnUserConnected: %1 is HC, skipping", _playerID], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
(AttendanceTracker getVariable ["allUsers", createHashMap]) set [_networkId, _userInfo];
|
||||
|
||||
[
|
||||
"Server",
|
||||
_playerID,
|
||||
_playerUID,
|
||||
_profileName,
|
||||
_steamName,
|
||||
nil,
|
||||
nil
|
||||
] call attendanceTracker_fnc_writeConnect;
|
||||
|
||||
}],
|
||||
["OnUserDisconnected", {
|
||||
params ["_networkId", "_clientStateNumber", "_clientState"];
|
||||
|
||||
[format ["(EventHandler) OnUserDisconnected fired: %1", _this], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
|
||||
if !(call attendanceTracker_fnc_missionLoaded) exitWith {
|
||||
[format ["(EventHandler) OnUserDisconnected: Server is in Mission Asked, likely mission selection state. Skipping.."], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
private _userInfo = (AttendanceTracker getVariable ["allUsers", createHashMap]) get _networkId;
|
||||
if (isNil "_userInfo") exitWith {
|
||||
[format ["(EventHandler) OnUserDisconnected: No user info found for %1", _networkId], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
_userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"];
|
||||
if (_isHC) exitWith {
|
||||
[format ["(EventHandler) OnUserDisconnected: %1 is HC, skipping", _playerID], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
[
|
||||
"Server",
|
||||
_playerID,
|
||||
_playerUID,
|
||||
_profileName,
|
||||
_steamName
|
||||
] call attendanceTracker_fnc_writeDisconnect;
|
||||
}],
|
||||
["PlayerConnected", {
|
||||
params ["_id", "_uid", "_name", "_jip", "_owner", "_idstr"];
|
||||
|
||||
[format ["(EventHandler) PlayerConnected fired: %1", _this], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
|
||||
if !(call attendanceTracker_fnc_missionLoaded) exitWith {
|
||||
[format ["(EventHandler) PlayerConnected: Server is in Mission Asked, likely mission selection state. Skipping.."], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
private _userInfo = (getUserInfo _idstr);
|
||||
if (isNil "_userInfo") exitWith {
|
||||
[format ["(EventHandler) PlayerConnected: No user info found for %1", _idstr], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
_userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"];
|
||||
if (_isHC) exitWith {
|
||||
[format ["(EventHandler) PlayerConnected: %1 is HC, skipping", _playerID], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
(AttendanceTracker getVariable ["allUsers", createHashMap]) set [_playerID, _userInfo];
|
||||
|
||||
[
|
||||
"Mission",
|
||||
_playerID,
|
||||
_playerUID,
|
||||
_profileName,
|
||||
_steamName,
|
||||
_jip,
|
||||
roleDescription _unit
|
||||
] call attendanceTracker_fnc_writeConnect;
|
||||
}],
|
||||
["PlayerDisconnected", {
|
||||
// NOTE: HandleDisconnect returns a DIFFERENT _id than PlayerDisconnected and above handlers, so we can't use it here
|
||||
params ["_id", "_uid", "_name", "_jip", "_owner", "_idstr"];
|
||||
|
||||
[format ["(EventHandler) HandleDisconnect fired: %1", _this], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
|
||||
if !(call attendanceTracker_fnc_missionLoaded) exitWith {
|
||||
[format ["(EventHandler) HandleDisconnect: Server is in Mission Asked, likely mission selection state. Skipping.."], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
private _userInfo = (AttendanceTracker getVariable ["allUsers", createHashMap]) get _idstr;
|
||||
if (isNil "_userInfo") exitWith {
|
||||
[format ["(EventHandler) HandleDisconnect: No user info found for %1", _idstr], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
_userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit", "_rowId"];
|
||||
if (_isHC) exitWith {
|
||||
[format ["(EventHandler) HandleDisconnect: %1 is HC, skipping", _playerID], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
[
|
||||
"Mission",
|
||||
_playerID,
|
||||
_playerUID,
|
||||
_profileName,
|
||||
_steamName,
|
||||
_jip,
|
||||
nil
|
||||
] call attendanceTracker_fnc_writeDisconnect;
|
||||
|
||||
false;
|
||||
}],
|
||||
["OnUserKicked", {
|
||||
params ["_networkId", "_kickTypeNumber", "_kickType", "_kickReason", "_kickMessageIncReason"];
|
||||
|
||||
[format ["(EventHandler) OnUserKicked fired: %1", _this], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
|
||||
if !(call attendanceTracker_fnc_missionLoaded) exitWith {
|
||||
[format ["(EventHandler) OnUserKicked: Server is in Mission Asked, likely mission selection state. Skipping.."], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
private _userInfo = (AttendanceTracker getVariable ["allUsers", createHashMap]) get _networkId;
|
||||
if (isNil "_userInfo") exitWith {
|
||||
[format ["(EventHandler) OnUserKicked: No user info found for %1", _networkId], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
_userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"];
|
||||
|
||||
if (_isHC) exitWith {
|
||||
[format ["(EventHandler) OnUserKicked: %1 is HC, skipping", _playerID], "DEBUG"] call attendanceTracker_fnc_log;
|
||||
};
|
||||
|
||||
[
|
||||
"Server",
|
||||
_playerID,
|
||||
_playerUID,
|
||||
_profileName,
|
||||
_steamName,
|
||||
nil,
|
||||
nil
|
||||
] call attendanceTracker_fnc_writeDisconnect;
|
||||
|
||||
[
|
||||
"Mission",
|
||||
_playerID,
|
||||
_playerUID,
|
||||
_profileName,
|
||||
_steamName,
|
||||
nil,
|
||||
nil
|
||||
] call attendanceTracker_fnc_writeDisconnect;
|
||||
}]
|
||||
];
|
||||
@@ -0,0 +1 @@
|
||||
(parseSimpleArray ("AttendanceTracker" callExtension "getMissionHash")) select 0;
|
||||
@@ -14,10 +14,15 @@ _workshopID = '';
|
||||
} foreach getLoadedModsInfo;
|
||||
|
||||
// [_name, _author, _workshopID];
|
||||
|
||||
[
|
||||
["worldName", _name],
|
||||
_return = createHashMapFromArray [
|
||||
["author", _author],
|
||||
["workshopID", _workshopID],
|
||||
["displayName", _name],
|
||||
["worldName", toLower worldName],
|
||||
["worldNameOriginal", _name],
|
||||
["worldSize", worldSize],
|
||||
["workshopID", _workshopID]
|
||||
];
|
||||
["latitude", getNumber( _world >> "latitude" )],
|
||||
["longitude", getNumber( _world >> "longitude" )]
|
||||
];
|
||||
diag_log format ["Attendance Tracker: WorldInfo is: %1", _return];
|
||||
_return
|
||||
@@ -0,0 +1 @@
|
||||
!(getClientStateNumber <= 5 || getClientStateNumber isEqualTo 11);
|
||||
@@ -2,7 +2,9 @@
|
||||
AttendanceTracker = false call CBA_fnc_createNamespace;
|
||||
|
||||
AttendanceTracker_missionStartTimestamp = call attendanceTracker_fnc_timestamp;
|
||||
AttendanceTracker_missionHash = "AttendanceTracker" callExtension ["getMissionHash", AttendanceTracker_missionStartTimestamp];
|
||||
diag_log format ["AttendanceTracker: Mission started at %1", AttendanceTracker_missionStartTimestamp];
|
||||
AttendanceTracker_missionHash = call attendanceTracker_fnc_getMissionHash;
|
||||
diag_log format ["AttendanceTracker: Mission hash is %1", AttendanceTracker_missionHash];
|
||||
|
||||
AttendanceTracker setVariable ["missionContext", createHashMapFromArray [
|
||||
["missionName", missionName],
|
||||
@@ -13,13 +15,15 @@ AttendanceTracker setVariable ["missionContext", createHashMapFromArray [
|
||||
["serverName", serverName],
|
||||
["serverProfile", profileName],
|
||||
["missionStart", AttendanceTracker_missionStartTimestamp],
|
||||
["missionHash", AttendanceTracker_missionHash]
|
||||
["missionHash", AttendanceTracker_missionHash],
|
||||
["worldName", toLower worldName]
|
||||
]];
|
||||
|
||||
|
||||
|
||||
// store all user details in a hash when they connect so we can reference it in disconnect events
|
||||
AttendanceTracker setVariable ["allUsers", createHashMap];
|
||||
AttendanceTracker setVariable ["rowIds", createHashMap];
|
||||
missionNamespace setVariable ["AttendanceTracker_debug", false];
|
||||
|
||||
call attendanceTracker_fnc_connectDB;
|
||||
@@ -0,0 +1,24 @@
|
||||
// (parseSimpleArray ("AttendanceTracker" callExtension "getTimestamp")) select 0;
|
||||
|
||||
// need date for MySQL in format 2006-01-02 15:04:05
|
||||
|
||||
systemTimeUTC params [
|
||||
"_year",
|
||||
"_month",
|
||||
"_day",
|
||||
"_hour",
|
||||
"_minute",
|
||||
"_second",
|
||||
"_millisecond"
|
||||
];
|
||||
|
||||
format[
|
||||
"%1-%2-%3 %4:%5:%6",
|
||||
_year,
|
||||
_month,
|
||||
_day,
|
||||
_hour,
|
||||
_minute,
|
||||
_second
|
||||
];
|
||||
|
||||
@@ -9,6 +9,7 @@ params [
|
||||
];
|
||||
|
||||
private _hash = + (AttendanceTracker getVariable ["missionContext", createHashMap]);
|
||||
|
||||
_hash set ["eventType", _eventType];
|
||||
_hash set ["playerId", _playerId];
|
||||
_hash set ["playerUID", _playerUID];
|
||||
@@ -18,6 +19,6 @@ _hash set ["isJIP", _isJIP];
|
||||
_hash set ["roleDescription", _roleDescription];
|
||||
_hash set ["missionHash", missionNamespace getVariable ["AttendanceTracker_missionHash", ""]];
|
||||
|
||||
"AttendanceTracker" callExtension ["logAttendance", [[_hash] call CBA_fnc_encodeJSON]];
|
||||
"AttendanceTracker" callExtension ["writeAttendance", [[_hash] call CBA_fnc_encodeJSON]];
|
||||
|
||||
true;
|
||||
@@ -3,20 +3,22 @@ params [
|
||||
["_playerId", ""],
|
||||
["_playerUID", ""],
|
||||
["_profileName", ""],
|
||||
["_steamName", ""]
|
||||
["_steamName", ""],
|
||||
["_isJIP", false, [true, false]],
|
||||
["_roleDescription", ""]
|
||||
];
|
||||
|
||||
|
||||
private _hash = + (AttendanceTracker getVariable ["missionContext", createHashMap]);
|
||||
|
||||
_hash set ["eventType", _eventType];
|
||||
_hash set ["playerId", _playerId];
|
||||
_hash set ["playerUID", _playerUID];
|
||||
_hash set ["profileName", _profileName];
|
||||
_hash set ["steamName", _steamName];
|
||||
_hash set ["isJIP", false];
|
||||
_hash set ["roleDescription", ""];
|
||||
_hash set ["isJIP", _isJIP];
|
||||
_hash set ["roleDescription", _roleDescription];
|
||||
_hash set ["missionHash", missionNamespace getVariable ["AttendanceTracker_missionHash", ""]];
|
||||
|
||||
"AttendanceTracker" callExtension ["logAttendance", [[_hash] call CBA_fnc_encodeJSON]];
|
||||
"AttendanceTracker" callExtension ["writeDisconnectEvent", [[_hash] call CBA_fnc_encodeJSON]];
|
||||
|
||||
true;
|
||||
7
@AttendanceTracker/config.json
Normal file
7
@AttendanceTracker/config.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"mysqlHost": "127.0.0.1",
|
||||
"mysqlPort": 12730,
|
||||
"mysqlUser": "root",
|
||||
"mysqlPassword": "i&Lz8A3RuPcY5b326ALXgjl",
|
||||
"mysqlDatabase": "testdb"
|
||||
}
|
||||
81
README.md
81
README.md
@@ -7,31 +7,68 @@
|
||||
Create a database with a name of your choosing. Then, run the following SQL command against it to create a table.
|
||||
|
||||
```sql
|
||||
CREATE TABLE `attendancelog` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`timestamp` DATETIME NOT NULL,
|
||||
`event_hash` VARCHAR(100) NOT NULL DEFAULT md5(concat(`server_name`,`mission_name`,`author`,`mission_start`)) COLLATE 'utf8mb3_general_ci',
|
||||
`event_type` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`player_id` VARCHAR(30) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`player_uid` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`profile_name` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`steam_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`is_jip` TINYINT(4) NULL DEFAULT NULL,
|
||||
`role_description` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`mission_start` DATETIME NOT NULL,
|
||||
`mission_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`briefing_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`mission_name_source` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`on_load_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`author` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`server_name` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`server_profile` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
-- a3server.attendancelog definition
|
||||
|
||||
CREATE TABLE `attendance` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`join_time` DATETIME NULL DEFAULT NULL,
|
||||
`disconnect_time` DATETIME NULL DEFAULT NULL,
|
||||
`mission_hash` VARCHAR(100) NULL DEFAULT '' COLLATE 'utf8mb3_general_ci',
|
||||
`event_type` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`player_id` VARCHAR(30) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`player_uid` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`profile_name` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`steam_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`is_jip` TINYINT(4) NULL DEFAULT NULL,
|
||||
`role_description` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
)
|
||||
COLLATE='utf8mb3_general_ci'
|
||||
ENGINE=InnoDB
|
||||
AUTO_INCREMENT=383
|
||||
AUTO_INCREMENT=5868
|
||||
;
|
||||
|
||||
|
||||
-- a3server.`missions` definition
|
||||
|
||||
CREATE TABLE `missions` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`mission_name` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`mission_name_source` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`briefing_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`on_load_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`author` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`server_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`server_profile` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`mission_start` DATETIME NULL DEFAULT NULL,
|
||||
`mission_hash` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
)
|
||||
COLLATE='utf8mb3_general_ci'
|
||||
ENGINE=InnoDB
|
||||
;
|
||||
|
||||
|
||||
|
||||
-- a3server.`worlds` definition
|
||||
|
||||
CREATE TABLE `worlds` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`author` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`display_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`world_name` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`world_name_original` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
`world_size` INT(11) NULL DEFAULT NULL,
|
||||
`latitude` FLOAT NULL DEFAULT NULL,
|
||||
`longitude` FLOAT NULL DEFAULT NULL,
|
||||
`workshop_id` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
)
|
||||
COLLATE='utf8mb3_general_ci'
|
||||
ENGINE=InnoDB
|
||||
AUTO_INCREMENT=2
|
||||
;
|
||||
|
||||
```
|
||||
|
||||
Finally, copy `config.example.json` to `config.json` and update it with your database credentials.
|
||||
Finally, copy `config.example.json` to `config.json` and update it with your database credentials.
|
||||
|
||||
Binary file not shown.
@@ -16,7 +16,8 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
@@ -32,6 +33,12 @@ var ADDON_FOLDER string = getDir() + "\\@AttendanceTracker"
|
||||
var LOG_FILE string = ADDON_FOLDER + "\\attendanceTracker.log"
|
||||
var CONFIG_FILE string = ADDON_FOLDER + "\\config.json"
|
||||
|
||||
var ATTENDANCE_TABLE string = "attendance"
|
||||
var MISSIONS_TABLE string = "missions"
|
||||
var WORLDS_TABLE string = "worlds"
|
||||
|
||||
// ! TODO make a hash to save key:netId from A3 value:rowId from join event
|
||||
|
||||
var ATConfig AttendanceTrackerConfig
|
||||
|
||||
type AttendanceTrackerConfig struct {
|
||||
@@ -104,14 +111,14 @@ func loadConfig() {
|
||||
writeLog(functionName, `["Config loaded", "INFO"]`)
|
||||
}
|
||||
|
||||
func getMissionHash(time string) string {
|
||||
func getMissionHash() string {
|
||||
functionName := "getMissionHash"
|
||||
// get md5 hash of string
|
||||
// https://stackoverflow.com/questions/2377881/how-to-get-a-md5-hash-from-a-string-in-golang
|
||||
hash := md5.Sum([]byte(time))
|
||||
hash := md5.Sum([]byte(time.Now().UTC().Format("2006-01-02 15:04:05")))
|
||||
|
||||
// convert to string
|
||||
hashString := fmt.Sprintf("%x", hash)
|
||||
hashString := fmt.Sprintf(`%x`, hash)
|
||||
writeLog(functionName, fmt.Sprintf(`["Mission hash: %s", "INFO"]`, hashString))
|
||||
return hashString
|
||||
}
|
||||
@@ -147,7 +154,7 @@ func connectDB() string {
|
||||
return "ERROR"
|
||||
}
|
||||
|
||||
// Connect and check the server version
|
||||
// Check the server version
|
||||
var version string
|
||||
err = db.QueryRow("SELECT VERSION()").Scan(&version)
|
||||
if err != nil {
|
||||
@@ -160,38 +167,47 @@ func connectDB() string {
|
||||
}
|
||||
|
||||
type WorldInfo struct {
|
||||
WorldName string `json:"worldName"`
|
||||
Author string `json:"author"`
|
||||
WorldSize int `json:"worldSize"`
|
||||
WorkshopID string `json:"workshopID"`
|
||||
Author string `json:"author"`
|
||||
WorkshopID string `json:"workshopID"`
|
||||
DisplayName string `json:"displayName"`
|
||||
WorldName string `json:"worldName"`
|
||||
WorldNameOriginal string `json:"worldNameOriginal"`
|
||||
WorldSize int `json:"worldSize"`
|
||||
Latitude float32 `json:"latitude"`
|
||||
Longitude float32 `json:"longitude"`
|
||||
}
|
||||
|
||||
func writeWorldInfo(worldInfo string) {
|
||||
functionName := "writeWorldInfo"
|
||||
// writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, worldInfo))
|
||||
// worldInfo is json, parse it
|
||||
var wi WorldInfo
|
||||
err := json.Unmarshal([]byte(worldInfo), &wi)
|
||||
fixedString := fixEscapeQuotes(trimQuotes(worldInfo))
|
||||
err := json.Unmarshal([]byte(fixedString), &wi)
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
// write to log
|
||||
writeLog(functionName, fmt.Sprintf(`["WorldName:%s Author:%s WorldSize:%d WorkshopID:%s", "INFO"]`, wi.WorldName, wi.Author, wi.WorldSize, wi.WorkshopID))
|
||||
// write to log as json
|
||||
// writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, json.Marshal(wi)))
|
||||
|
||||
// write to database
|
||||
// check if world exists
|
||||
var worldID int
|
||||
err = db.QueryRow("SELECT id FROM worlds WHERE workshop_id = ?", wi.WorkshopID).Scan(&worldID)
|
||||
err = db.QueryRow("SELECT id FROM worlds WHERE world_name = ?", wi.WorldName).Scan(&worldID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
// world does not exist, insert it
|
||||
stmt, err := db.Prepare("INSERT INTO worlds (world_name, author, world_size, workshop_id) VALUES (?, ?, ?, ?)")
|
||||
stmt, err := db.Prepare(fmt.Sprintf(
|
||||
"INSERT INTO %s (author, workshop_id, display_name, world_name, world_name_original, world_size, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
WORLDS_TABLE,
|
||||
))
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
defer stmt.Close()
|
||||
res, err := stmt.Exec(wi.WorldName, wi.Author, wi.WorldSize, wi.WorkshopID)
|
||||
res, err := stmt.Exec(wi.Author, wi.WorkshopID, wi.DisplayName, wi.WorldName, wi.WorldNameOriginal, wi.WorldSize, wi.Latitude, wi.Longitude)
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
@@ -208,13 +224,16 @@ func writeWorldInfo(worldInfo string) {
|
||||
}
|
||||
} else {
|
||||
// world exists, update it
|
||||
stmt, err := db.Prepare("UPDATE worlds SET world_name = ?, author = ?, world_size = ? WHERE id = ?")
|
||||
stmt, err := db.Prepare(fmt.Sprintf(
|
||||
"UPDATE %s SET author = ?, workshop_id = ?, display_name = ?, world_name = ?, world_name_original = ?, world_size = ?, latitude = ?, longitude = ? WHERE id = ?",
|
||||
WORLDS_TABLE,
|
||||
))
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
defer stmt.Close()
|
||||
res, err := stmt.Exec(wi.WorldName, wi.Author, wi.WorldSize, worldID)
|
||||
res, err := stmt.Exec(wi.Author, wi.WorkshopID, wi.DisplayName, wi.WorldName, wi.WorldNameOriginal, wi.WorldSize, wi.Latitude, wi.Longitude, worldID)
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
@@ -238,48 +257,74 @@ type MissionInfo struct {
|
||||
ServerProfile string `json:"serverProfile"`
|
||||
MissionStart string `json:"missionStart"`
|
||||
MissionHash string `json:"missionHash"`
|
||||
WorldName string `json:"worldName"`
|
||||
}
|
||||
|
||||
func writeMissionInfo(missionInfo string) {
|
||||
functionName := "writeMissionInfo"
|
||||
var err error
|
||||
// writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, missionInfo))
|
||||
// missionInfo is json, parse it
|
||||
var mi MissionInfo
|
||||
err := json.Unmarshal([]byte(missionInfo), &mi)
|
||||
fixedString := fixEscapeQuotes(trimQuotes(missionInfo))
|
||||
err = json.Unmarshal([]byte(fixedString), &mi)
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
|
||||
// get MySQL friendly datetime
|
||||
// first, convert string to int
|
||||
missionStartTime, err := strconv.ParseInt(mi.MissionStart, 10, 64)
|
||||
// check if mission exists based on hash
|
||||
var worldID int
|
||||
err = db.QueryRow("SELECT id FROM worlds WHERE world_name = ?", mi.WorldName).Scan(&worldID)
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
t := time.Unix(0, missionStartTime).Format("2006-01-02 15:04:05")
|
||||
// write to log
|
||||
writeLog(functionName, fmt.Sprintf(`["MissionName:%s BriefingName:%s MissionNameSource:%s OnLoadName:%s Author:%s ServerName:%s ServerProfile:%s MissionStart:%s MissionHash:%s", "INFO"]`, mi.MissionName, mi.BriefingName, mi.MissionNameSource, mi.OnLoadName, mi.Author, mi.ServerName, mi.ServerProfile, t, mi.MissionHash))
|
||||
|
||||
// write to database
|
||||
// every mission is unique, so insert it
|
||||
stmt, err := db.Prepare("INSERT INTO missions (mission_name, briefing_name, mission_name_source, on_load_name, author, server_name, server_profile, mission_start, mission_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
defer stmt.Close()
|
||||
res, err := stmt.Exec(mi.MissionName, mi.BriefingName, mi.MissionNameSource, mi.OnLoadName, mi.Author, mi.ServerName, mi.ServerProfile, t, mi.MissionHash)
|
||||
var stmt *sql.Stmt
|
||||
var res sql.Result
|
||||
|
||||
if worldID != 0 {
|
||||
sqlWorld := fmt.Sprintf(
|
||||
"INSERT INTO %s (mission_name, briefing_name, mission_name_source, on_load_name, author, server_name, server_profile, mission_start, mission_hash, world_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
MISSIONS_TABLE,
|
||||
)
|
||||
stmt, err = db.Prepare(sqlWorld)
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
res, err = stmt.Exec(mi.MissionName, mi.BriefingName, mi.MissionNameSource, mi.OnLoadName, mi.Author, mi.ServerName, mi.ServerProfile, mi.MissionStart, mi.MissionHash, worldID)
|
||||
|
||||
} else {
|
||||
// if no world was found, write without it
|
||||
sqlNoWorld := fmt.Sprintf(
|
||||
"INSERT INTO %s (mission_name, briefing_name, mission_name_source, on_load_name, author, server_name, server_profile, mission_start, mission_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
MISSIONS_TABLE,
|
||||
)
|
||||
stmt, err = db.Prepare(sqlNoWorld)
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
defer stmt.Close()
|
||||
res, err = stmt.Exec(mi.MissionName, mi.BriefingName, mi.MissionNameSource, mi.OnLoadName, mi.Author, mi.ServerName, mi.ServerProfile, mi.MissionStart, mi.MissionHash)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
|
||||
lastID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
writeLog(functionName, fmt.Sprintf(`["Mission inserted with ID %d", "INFO"]`, lastID))
|
||||
writeLog(functionName, fmt.Sprintf(`["MISSION_ID", "%d"]`, lastID))
|
||||
}
|
||||
|
||||
type AttendanceLogItem struct {
|
||||
@@ -295,17 +340,18 @@ type AttendanceLogItem struct {
|
||||
|
||||
func writeAttendance(data string) {
|
||||
functionName := "writeAttendance"
|
||||
var err error
|
||||
// data is json, parse it
|
||||
stringjson := fixEscapeQuotes(trimQuotes(data))
|
||||
var event AttendanceLogItem
|
||||
err := json.Unmarshal([]byte(stringjson), &event)
|
||||
err = json.Unmarshal([]byte(stringjson), &event)
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
|
||||
// get MySQL friendly NOW
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
now := time.Now().UTC().Format("2006-01-02 15:04:05")
|
||||
|
||||
// prevent crash
|
||||
if db == nil {
|
||||
@@ -314,18 +360,44 @@ func writeAttendance(data string) {
|
||||
}
|
||||
|
||||
// send to DB
|
||||
var result sql.Result
|
||||
|
||||
result, err := db.ExecContext(context.Background(), `INSERT INTO AttendanceLog (timestamp, event_type, player_id, player_uid, profile_name, steam_name, is_jip, role_description, mission_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
now,
|
||||
event.EventType,
|
||||
event.PlayerId,
|
||||
event.PlayerUID,
|
||||
event.ProfileName,
|
||||
event.SteamName,
|
||||
event.IsJIP,
|
||||
event.RoleDescription,
|
||||
event.MissionHash,
|
||||
)
|
||||
if event.EventType == "Server" {
|
||||
sql := fmt.Sprintf(
|
||||
`INSERT INTO %s (join_time, event_type, player_id, player_uid, profile_name, steam_name, is_jip, role_description) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
ATTENDANCE_TABLE,
|
||||
)
|
||||
result, err = db.ExecContext(
|
||||
context.Background(),
|
||||
sql,
|
||||
now,
|
||||
event.EventType,
|
||||
event.PlayerId,
|
||||
event.PlayerUID,
|
||||
event.ProfileName,
|
||||
event.SteamName,
|
||||
event.IsJIP,
|
||||
event.RoleDescription,
|
||||
)
|
||||
} else if event.EventType == "Mission" {
|
||||
sql := fmt.Sprintf(
|
||||
`INSERT INTO %s (join_time, event_type, player_id, player_uid, profile_name, steam_name, is_jip, role_description, mission_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
ATTENDANCE_TABLE,
|
||||
)
|
||||
result, err = db.ExecContext(
|
||||
context.Background(),
|
||||
sql,
|
||||
now,
|
||||
event.EventType,
|
||||
event.PlayerId,
|
||||
event.PlayerUID,
|
||||
event.ProfileName,
|
||||
event.SteamName,
|
||||
event.IsJIP,
|
||||
event.RoleDescription,
|
||||
event.MissionHash,
|
||||
)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
@@ -339,9 +411,124 @@ func writeAttendance(data string) {
|
||||
}
|
||||
|
||||
writeLog(functionName, fmt.Sprintf(`["Saved attendance for %s to row id %d", "INFO"]`, event.ProfileName, id))
|
||||
if event.EventType == "Server" {
|
||||
writeLog(functionName, fmt.Sprintf(`["ATT_LOG", ["SERVER", "%s", "%d"]]`, event.PlayerId, id))
|
||||
} else if event.EventType == "Mission" {
|
||||
writeLog(functionName, fmt.Sprintf(`["ATT_LOG", ["MISSION", "%s", "%d"]]`, event.PlayerId, id))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type DisconnectItem struct {
|
||||
EventType string `json:"eventType"`
|
||||
PlayerId string `json:"playerId"`
|
||||
MissionHash string `json:"missionHash"`
|
||||
}
|
||||
|
||||
func writeDisconnectEvent(data string) {
|
||||
functionName := "writeDisconnectEvent"
|
||||
// data is json, parse it
|
||||
stringjson := fixEscapeQuotes(trimQuotes(data))
|
||||
var event DisconnectItem
|
||||
err := json.Unmarshal([]byte(stringjson), &event)
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
|
||||
// get MySQL friendly NOW
|
||||
now := time.Now().UTC().Format("2006-01-02 15:04:05")
|
||||
|
||||
// prevent crash
|
||||
if db == nil {
|
||||
writeLog(functionName, `["db is nil", "ERROR"]`)
|
||||
return
|
||||
}
|
||||
|
||||
// first, check if a row exists for this player
|
||||
var sql string
|
||||
if event.EventType == "Mission" {
|
||||
sql = fmt.Sprintf(
|
||||
`
|
||||
SELECT id FROM attendance
|
||||
WHERE player_id = '%s' and event_type = '%s' and mission_hash = '%s' and disconnect_time IS NULL and join_time >= (NOW() - INTERVAL 24 hour)
|
||||
ORDER BY join_time DESC
|
||||
`,
|
||||
event.PlayerId,
|
||||
event.EventType,
|
||||
event.MissionHash,
|
||||
)
|
||||
} else if event.EventType == "Server" {
|
||||
sql = fmt.Sprintf(
|
||||
`
|
||||
SELECT id FROM attendance
|
||||
WHERE player_id = '%s' and event_type = '%s' and disconnect_time IS NULL and join_time >= (NOW() - INTERVAL 24 hour)
|
||||
ORDER BY join_time DESC
|
||||
`,
|
||||
event.PlayerId,
|
||||
event.EventType,
|
||||
)
|
||||
} else {
|
||||
writeLog(functionName, fmt.Sprintf(`["Unknown event type %s", "ERROR"]`, event.EventType))
|
||||
return
|
||||
}
|
||||
|
||||
rows, err := db.QueryContext(context.Background(), sql)
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// if there is a row, update it
|
||||
if rows.Next() {
|
||||
// create interface to hold values
|
||||
var rowId int64
|
||||
|
||||
err = rows.Scan(&rowId)
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
|
||||
// update the row
|
||||
sql = fmt.Sprintf(
|
||||
`UPDATE attendance SET disconnect_time = '%s' WHERE id = %d`,
|
||||
now,
|
||||
rowId,
|
||||
)
|
||||
|
||||
_, err := db.ExecContext(context.Background(), sql)
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
writeLog(functionName, fmt.Sprintf(`["Saved disconnect event for %s to row id %d", "INFO"]`, event.PlayerId, rowId))
|
||||
|
||||
} else {
|
||||
// otherwise, log an error
|
||||
writeLog(functionName, fmt.Sprintf(`["No row found for %s, %s", "ERROR"]`, event.PlayerId, event.EventType))
|
||||
}
|
||||
}
|
||||
|
||||
func fillLastMissionNull() {
|
||||
functionName := "fillLastMissionNull"
|
||||
// prevent crash
|
||||
if db == nil {
|
||||
writeLog(functionName, `["db is nil", "ERROR"]`)
|
||||
return
|
||||
}
|
||||
|
||||
sql := `call proc_filllastmissionnull`
|
||||
|
||||
_, err := db.ExecContext(context.Background(), sql)
|
||||
if err != nil {
|
||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||
return
|
||||
}
|
||||
writeLog(functionName, `["Filled mission event NULLs", "INFO"]`)
|
||||
}
|
||||
|
||||
func runExtensionCallback(name *C.char, function *C.char, data *C.char) C.int {
|
||||
return C.runExtensionCallback(extensionCallbackFnc, name, function, data)
|
||||
}
|
||||
@@ -370,12 +557,23 @@ func goRVExtensionArgs(output *C.char, outputsize C.size_t, input *C.char, argv
|
||||
temp := fmt.Sprintf("Function: %s nb params: %d", C.GoString(input), argc)
|
||||
|
||||
switch C.GoString(input) {
|
||||
case "logAttendance":
|
||||
{ // callExtension ["serverEvent", [_hash] call CBA_fnc_encodeJSON];
|
||||
case "fillLastMissionNull":
|
||||
{
|
||||
go fillLastMissionNull()
|
||||
}
|
||||
case "writeAttendance":
|
||||
{ // callExtension ["logAttendance", [_hash] call CBA_fnc_encodeJSON]];
|
||||
if argc == 1 {
|
||||
go writeAttendance(out[0])
|
||||
}
|
||||
}
|
||||
case "writeDisconnectEvent":
|
||||
{ // callExtension ["writeDisconnectEvent", [[_hash] call CBA_fnc_encodeJSON]];
|
||||
|
||||
if argc == 1 {
|
||||
go writeDisconnectEvent(out[0])
|
||||
}
|
||||
}
|
||||
case "logMission":
|
||||
if argc == 1 {
|
||||
go writeMissionInfo(out[0])
|
||||
@@ -411,9 +609,10 @@ func callBackExample() {
|
||||
}
|
||||
}
|
||||
|
||||
func getTimestamp() int64 {
|
||||
func getTimestamp() string {
|
||||
// get the current unix timestamp in nanoseconds
|
||||
return time.Now().UnixNano()
|
||||
// return time.Now().Local().Unix()
|
||||
return time.Now().UTC().Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
func trimQuotes(s string) string {
|
||||
@@ -435,6 +634,9 @@ func writeLog(functionName string, data string) {
|
||||
defer C.free(unsafe.Pointer(statusParam))
|
||||
runExtensionCallback(statusName, statusFunction, statusParam)
|
||||
|
||||
// get calling function & line
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
log.Printf(`%s:%d: %s`, path.Base(file), line, data)
|
||||
log.Printf(`%s: %s`, functionName, data)
|
||||
}
|
||||
|
||||
@@ -451,12 +653,12 @@ func goRVExtension(output *C.char, outputsize C.size_t, input *C.char) {
|
||||
case "getDir":
|
||||
temp = getDir()
|
||||
case "getTimestamp":
|
||||
time := getTimestamp()
|
||||
temp = fmt.Sprintf(`["%s"]`, strconv.FormatInt(time, 10))
|
||||
temp = fmt.Sprintf(`["%s"]`, getTimestamp())
|
||||
case "connectDB":
|
||||
go connectDB()
|
||||
temp = fmt.Sprintf(`["%s"]`, "Connecting to DB")
|
||||
|
||||
case "getMissionHash":
|
||||
temp = fmt.Sprintf(`["%s"]`, getMissionHash())
|
||||
default:
|
||||
temp = fmt.Sprintf(`["%s"]`, "Unknown Function")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user