mirror of
https://github.com/indig0fox/Arma3-AttendanceTracker.git/
synced 2025-12-08 09:51:47 -06:00
Compare commits
8 Commits
43aa384949
...
79fb39b154
| Author | SHA1 | Date | |
|---|---|---|---|
|
79fb39b154
|
|||
|
fc53ecb770
|
|||
|
dbd3d68537
|
|||
|
3bb8c358fa
|
|||
|
8f971d9887
|
|||
|
f6ff42e467
|
|||
|
7608df9e53
|
|||
|
2cbfdcd512
|
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 eventHandlers {};
|
||||||
class callbackHandler {postInit = 1;};
|
class callbackHandler {postInit = 1;};
|
||||||
class log {};
|
class log {};
|
||||||
class logMissionEvent {};
|
class writeConnect {};
|
||||||
class logServerEvent {};
|
class writeDisconnect {};
|
||||||
class timestamp {};
|
class timestamp {};
|
||||||
|
class getMissionHash {};
|
||||||
|
class getWorldInfo {};
|
||||||
|
class missionLoaded {};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -21,11 +21,16 @@ addMissionEventHandler ["ExtensionCallback", {
|
|||||||
false;
|
false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
diag_log format ["Raw callback: %1: %2", _function, _data];
|
||||||
|
|
||||||
// Parse response from string array
|
// Parse response from string array
|
||||||
private "_response";
|
private "_response";
|
||||||
try {
|
try {
|
||||||
// diag_log format ["Raw callback: %1: %2", _function, _data];
|
// diag_log format ["Raw callback: %1: %2", _function, _data];
|
||||||
_response = parseSimpleArray _data;
|
_response = parseSimpleArray _data;
|
||||||
|
if (_response isEqualTo []) then {
|
||||||
|
throw "Failed to parse response as array";
|
||||||
|
};
|
||||||
} catch {
|
} catch {
|
||||||
[
|
[
|
||||||
format ["Callback invalid data: %1: %2: %3", _function, _data, _exception],
|
format ["Callback invalid data: %1: %2: %3", _function, _data, _exception],
|
||||||
@@ -42,17 +47,39 @@ addMissionEventHandler ["ExtensionCallback", {
|
|||||||
if (_response#0 == "SUCCESS") then {
|
if (_response#0 == "SUCCESS") then {
|
||||||
missionNamespace setVariable ["AttendanceTracker_DBConnected", true];
|
missionNamespace setVariable ["AttendanceTracker_DBConnected", true];
|
||||||
|
|
||||||
// log mission info and get back the row Id to send with future messages
|
// close any null disconnect values from previous mission
|
||||||
private _response = "AttendanceTracker" callExtension ["logMission", [
|
"AttendanceTracker" callExtension ["fillLastMissionNull", []];
|
||||||
[AttendanceTracker getVariable ["missionContext", createHashMap]] call CBA_fnc_encodeJSON
|
|
||||||
]];
|
|
||||||
AttendanceTracker_missionId = parseNumber _response;
|
|
||||||
|
|
||||||
|
|
||||||
// log world info
|
// log world info
|
||||||
private _response = "AttendanceTracker" callExtension ["logWorld", [
|
private _response = "AttendanceTracker" callExtension [
|
||||||
[call attendanceTracker_fnc_getWorldInfo] call CBA_fnc_encodeJSON
|
"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
|
||||||
|
]
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
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 {
|
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;
|
} foreach getLoadedModsInfo;
|
||||||
|
|
||||||
// [_name, _author, _workshopID];
|
// [_name, _author, _workshopID];
|
||||||
|
_return = createHashMapFromArray [
|
||||||
[
|
|
||||||
["worldName", _name],
|
|
||||||
["author", _author],
|
["author", _author],
|
||||||
|
["workshopID", _workshopID],
|
||||||
|
["displayName", _name],
|
||||||
|
["worldName", toLower worldName],
|
||||||
|
["worldNameOriginal", _name],
|
||||||
["worldSize", worldSize],
|
["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 = false call CBA_fnc_createNamespace;
|
||||||
|
|
||||||
AttendanceTracker_missionStartTimestamp = call attendanceTracker_fnc_timestamp;
|
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 [
|
AttendanceTracker setVariable ["missionContext", createHashMapFromArray [
|
||||||
["missionName", missionName],
|
["missionName", missionName],
|
||||||
@@ -13,13 +15,15 @@ AttendanceTracker setVariable ["missionContext", createHashMapFromArray [
|
|||||||
["serverName", serverName],
|
["serverName", serverName],
|
||||||
["serverProfile", profileName],
|
["serverProfile", profileName],
|
||||||
["missionStart", AttendanceTracker_missionStartTimestamp],
|
["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
|
// store all user details in a hash when they connect so we can reference it in disconnect events
|
||||||
AttendanceTracker setVariable ["allUsers", createHashMap];
|
AttendanceTracker setVariable ["allUsers", createHashMap];
|
||||||
|
AttendanceTracker setVariable ["rowIds", createHashMap];
|
||||||
missionNamespace setVariable ["AttendanceTracker_debug", false];
|
missionNamespace setVariable ["AttendanceTracker_debug", false];
|
||||||
|
|
||||||
call attendanceTracker_fnc_connectDB;
|
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]);
|
private _hash = + (AttendanceTracker getVariable ["missionContext", createHashMap]);
|
||||||
|
|
||||||
_hash set ["eventType", _eventType];
|
_hash set ["eventType", _eventType];
|
||||||
_hash set ["playerId", _playerId];
|
_hash set ["playerId", _playerId];
|
||||||
_hash set ["playerUID", _playerUID];
|
_hash set ["playerUID", _playerUID];
|
||||||
@@ -18,6 +19,6 @@ _hash set ["isJIP", _isJIP];
|
|||||||
_hash set ["roleDescription", _roleDescription];
|
_hash set ["roleDescription", _roleDescription];
|
||||||
_hash set ["missionHash", missionNamespace getVariable ["AttendanceTracker_missionHash", ""]];
|
_hash set ["missionHash", missionNamespace getVariable ["AttendanceTracker_missionHash", ""]];
|
||||||
|
|
||||||
"AttendanceTracker" callExtension ["logAttendance", [[_hash] call CBA_fnc_encodeJSON]];
|
"AttendanceTracker" callExtension ["writeAttendance", [[_hash] call CBA_fnc_encodeJSON]];
|
||||||
|
|
||||||
true;
|
true;
|
||||||
@@ -3,20 +3,22 @@ params [
|
|||||||
["_playerId", ""],
|
["_playerId", ""],
|
||||||
["_playerUID", ""],
|
["_playerUID", ""],
|
||||||
["_profileName", ""],
|
["_profileName", ""],
|
||||||
["_steamName", ""]
|
["_steamName", ""],
|
||||||
|
["_isJIP", false, [true, false]],
|
||||||
|
["_roleDescription", ""]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
private _hash = + (AttendanceTracker getVariable ["missionContext", createHashMap]);
|
private _hash = + (AttendanceTracker getVariable ["missionContext", createHashMap]);
|
||||||
|
|
||||||
_hash set ["eventType", _eventType];
|
_hash set ["eventType", _eventType];
|
||||||
_hash set ["playerId", _playerId];
|
_hash set ["playerId", _playerId];
|
||||||
_hash set ["playerUID", _playerUID];
|
_hash set ["playerUID", _playerUID];
|
||||||
_hash set ["profileName", _profileName];
|
_hash set ["profileName", _profileName];
|
||||||
_hash set ["steamName", _steamName];
|
_hash set ["steamName", _steamName];
|
||||||
_hash set ["isJIP", false];
|
_hash set ["isJIP", _isJIP];
|
||||||
_hash set ["roleDescription", ""];
|
_hash set ["roleDescription", _roleDescription];
|
||||||
_hash set ["missionHash", missionNamespace getVariable ["AttendanceTracker_missionHash", ""]];
|
_hash set ["missionHash", missionNamespace getVariable ["AttendanceTracker_missionHash", ""]];
|
||||||
|
|
||||||
"AttendanceTracker" callExtension ["logAttendance", [[_hash] call CBA_fnc_encodeJSON]];
|
"AttendanceTracker" callExtension ["writeDisconnectEvent", [[_hash] call CBA_fnc_encodeJSON]];
|
||||||
|
|
||||||
true;
|
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"
|
||||||
|
}
|
||||||
79
README.md
79
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.
|
Create a database with a name of your choosing. Then, run the following SQL command against it to create a table.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE `attendancelog` (
|
-- a3server.attendancelog definition
|
||||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`timestamp` DATETIME NOT NULL,
|
CREATE TABLE `attendance` (
|
||||||
`event_hash` VARCHAR(100) NOT NULL DEFAULT md5(concat(`server_name`,`mission_name`,`author`,`mission_start`)) COLLATE 'utf8mb3_general_ci',
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
`event_type` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
`join_time` DATETIME NULL DEFAULT NULL,
|
||||||
`player_id` VARCHAR(30) NOT NULL COLLATE 'utf8mb3_general_ci',
|
`disconnect_time` DATETIME NULL DEFAULT NULL,
|
||||||
`player_uid` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
`mission_hash` VARCHAR(100) NULL DEFAULT '' COLLATE 'utf8mb3_general_ci',
|
||||||
`profile_name` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
`event_type` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`steam_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
`player_id` VARCHAR(30) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`is_jip` TINYINT(4) NULL DEFAULT NULL,
|
`player_uid` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`role_description` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
`profile_name` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`mission_start` DATETIME NOT NULL,
|
`steam_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`mission_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
`is_jip` TINYINT(4) NULL DEFAULT NULL,
|
||||||
`briefing_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
`role_description` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`mission_name_source` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
`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
|
|
||||||
)
|
)
|
||||||
COLLATE='utf8mb3_general_ci'
|
COLLATE='utf8mb3_general_ci'
|
||||||
ENGINE=InnoDB
|
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"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"path"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@@ -32,6 +33,12 @@ var ADDON_FOLDER string = getDir() + "\\@AttendanceTracker"
|
|||||||
var LOG_FILE string = ADDON_FOLDER + "\\attendanceTracker.log"
|
var LOG_FILE string = ADDON_FOLDER + "\\attendanceTracker.log"
|
||||||
var CONFIG_FILE string = ADDON_FOLDER + "\\config.json"
|
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
|
var ATConfig AttendanceTrackerConfig
|
||||||
|
|
||||||
type AttendanceTrackerConfig struct {
|
type AttendanceTrackerConfig struct {
|
||||||
@@ -104,14 +111,14 @@ func loadConfig() {
|
|||||||
writeLog(functionName, `["Config loaded", "INFO"]`)
|
writeLog(functionName, `["Config loaded", "INFO"]`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMissionHash(time string) string {
|
func getMissionHash() string {
|
||||||
functionName := "getMissionHash"
|
functionName := "getMissionHash"
|
||||||
// get md5 hash of string
|
// get md5 hash of string
|
||||||
// https://stackoverflow.com/questions/2377881/how-to-get-a-md5-hash-from-a-string-in-golang
|
// 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
|
// convert to string
|
||||||
hashString := fmt.Sprintf("%x", hash)
|
hashString := fmt.Sprintf(`%x`, hash)
|
||||||
writeLog(functionName, fmt.Sprintf(`["Mission hash: %s", "INFO"]`, hashString))
|
writeLog(functionName, fmt.Sprintf(`["Mission hash: %s", "INFO"]`, hashString))
|
||||||
return hashString
|
return hashString
|
||||||
}
|
}
|
||||||
@@ -147,7 +154,7 @@ func connectDB() string {
|
|||||||
return "ERROR"
|
return "ERROR"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect and check the server version
|
// Check the server version
|
||||||
var version string
|
var version string
|
||||||
err = db.QueryRow("SELECT VERSION()").Scan(&version)
|
err = db.QueryRow("SELECT VERSION()").Scan(&version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -160,38 +167,47 @@ func connectDB() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WorldInfo struct {
|
type WorldInfo struct {
|
||||||
WorldName string `json:"worldName"`
|
Author string `json:"author"`
|
||||||
Author string `json:"author"`
|
WorkshopID string `json:"workshopID"`
|
||||||
WorldSize int `json:"worldSize"`
|
DisplayName string `json:"displayName"`
|
||||||
WorkshopID string `json:"workshopID"`
|
WorldName string `json:"worldName"`
|
||||||
|
WorldNameOriginal string `json:"worldNameOriginal"`
|
||||||
|
WorldSize int `json:"worldSize"`
|
||||||
|
Latitude float32 `json:"latitude"`
|
||||||
|
Longitude float32 `json:"longitude"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeWorldInfo(worldInfo string) {
|
func writeWorldInfo(worldInfo string) {
|
||||||
functionName := "writeWorldInfo"
|
functionName := "writeWorldInfo"
|
||||||
|
// writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, worldInfo))
|
||||||
// worldInfo is json, parse it
|
// worldInfo is json, parse it
|
||||||
var wi WorldInfo
|
var wi WorldInfo
|
||||||
err := json.Unmarshal([]byte(worldInfo), &wi)
|
fixedString := fixEscapeQuotes(trimQuotes(worldInfo))
|
||||||
|
err := json.Unmarshal([]byte(fixedString), &wi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// write to log
|
// write to log as json
|
||||||
writeLog(functionName, fmt.Sprintf(`["WorldName:%s Author:%s WorldSize:%d WorkshopID:%s", "INFO"]`, wi.WorldName, wi.Author, wi.WorldSize, wi.WorkshopID))
|
// writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, json.Marshal(wi)))
|
||||||
|
|
||||||
// write to database
|
// write to database
|
||||||
// check if world exists
|
// check if world exists
|
||||||
var worldID int
|
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 != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
// world does not exist, insert it
|
// 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 {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
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 {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
@@ -208,13 +224,16 @@ func writeWorldInfo(worldInfo string) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// world exists, update it
|
// 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 {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
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 {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
@@ -238,48 +257,74 @@ type MissionInfo struct {
|
|||||||
ServerProfile string `json:"serverProfile"`
|
ServerProfile string `json:"serverProfile"`
|
||||||
MissionStart string `json:"missionStart"`
|
MissionStart string `json:"missionStart"`
|
||||||
MissionHash string `json:"missionHash"`
|
MissionHash string `json:"missionHash"`
|
||||||
|
WorldName string `json:"worldName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeMissionInfo(missionInfo string) {
|
func writeMissionInfo(missionInfo string) {
|
||||||
functionName := "writeMissionInfo"
|
functionName := "writeMissionInfo"
|
||||||
|
var err error
|
||||||
|
// writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, missionInfo))
|
||||||
// missionInfo is json, parse it
|
// missionInfo is json, parse it
|
||||||
var mi MissionInfo
|
var mi MissionInfo
|
||||||
err := json.Unmarshal([]byte(missionInfo), &mi)
|
fixedString := fixEscapeQuotes(trimQuotes(missionInfo))
|
||||||
|
err = json.Unmarshal([]byte(fixedString), &mi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get MySQL friendly datetime
|
// check if mission exists based on hash
|
||||||
// first, convert string to int
|
var worldID int
|
||||||
missionStartTime, err := strconv.ParseInt(mi.MissionStart, 10, 64)
|
err = db.QueryRow("SELECT id FROM worlds WHERE world_name = ?", mi.WorldName).Scan(&worldID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
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
|
var stmt *sql.Stmt
|
||||||
// every mission is unique, so insert it
|
var res sql.Result
|
||||||
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 {
|
if worldID != 0 {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
sqlWorld := fmt.Sprintf(
|
||||||
return
|
"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,
|
||||||
defer stmt.Close()
|
)
|
||||||
res, err := stmt.Exec(mi.MissionName, mi.BriefingName, mi.MissionNameSource, mi.OnLoadName, mi.Author, mi.ServerName, mi.ServerProfile, t, mi.MissionHash)
|
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 {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
lastID, err := res.LastInsertId()
|
lastID, err := res.LastInsertId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writeLog(functionName, fmt.Sprintf(`["Mission inserted with ID %d", "INFO"]`, lastID))
|
writeLog(functionName, fmt.Sprintf(`["Mission inserted with ID %d", "INFO"]`, lastID))
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["MISSION_ID", "%d"]`, lastID))
|
||||||
}
|
}
|
||||||
|
|
||||||
type AttendanceLogItem struct {
|
type AttendanceLogItem struct {
|
||||||
@@ -295,17 +340,18 @@ type AttendanceLogItem struct {
|
|||||||
|
|
||||||
func writeAttendance(data string) {
|
func writeAttendance(data string) {
|
||||||
functionName := "writeAttendance"
|
functionName := "writeAttendance"
|
||||||
|
var err error
|
||||||
// data is json, parse it
|
// data is json, parse it
|
||||||
stringjson := fixEscapeQuotes(trimQuotes(data))
|
stringjson := fixEscapeQuotes(trimQuotes(data))
|
||||||
var event AttendanceLogItem
|
var event AttendanceLogItem
|
||||||
err := json.Unmarshal([]byte(stringjson), &event)
|
err = json.Unmarshal([]byte(stringjson), &event)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get MySQL friendly NOW
|
// 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
|
// prevent crash
|
||||||
if db == nil {
|
if db == nil {
|
||||||
@@ -314,18 +360,44 @@ func writeAttendance(data string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send to DB
|
// 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 (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
if event.EventType == "Server" {
|
||||||
now,
|
sql := fmt.Sprintf(
|
||||||
event.EventType,
|
`INSERT INTO %s (join_time, event_type, player_id, player_uid, profile_name, steam_name, is_jip, role_description) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
event.PlayerId,
|
ATTENDANCE_TABLE,
|
||||||
event.PlayerUID,
|
)
|
||||||
event.ProfileName,
|
result, err = db.ExecContext(
|
||||||
event.SteamName,
|
context.Background(),
|
||||||
event.IsJIP,
|
sql,
|
||||||
event.RoleDescription,
|
now,
|
||||||
event.MissionHash,
|
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 {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
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))
|
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 {
|
func runExtensionCallback(name *C.char, function *C.char, data *C.char) C.int {
|
||||||
return C.runExtensionCallback(extensionCallbackFnc, name, function, data)
|
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)
|
temp := fmt.Sprintf("Function: %s nb params: %d", C.GoString(input), argc)
|
||||||
|
|
||||||
switch C.GoString(input) {
|
switch C.GoString(input) {
|
||||||
case "logAttendance":
|
case "fillLastMissionNull":
|
||||||
{ // callExtension ["serverEvent", [_hash] call CBA_fnc_encodeJSON];
|
{
|
||||||
|
go fillLastMissionNull()
|
||||||
|
}
|
||||||
|
case "writeAttendance":
|
||||||
|
{ // callExtension ["logAttendance", [_hash] call CBA_fnc_encodeJSON]];
|
||||||
if argc == 1 {
|
if argc == 1 {
|
||||||
go writeAttendance(out[0])
|
go writeAttendance(out[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case "writeDisconnectEvent":
|
||||||
|
{ // callExtension ["writeDisconnectEvent", [[_hash] call CBA_fnc_encodeJSON]];
|
||||||
|
|
||||||
|
if argc == 1 {
|
||||||
|
go writeDisconnectEvent(out[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
case "logMission":
|
case "logMission":
|
||||||
if argc == 1 {
|
if argc == 1 {
|
||||||
go writeMissionInfo(out[0])
|
go writeMissionInfo(out[0])
|
||||||
@@ -411,9 +609,10 @@ func callBackExample() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTimestamp() int64 {
|
func getTimestamp() string {
|
||||||
// get the current unix timestamp in nanoseconds
|
// 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 {
|
func trimQuotes(s string) string {
|
||||||
@@ -435,6 +634,9 @@ func writeLog(functionName string, data string) {
|
|||||||
defer C.free(unsafe.Pointer(statusParam))
|
defer C.free(unsafe.Pointer(statusParam))
|
||||||
runExtensionCallback(statusName, statusFunction, 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)
|
log.Printf(`%s: %s`, functionName, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,12 +653,12 @@ func goRVExtension(output *C.char, outputsize C.size_t, input *C.char) {
|
|||||||
case "getDir":
|
case "getDir":
|
||||||
temp = getDir()
|
temp = getDir()
|
||||||
case "getTimestamp":
|
case "getTimestamp":
|
||||||
time := getTimestamp()
|
temp = fmt.Sprintf(`["%s"]`, getTimestamp())
|
||||||
temp = fmt.Sprintf(`["%s"]`, strconv.FormatInt(time, 10))
|
|
||||||
case "connectDB":
|
case "connectDB":
|
||||||
go connectDB()
|
go connectDB()
|
||||||
temp = fmt.Sprintf(`["%s"]`, "Connecting to DB")
|
temp = fmt.Sprintf(`["%s"]`, "Connecting to DB")
|
||||||
|
case "getMissionHash":
|
||||||
|
temp = fmt.Sprintf(`["%s"]`, getMissionHash())
|
||||||
default:
|
default:
|
||||||
temp = fmt.Sprintf(`["%s"]`, "Unknown Function")
|
temp = fmt.Sprintf(`["%s"]`, "Unknown Function")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user