mirror of
https://github.com/indig0fox/Arma3-AttendanceTracker.git/
synced 2025-12-08 09:51:47 -06:00
Compare commits
3 Commits
71ec70ef6a
...
4d50801c29
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d50801c29 | |||
|
6cf76d1019
|
|||
|
62fbe8b24c
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,3 +11,5 @@ hemtt.exe
|
|||||||
*.bk
|
*.bk
|
||||||
|
|
||||||
releases/
|
releases/
|
||||||
|
mariadb/db/
|
||||||
|
AttendanceTracker.config.json
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ git_hash=6 # Default: 8
|
|||||||
[files]
|
[files]
|
||||||
include=[
|
include=[
|
||||||
"AttendanceTracker.config.example.json",
|
"AttendanceTracker.config.example.json",
|
||||||
|
# "AttendanceTracker.config.json", # used for copying active config during debugging
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
"README",
|
"README",
|
||||||
"mod.cpp",
|
"mod.cpp",
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
"mysqlHost": "localhost",
|
"mysqlHost": "localhost",
|
||||||
"mysqlPort": 3306,
|
"mysqlPort": 3306,
|
||||||
"mysqlUser": "root",
|
"mysqlUser": "root",
|
||||||
"mysqlPassword": "password",
|
"mysqlPassword": "example",
|
||||||
"mysqlDatabase": "a3attendance"
|
"mysqlDatabase": "a3attendance"
|
||||||
},
|
},
|
||||||
"armaConfig": {
|
"armaConfig": {
|
||||||
"dbUpdateInterval": "90s",
|
"dbUpdateInterval": "90s",
|
||||||
"debug": false,
|
"debug": false,
|
||||||
"traceLogToFile": false
|
"trace": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ In the event that the server crashes and a user has not been in the mission long
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| worlds | Stores world information. |
|
| worlds | Stores world information. |
|
||||||
| missions | Stores mission information. |
|
| missions | Stores mission information. |
|
||||||
| Session | Stores rows that indicate player information and join/disconnect times. |
|
| sessions | Stores rows that indicate player information and join/disconnect times. |
|
||||||
|
|
||||||
### Worlds
|
### Worlds
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ docker pull x1unix/go-mingw:1.20
|
|||||||
|
|
||||||
# version is semantic + build date + git hash
|
# version is semantic + build date + git hash
|
||||||
# e.g. 1.0.0-2021-05-30-1a2b3c4d
|
# e.g. 1.0.0-2021-05-30-1a2b3c4d
|
||||||
$versionSem = '1.0.1'
|
$versionSem = '1.1.1'
|
||||||
$dateStr = Get-Date -Format 'yyyyMMdd'
|
$dateStr = Get-Date -Format 'yyyyMMdd'
|
||||||
$version = "$versionSem-$dateStr-$(git rev-parse --short HEAD)"
|
$version = "$versionSem-$dateStr-$(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "script_mod.hpp"
|
#include "script_component.hpp"
|
||||||
|
|
||||||
class CfgPatches {
|
class CfgPatches {
|
||||||
class AttendanceTracker {
|
class ADDON {
|
||||||
units[] = {};
|
units[] = {};
|
||||||
weapons[] = {};
|
weapons[] = {};
|
||||||
requiredVersion = 2.10;
|
requiredVersion = 2.10;
|
||||||
@@ -17,20 +17,17 @@ class CfgPatches {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class CfgFunctions {
|
class CfgFunctions {
|
||||||
class attendanceTracker {
|
class ADDON {
|
||||||
class functions {
|
class functions {
|
||||||
file = "x\addons\attendancetracker\main\functions";
|
class postInit {
|
||||||
class postInit {postInit = 1;};
|
file = QPATHTOF(DOUBLES(fnc,postInit).sqf);
|
||||||
class callbackHandler {postInit = 1;};
|
postInit = 1;
|
||||||
class getMissionHash {};
|
};
|
||||||
class getMissionInfo {};
|
PATHTO_FNC(getMissionInfo);
|
||||||
class getSettings {};
|
PATHTO_FNC(getWorldInfo);
|
||||||
class getWorldInfo {};
|
PATHTO_FNC(log);
|
||||||
class log {};
|
PATHTO_FNC(missionLoaded);
|
||||||
class missionLoaded {};
|
PATHTO_FNC(onPlayerConnected);
|
||||||
class onPlayerConnected {};
|
|
||||||
class timestamp {};
|
|
||||||
class writePlayer {};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
13
addons/main/fnc_getMissionInfo.sqf
Normal file
13
addons/main/fnc_getMissionInfo.sqf
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#include "script_component.hpp"
|
||||||
|
[
|
||||||
|
["missionName", missionName],
|
||||||
|
["missionStart", GVAR(missionStart)],
|
||||||
|
["missionHash", GVAR(missionHash)],
|
||||||
|
["briefingName", briefingName],
|
||||||
|
["missionNameSource", missionNameSource],
|
||||||
|
["onLoadName", getMissionConfigValue ["onLoadName", "Unknown"]],
|
||||||
|
["author", getMissionConfigValue ["author", "Unknown"]],
|
||||||
|
["serverName", serverName],
|
||||||
|
["serverProfile", profileName],
|
||||||
|
["worldName", toLower worldName]
|
||||||
|
];
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#include "script_component.hpp"
|
||||||
|
|
||||||
_world = ( configfile >> "CfgWorlds" >> worldName );
|
_world = ( configfile >> "CfgWorlds" >> worldName );
|
||||||
_author = getText( _world >> "author" );
|
_author = getText( _world >> "author" );
|
||||||
_name = getText ( _world >> "description" );
|
_name = getText ( _world >> "description" );
|
||||||
@@ -5,7 +7,6 @@ _name = getText ( _world >> "description" );
|
|||||||
_source = configSourceMod ( _world );
|
_source = configSourceMod ( _world );
|
||||||
|
|
||||||
_workshopID = '';
|
_workshopID = '';
|
||||||
|
|
||||||
{
|
{
|
||||||
if ( ( _x#1 ) == _source ) then {
|
if ( ( _x#1 ) == _source ) then {
|
||||||
_workshopID = _x#7;
|
_workshopID = _x#7;
|
||||||
@@ -13,8 +14,12 @@ _workshopID = '';
|
|||||||
};
|
};
|
||||||
} foreach getLoadedModsInfo;
|
} foreach getLoadedModsInfo;
|
||||||
|
|
||||||
|
if (_workshopID isEqualTo "") then {
|
||||||
|
_workshopID = "0";
|
||||||
|
};
|
||||||
|
|
||||||
// [_name, _author, _workshopID];
|
// [_name, _author, _workshopID];
|
||||||
_return = createHashMapFromArray [
|
_return = [
|
||||||
["author", _author],
|
["author", _author],
|
||||||
["workshopID", _workshopID],
|
["workshopID", _workshopID],
|
||||||
["displayName", _name],
|
["displayName", _name],
|
||||||
@@ -24,5 +29,5 @@ _return = createHashMapFromArray [
|
|||||||
["latitude", -1 * getNumber( _world >> "latitude" )],
|
["latitude", -1 * getNumber( _world >> "latitude" )],
|
||||||
["longitude", getNumber( _world >> "longitude" )]
|
["longitude", getNumber( _world >> "longitude" )]
|
||||||
];
|
];
|
||||||
[format["WorldInfo is: %1", _return]] call attendanceTracker_fnc_log;
|
["DEBUG", format["WorldInfo is: %1", _return]] call FUNC(log);
|
||||||
_return
|
_return
|
||||||
31
addons/main/fnc_log.sqf
Normal file
31
addons/main/fnc_log.sqf
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include "script_component.hpp"
|
||||||
|
|
||||||
|
if (!isServer) exitWith {};
|
||||||
|
|
||||||
|
if (typeName _this != "ARRAY") exitWith {
|
||||||
|
diag_log format ["[%1]: Invalid log params: %2", GVAR(logPrefix), _this];
|
||||||
|
};
|
||||||
|
|
||||||
|
params [
|
||||||
|
["_level", "INFO", [""]],
|
||||||
|
["_text", "", ["", []]]
|
||||||
|
];
|
||||||
|
|
||||||
|
if (_text isEqualType []) then {
|
||||||
|
_text = format ["%1", _text];
|
||||||
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
_level == "DEBUG" &&
|
||||||
|
!GVAR(debug)
|
||||||
|
) exitWith {};
|
||||||
|
|
||||||
|
if (_text isEqualTo "") exitWith {};
|
||||||
|
|
||||||
|
diag_log formatText [
|
||||||
|
"[%1] %2: %3",
|
||||||
|
GVAR(logPrefix),
|
||||||
|
_level,
|
||||||
|
_text
|
||||||
|
];
|
||||||
|
|
||||||
@@ -1 +1,3 @@
|
|||||||
|
#include "script_component.hpp"
|
||||||
|
|
||||||
!(getClientStateNumber <= 5 || getClientStateNumber isEqualTo 11);
|
!(getClientStateNumber <= 5 || getClientStateNumber isEqualTo 11);
|
||||||
84
addons/main/fnc_onPlayerConnected.sqf
Normal file
84
addons/main/fnc_onPlayerConnected.sqf
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#include "script_component.hpp"
|
||||||
|
|
||||||
|
params ["_id", "_uid", "_name", "_jip", "_owner", "_idstr"];
|
||||||
|
|
||||||
|
["DEBUG", format ["(EventHandler) PlayerConnected fired: %1", _this]] call FUNC(log);
|
||||||
|
|
||||||
|
if !(call FUNC(missionLoaded)) exitWith {
|
||||||
|
["DEBUG", format ["(EventHandler) PlayerConnected: Server is in Mission Asked, likely mission selection state. Skipping.."]] call FUNC(log);
|
||||||
|
};
|
||||||
|
|
||||||
|
private _userInfo = (getUserInfo _idstr);
|
||||||
|
if ((count _userInfo) isEqualTo 0) exitWith {
|
||||||
|
["DEBUG", format ["(EventHandler) PlayerConnected: No user info found for %1", _idstr]] call FUNC(log);
|
||||||
|
};
|
||||||
|
|
||||||
|
_userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"];
|
||||||
|
if (_isHC) exitWith {
|
||||||
|
[
|
||||||
|
"DEBUG",
|
||||||
|
format [
|
||||||
|
"(EventHandler) PlayerConnected: %1 is HC, skipping",
|
||||||
|
_playerID
|
||||||
|
]
|
||||||
|
] call FUNC(log);
|
||||||
|
};
|
||||||
|
|
||||||
|
// start CBA PFH
|
||||||
|
[
|
||||||
|
"DEBUG",
|
||||||
|
format [
|
||||||
|
"(EventHandler) PlayerConnected: Starting CBA PFH for %1",
|
||||||
|
_playerID
|
||||||
|
]
|
||||||
|
] call FUNC(log);
|
||||||
|
|
||||||
|
[{
|
||||||
|
params ["_args", "_handle"];
|
||||||
|
|
||||||
|
// every dbUpdateInterval, queue a wait for the mission to be logged
|
||||||
|
// times out after 30 seconds
|
||||||
|
// used to ensure joins at start of mission (during db connect) are logged
|
||||||
|
[{GVAR(missionLogged)}, {
|
||||||
|
// check if player is still connected
|
||||||
|
private _hash = _this;
|
||||||
|
private _clientStateNumber = 0;
|
||||||
|
|
||||||
|
private _userInfo = getUserInfo (_hash get "playerId");
|
||||||
|
if (_userInfo isEqualTo []) exitWith {
|
||||||
|
["DEBUG", format ["(EventHandler) PlayerConnected: %1 (UID) is no longer connected to the mission, exiting CBA PFH", _hash get "playerUID"]] call FUNC(log);
|
||||||
|
[_handle] call CBA_fnc_removePerFrameHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
_clientStateNumber = _userInfo select 6;
|
||||||
|
|
||||||
|
if (_clientStateNumber < 6) exitWith {
|
||||||
|
["DEBUG", format ["(EventHandler) PlayerConnected: %1 (UID) is no longer connected to the mission, exiting CBA PFH", _hash get "playerUID"]] call FUNC(log);
|
||||||
|
[_handle] call CBA_fnc_removePerFrameHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
["DEBUG", format [
|
||||||
|
"(EventHandler) PlayerConnected: %1 (UID) is connected to the mission, logging. data: %2",
|
||||||
|
_hash get "playerUID",
|
||||||
|
_hash
|
||||||
|
]] call FUNC(log);
|
||||||
|
GVAR(extensionName) callExtension [
|
||||||
|
":LOG:PRESENCE:", [
|
||||||
|
_hash
|
||||||
|
]];
|
||||||
|
},
|
||||||
|
_args, // args
|
||||||
|
30 // timeout
|
||||||
|
] call CBA_fnc_waitUntilAndExecute;
|
||||||
|
},
|
||||||
|
GVAR(updateInterval),
|
||||||
|
(createHashMapFromArray [ // args
|
||||||
|
["playerId", _playerID],
|
||||||
|
["playerUID", _playerUID],
|
||||||
|
["profileName", _profileName],
|
||||||
|
["steamName", _steamName],
|
||||||
|
["isJIP", _jip],
|
||||||
|
["roleDescription", if (roleDescription _unit isEqualTo "") then {"None"} else {roleDescription _unit}],
|
||||||
|
["missionHash", GVAR(missionHash)]
|
||||||
|
])
|
||||||
|
] call CBA_fnc_addPerFrameHandler;
|
||||||
94
addons/main/fnc_postInit.sqf
Normal file
94
addons/main/fnc_postInit.sqf
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#include "script_component.hpp"
|
||||||
|
|
||||||
|
if (!isServer) exitWith {};
|
||||||
|
|
||||||
|
GVAR(attendanceTracker) = true;
|
||||||
|
GVAR(debug) = true;
|
||||||
|
GVAR(logPrefix) = "AttendanceTracker";
|
||||||
|
GVAR(extensionName) = "AttendanceTracker";
|
||||||
|
GVAR(missionLogged) = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
addMissionEventHandler ["ExtensionCallback", {
|
||||||
|
params ["_name", "_function", "_data"];
|
||||||
|
if !(_name isEqualTo GVAR(extensionName)) exitWith {};
|
||||||
|
|
||||||
|
_dataArr = parseSimpleArray _data;
|
||||||
|
if (count _dataArr isEqualTo 0) exitWith {};
|
||||||
|
|
||||||
|
switch (_function) do {
|
||||||
|
case ":LOG:MISSION:SUCCESS:": {
|
||||||
|
GVAR(missionLogged) = true;
|
||||||
|
};
|
||||||
|
case ":LOG:": {
|
||||||
|
diag_log formatText[
|
||||||
|
"[%1] %2",
|
||||||
|
GVAR(logPrefix),
|
||||||
|
_dataArr select 0
|
||||||
|
];
|
||||||
|
};
|
||||||
|
default {
|
||||||
|
["DEBUG", format["%1", _dataArr]] call FUNC(log);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}];
|
||||||
|
|
||||||
|
|
||||||
|
// LOAD EXTENSION
|
||||||
|
GVAR(extensionName) callExtension ":START:";
|
||||||
|
|
||||||
|
// GET MISSION START TIMESTAMP AND UNIQUE HASH
|
||||||
|
private _missionHashData = parseSimpleArray ("AttendanceTracker" callExtension ":MISSION:HASH:");
|
||||||
|
if (count _missionHashData isEqualTo 0) exitWith {
|
||||||
|
["ERROR", "Failed to get mission hash, exiting"] call FUNC(log);
|
||||||
|
};
|
||||||
|
|
||||||
|
_missionHashData params ["_timestamp", "_hash"];
|
||||||
|
GVAR(missionStart) = _timestamp;
|
||||||
|
GVAR(missionHash) = _hash;
|
||||||
|
|
||||||
|
|
||||||
|
// PARSE SETTINGS
|
||||||
|
private _settings = parseSimpleArray (GVAR(extensionName) callExtension ":GET:SETTINGS:");
|
||||||
|
if (count _settings isEqualTo 0) exitWith {
|
||||||
|
["ERROR", "Failed to get settings, exiting"] call FUNC(log);
|
||||||
|
};
|
||||||
|
|
||||||
|
GVAR(settings) = createHashMapFromArray (_settings#0);
|
||||||
|
GVAR(debug) = GVAR(settings) getOrDefault ["debug", GVAR(debug)];
|
||||||
|
private _updateInterval = GVAR(settings) getOrDefault ["dbupdateinterval", 90];
|
||||||
|
// remove duration by removing the last index
|
||||||
|
_updateInterval = _updateInterval select [0, count _updateInterval - 1];
|
||||||
|
GVAR(updateInterval) = parseNumber _updateInterval;
|
||||||
|
|
||||||
|
// add player connected (to mission) handler
|
||||||
|
addMissionEventHandler ["PlayerConnected", {
|
||||||
|
_this call FUNC(onPlayerConnected);
|
||||||
|
}];
|
||||||
|
|
||||||
|
|
||||||
|
// we'll wait for the end of init (DB connect included) of the extension
|
||||||
|
// then we'll log the world and mission
|
||||||
|
// the response to THAT is handled above in the extension callback
|
||||||
|
// and will set GVAR(missionLogged) true
|
||||||
|
addMissionEventHandler ["ExtensionCallback", {
|
||||||
|
params ["_name", "_function", "_data"];
|
||||||
|
if !(_name isEqualTo GVAR(extensionName)) exitWith {};
|
||||||
|
if !(_function isEqualTo ":READY:") exitWith {};
|
||||||
|
|
||||||
|
// LOAD WORLD AND MISSION INFO
|
||||||
|
GVAR(worldInfo) = call FUNC(getWorldInfo);
|
||||||
|
GVAR(missionInfo) = call FUNC(getMissionInfo);
|
||||||
|
|
||||||
|
["INFO", (GVAR(extensionName) callExtension [
|
||||||
|
":LOG:MISSION:",
|
||||||
|
[
|
||||||
|
GVAR(worldInfo),
|
||||||
|
GVAR(missionInfo)
|
||||||
|
]
|
||||||
|
]) select 0] call FUNC(log);
|
||||||
|
|
||||||
|
// remove the handler
|
||||||
|
removeMissionEventHandler ["ExtensionCallback", _thisEventHandler];
|
||||||
|
}];
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
addMissionEventHandler ["ExtensionCallback", {
|
|
||||||
params ["_name", "_function", "_data"];
|
|
||||||
if !(_name isEqualTo "AttendanceTracker") exitWith {};
|
|
||||||
|
|
||||||
if (ATDebug && _function isNotEqualTo ":LOG:") then {
|
|
||||||
diag_log format ["Raw callback: %1 _ %2", _function, _data];
|
|
||||||
};
|
|
||||||
|
|
||||||
_dataArr = parseSimpleArray _data;
|
|
||||||
if (count _dataArr < 1) exitWith {};
|
|
||||||
|
|
||||||
switch (_function) do {
|
|
||||||
case ":LOG:": {
|
|
||||||
diag_log formatText[
|
|
||||||
"[Attendance Tracker] %1",
|
|
||||||
_dataArr select 0
|
|
||||||
];
|
|
||||||
};
|
|
||||||
default {
|
|
||||||
[format["%1", _dataArr]] call attendanceTracker_fnc_log;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
true;
|
|
||||||
}];
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
addMissionEventHandler ["ExtensionCallback", {
|
|
||||||
params ["_extension", "_function", "_data"];
|
|
||||||
if !(_extension isEqualTo "AttendanceTracker") exitWith {};
|
|
||||||
if !(_function isEqualTo ":MISSION:HASH:") exitWith {};
|
|
||||||
|
|
||||||
_dataArr = parseSimpleArray _data;
|
|
||||||
if (count _dataArr < 1) exitWith {};
|
|
||||||
|
|
||||||
_dataArr params ["_startTime", "_hash"];
|
|
||||||
ATNamespace setVariable ["missionStartTime", call attendanceTracker_fnc_timestamp];
|
|
||||||
ATNamespace setVariable ["missionHash", _hash];
|
|
||||||
|
|
||||||
removeMissionEventHandler [
|
|
||||||
"ExtensionCallback",
|
|
||||||
_thisEventHandler
|
|
||||||
];
|
|
||||||
}];
|
|
||||||
|
|
||||||
"AttendanceTracker" callExtension ":MISSION:HASH:";
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
createHashMapFromArray [
|
|
||||||
["missionName", missionName],
|
|
||||||
["missionStart", ATNamespace getVariable "missionStartTime"],
|
|
||||||
["missionHash", ATNamespace getVariable "missionHash"],
|
|
||||||
["briefingName", briefingName],
|
|
||||||
["missionNameSource", missionNameSource],
|
|
||||||
["onLoadName", getMissionConfigValue ["onLoadName", ""]],
|
|
||||||
["author", getMissionConfigValue ["author", ""]],
|
|
||||||
["serverName", serverName],
|
|
||||||
["serverProfile", profileName],
|
|
||||||
["worldName", toLower worldName]
|
|
||||||
];
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
addMissionEventHandler ["ExtensionCallback", {
|
|
||||||
params ["_extension", "_function", "_data"];
|
|
||||||
if !(_extension isEqualTo "AttendanceTracker") exitWith {};
|
|
||||||
if !(_function isEqualTo ":GET:SETTINGS:") exitWith {};
|
|
||||||
|
|
||||||
_dataArr = parseSimpleArray _data;
|
|
||||||
diag_log format ["AT: Settings received: %1", _dataArr];
|
|
||||||
if (count _dataArr < 1) exitWith {};
|
|
||||||
|
|
||||||
private _settingsJSON = _dataArr select 0;
|
|
||||||
private _settingsNamespace = [_settingsJSON] call CBA_fnc_parseJSON;
|
|
||||||
{
|
|
||||||
ATNamespace setVariable [_x, _settingsNamespace getVariable _x];
|
|
||||||
} forEach (allVariables _settingsNamespace);
|
|
||||||
ATDebug = ATNamespace getVariable "debug";
|
|
||||||
ATUpdateDelay = ATNamespace getVariable "dbUpdateInterval";
|
|
||||||
// remove last character (unit of time) and parse to number
|
|
||||||
ATUpdateDelay = parseNumber (ATUpdateDelay select [0, count ATUpdateDelay - 1]);
|
|
||||||
|
|
||||||
|
|
||||||
removeMissionEventHandler [
|
|
||||||
"ExtensionCallback",
|
|
||||||
_thisEventHandler
|
|
||||||
];
|
|
||||||
}];
|
|
||||||
|
|
||||||
"AttendanceTracker" callExtension ":GET:SETTINGS:";
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#include "..\script_mod.hpp"
|
|
||||||
|
|
||||||
params [
|
|
||||||
["_message", "", [""]],
|
|
||||||
["_level", "INFO", [""]],
|
|
||||||
"_function"
|
|
||||||
];
|
|
||||||
|
|
||||||
if (isNil "_message") exitWith {false};
|
|
||||||
if (
|
|
||||||
missionNamespace getVariable ["ATDebug", true] &&
|
|
||||||
_level != "WARN" && _level != "ERROR"
|
|
||||||
) exitWith {};
|
|
||||||
|
|
||||||
LOG_SYS(_level, _message);
|
|
||||||
|
|
||||||
true;
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
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 ((count _userInfo) isEqualTo 0) 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
// start CBA PFH
|
|
||||||
[
|
|
||||||
format [
|
|
||||||
"(EventHandler) PlayerConnected: Starting CBA PFH for %1",
|
|
||||||
_playerID
|
|
||||||
],
|
|
||||||
"DEBUG"
|
|
||||||
] call attendanceTracker_fnc_log;
|
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
params ["_args", "_handle"];
|
|
||||||
// check if player is still connected
|
|
||||||
_args params ["_playerID", "_playerUID", "_profileName", "_steamName", "_jip", "_roleDescription"];
|
|
||||||
private _userInfo = getUserInfo _playerID;
|
|
||||||
private _clientStateNumber = 0;
|
|
||||||
if (_userInfo isEqualTo []) exitWith {
|
|
||||||
[_handle] call CBA_fnc_removePerFrameHandler;
|
|
||||||
};
|
|
||||||
|
|
||||||
_clientStateNumber = _userInfo select 6;
|
|
||||||
|
|
||||||
if (_clientStateNumber < 6) exitWith {
|
|
||||||
[format ["(EventHandler) PlayerConnected: %1 (UID) is no longer connected to the mission, exiting CBA PFH", _playerID], "DEBUG"] call attendanceTracker_fnc_log;
|
|
||||||
[_handle] call CBA_fnc_removePerFrameHandler;
|
|
||||||
};
|
|
||||||
|
|
||||||
_args call attendanceTracker_fnc_writePlayer;
|
|
||||||
},
|
|
||||||
ATUpdateDelay,
|
|
||||||
[
|
|
||||||
_playerID,
|
|
||||||
_playerUID,
|
|
||||||
_profileName,
|
|
||||||
_steamName,
|
|
||||||
_jip,
|
|
||||||
roleDescription _unit
|
|
||||||
]
|
|
||||||
] call CBA_fnc_addPerFrameHandler;
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
#include "..\script_mod.hpp"
|
|
||||||
|
|
||||||
if (!isServer) exitWith {};
|
|
||||||
|
|
||||||
ATNamespace = false call CBA_fnc_createNamespace;
|
|
||||||
ATDebug = true;
|
|
||||||
"AttendanceTracker" callExtension ":START:";
|
|
||||||
|
|
||||||
|
|
||||||
// we'll wait for the asynchronous init steps of the extension to finish, to confirm we have a DB connection and our config was loaded. If there are errors with either, the extension won't reply and initiate further during this mission.
|
|
||||||
addMissionEventHandler ["ExtensionCallback", {
|
|
||||||
params ["_name", "_function", "_data"];
|
|
||||||
if !(_name isEqualTo "AttendanceTracker") exitWith {};
|
|
||||||
if !(_function isEqualTo ":READY:") exitWith {};
|
|
||||||
|
|
||||||
call attendanceTracker_fnc_getMissionHash;
|
|
||||||
call attendanceTracker_fnc_getSettings;
|
|
||||||
|
|
||||||
[
|
|
||||||
{// wait until settings have been loaded from extension
|
|
||||||
!isNil {ATNamespace getVariable "missionHash"} &&
|
|
||||||
!isNil {ATDebug}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
|
|
||||||
// get world and mission context
|
|
||||||
ATNamespace setVariable [
|
|
||||||
"worldContext",
|
|
||||||
call attendanceTracker_fnc_getWorldInfo
|
|
||||||
];
|
|
||||||
ATNamespace setVariable [
|
|
||||||
"missionContext",
|
|
||||||
call attendanceTracker_fnc_getMissionInfo
|
|
||||||
];
|
|
||||||
|
|
||||||
// write them to establish DB rows
|
|
||||||
"AttendanceTracker" callExtension [
|
|
||||||
":LOG:MISSION:",
|
|
||||||
[
|
|
||||||
[ATNamespace getVariable "missionContext"] call CBA_fnc_encodeJSON,
|
|
||||||
[ATNamespace getVariable "worldContext"] call CBA_fnc_encodeJSON
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
// add player connected (to mission) handler
|
|
||||||
addMissionEventHandler ["PlayerConnected", {
|
|
||||||
_this call attendanceTracker_fnc_onPlayerConnected;
|
|
||||||
}];
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
10, // 10 second timeout
|
|
||||||
{ // timeout code
|
|
||||||
["Failed to load settings", "ERROR"] call attendanceTracker_fnc_log;
|
|
||||||
}
|
|
||||||
] call CBA_fnc_waitUntilAndExecute;
|
|
||||||
|
|
||||||
removeMissionEventHandler [
|
|
||||||
"ExtensionCallback",
|
|
||||||
_thisEventHandler
|
|
||||||
];
|
|
||||||
}];
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
// (parseSimpleArray ("AttendanceTracker" callExtension "getTimestamp")) select 0;
|
|
||||||
|
|
||||||
// const time.RFC3339 untyped string = "2006-01-02T15:04:05Z07:00"
|
|
||||||
|
|
||||||
systemTimeUTC apply {if (_x < 10) then {"0" + str _x} else {str _x}} params [
|
|
||||||
"_year",
|
|
||||||
"_month",
|
|
||||||
"_day",
|
|
||||||
"_hour",
|
|
||||||
"_minute",
|
|
||||||
"_second",
|
|
||||||
"_millisecond"
|
|
||||||
];
|
|
||||||
|
|
||||||
format[
|
|
||||||
"%1-%2-%3T%4:%5:%6Z",
|
|
||||||
_year,
|
|
||||||
_month,
|
|
||||||
_day,
|
|
||||||
_hour,
|
|
||||||
_minute,
|
|
||||||
_second
|
|
||||||
];
|
|
||||||
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
params [
|
|
||||||
["_playerId", ""],
|
|
||||||
["_playerUID", ""],
|
|
||||||
["_profileName", ""],
|
|
||||||
["_steamName", ""],
|
|
||||||
["_isJIP", false, [true, false]],
|
|
||||||
["_roleDescription", ""]
|
|
||||||
];
|
|
||||||
|
|
||||||
private _hash = +(ATNamespace getVariable ["missionContext", createHashMap]);
|
|
||||||
|
|
||||||
_hash set ["playerId", _playerId];
|
|
||||||
_hash set ["playerUID", _playerUID];
|
|
||||||
_hash set ["profileName", _profileName];
|
|
||||||
_hash set ["steamName", _steamName];
|
|
||||||
_hash set ["isJIP", _isJIP];
|
|
||||||
_hash set ["roleDescription", _roleDescription];
|
|
||||||
|
|
||||||
"AttendanceTracker" callExtension [":LOG:PRESENCE:", [[_hash] call CBA_fnc_encodeJSON]];
|
|
||||||
|
|
||||||
true;
|
|
||||||
4
addons/main/script_component.hpp
Normal file
4
addons/main/script_component.hpp
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#define COMPONENT main
|
||||||
|
#define COMPONENT_BEAUTIFIED Main
|
||||||
|
|
||||||
|
#include "\x\attendancetracker\addons\main\script_mod.hpp"
|
||||||
@@ -1,12 +1,8 @@
|
|||||||
#include "script_version.hpp"
|
#include "script_version.hpp"
|
||||||
|
|
||||||
#define COMPONENT main
|
|
||||||
#define COMPONENT_BEAUTIFIED Main
|
|
||||||
|
|
||||||
#define MAINPREFIX x
|
#define MAINPREFIX x
|
||||||
|
#define PREFIX attendancetracker
|
||||||
|
#define PREFIX_BEAUTIFIED AttendanceTracker
|
||||||
#define SUBPREFIX addons
|
#define SUBPREFIX addons
|
||||||
#define PREFIX AttendanceTracker
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "\x\cba\addons\main\script_macros_common.hpp"
|
#include "\x\cba\addons\main\script_macros_common.hpp"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#define MAJOR 1
|
#define MAJOR 1
|
||||||
#define MINOR 1
|
#define MINOR 1
|
||||||
#define PATCH 0
|
#define PATCH 0
|
||||||
#define BUILD 20231003
|
#define BUILD 20231012
|
||||||
|
|
||||||
#define VERSION 1.1
|
#define VERSION 1.1
|
||||||
#define VERSION_STR MAJOR##.##MINOR##.##PATCH##.##BUILD
|
#define VERSION_STR MAJOR##.##MINOR##.##PATCH##.##BUILD
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
@@ -22,35 +21,25 @@ import (
|
|||||||
"github.com/indig0fox/Arma3-AttendanceTracker/internal/util"
|
"github.com/indig0fox/Arma3-AttendanceTracker/internal/util"
|
||||||
"github.com/indig0fox/a3go/a3interface"
|
"github.com/indig0fox/a3go/a3interface"
|
||||||
"github.com/indig0fox/a3go/assemblyfinder"
|
"github.com/indig0fox/a3go/assemblyfinder"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
)
|
)
|
||||||
|
|
||||||
const EXTENSION_NAME string = "AttendanceTracker"
|
const EXTENSION_NAME string = "AttendanceTracker"
|
||||||
const ADDON_NAME string = "AttendanceTracker"
|
const ADDON_NAME string = "AttendanceTracker"
|
||||||
const EXTENSION_VERSION string = "dev"
|
|
||||||
|
|
||||||
// file paths
|
// file paths
|
||||||
const ATTENDANCE_TABLE string = "attendance"
|
const ATTENDANCE_TABLE string = "attendance"
|
||||||
const MISSIONS_TABLE string = "missions"
|
const MISSIONS_TABLE string = "missions"
|
||||||
const WORLDS_TABLE string = "worlds"
|
const WORLDS_TABLE string = "worlds"
|
||||||
|
|
||||||
var currentMissionID uint = 0
|
|
||||||
|
|
||||||
var RVExtensionChannels = map[string]chan string{
|
|
||||||
":START:": make(chan string),
|
|
||||||
":MISSION:HASH:": make(chan string),
|
|
||||||
":GET:SETTINGS:": make(chan string),
|
|
||||||
}
|
|
||||||
|
|
||||||
var RVExtensionArgsChannels = map[string]chan []string{
|
|
||||||
":LOG:MISSION:": make(chan []string),
|
|
||||||
":LOG:PRESENCE:": make(chan []string),
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
EXTENSION_VERSION string = "DEVELOPMENT"
|
||||||
|
|
||||||
modulePath string
|
modulePath string
|
||||||
modulePathDir string
|
modulePathDir string
|
||||||
|
|
||||||
initSuccess bool // default false
|
loadedMission *Mission
|
||||||
|
loadedWorld *World
|
||||||
)
|
)
|
||||||
|
|
||||||
// configure log output
|
// configure log output
|
||||||
@@ -58,31 +47,28 @@ func init() {
|
|||||||
|
|
||||||
a3interface.SetVersion(EXTENSION_VERSION)
|
a3interface.SetVersion(EXTENSION_VERSION)
|
||||||
a3interface.NewRegistration(":START:").
|
a3interface.NewRegistration(":START:").
|
||||||
SetDefaultResponse(`["Extension beginning init process"]`).
|
|
||||||
SetFunction(onStartCommand).
|
SetFunction(onStartCommand).
|
||||||
SetRunInBackground(true).
|
SetRunInBackground(false).
|
||||||
Register()
|
Register()
|
||||||
|
|
||||||
a3interface.NewRegistration(":MISSION:HASH:").
|
a3interface.NewRegistration(":MISSION:HASH:").
|
||||||
SetDefaultResponse(`["Retrieving mission hash"]`).
|
|
||||||
SetFunction(onMissionHashCommand).
|
SetFunction(onMissionHashCommand).
|
||||||
SetRunInBackground(true).
|
SetRunInBackground(false).
|
||||||
Register()
|
Register()
|
||||||
|
|
||||||
a3interface.NewRegistration(":GET:SETTINGS:").
|
a3interface.NewRegistration(":GET:SETTINGS:").
|
||||||
SetDefaultResponse(`["Retrieving settings"]`).
|
|
||||||
SetFunction(onGetSettingsCommand).
|
SetFunction(onGetSettingsCommand).
|
||||||
SetRunInBackground(true).
|
SetRunInBackground(false).
|
||||||
Register()
|
Register()
|
||||||
|
|
||||||
a3interface.NewRegistration(":LOG:MISSION:").
|
a3interface.NewRegistration(":LOG:MISSION:").
|
||||||
SetDefaultResponse(`["Logging mission data"]`).
|
SetDefaultResponse(`Logging mission data`).
|
||||||
SetArgsFunction(onLogMissionArgsCommand).
|
SetArgsFunction(onLogMissionArgsCommand).
|
||||||
SetRunInBackground(true).
|
SetRunInBackground(true).
|
||||||
Register()
|
Register()
|
||||||
|
|
||||||
a3interface.NewRegistration(":LOG:PRESENCE:").
|
a3interface.NewRegistration(":LOG:PRESENCE:").
|
||||||
SetDefaultResponse(`["Logging presence data"]`).
|
SetDefaultResponse(`Logging presence data`).
|
||||||
SetArgsFunction(onLogPresenceArgsCommand).
|
SetArgsFunction(onLogPresenceArgsCommand).
|
||||||
SetRunInBackground(true).
|
SetRunInBackground(true).
|
||||||
Register()
|
Register()
|
||||||
@@ -91,12 +77,7 @@ func init() {
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
modulePath = assemblyfinder.GetModulePath()
|
modulePath = assemblyfinder.GetModulePath()
|
||||||
// get absolute path of module path
|
modulePathDir = filepath.Dir(modulePath)
|
||||||
modulePathAbs, err := filepath.Abs(modulePath)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
modulePathDir = filepath.Dir(modulePathAbs)
|
|
||||||
|
|
||||||
result, configErr := util.LoadConfig(modulePathDir)
|
result, configErr := util.LoadConfig(modulePathDir)
|
||||||
logger.InitLoggers(&logger.LoggerOptionsType{
|
logger.InitLoggers(&logger.LoggerOptionsType{
|
||||||
@@ -107,11 +88,13 @@ func init() {
|
|||||||
EXTENSION_NAME,
|
EXTENSION_NAME,
|
||||||
EXTENSION_VERSION,
|
EXTENSION_VERSION,
|
||||||
)),
|
)),
|
||||||
AddonName: ADDON_NAME,
|
AddonName: ADDON_NAME,
|
||||||
ExtensionName: EXTENSION_NAME,
|
ExtensionName: EXTENSION_NAME,
|
||||||
Debug: util.ConfigJSON.GetBool("armaConfig.debug"),
|
ExtensionVersion: EXTENSION_VERSION,
|
||||||
Trace: util.ConfigJSON.GetBool("armaConfig.traceLogToFile"),
|
Debug: util.ConfigJSON.GetBool("armaConfig.debug"),
|
||||||
|
Trace: util.ConfigJSON.GetBool("armaConfig.trace"),
|
||||||
})
|
})
|
||||||
|
logger.RotateLogs()
|
||||||
if configErr != nil {
|
if configErr != nil {
|
||||||
logger.Log.Error().Err(configErr).Msgf(`Error loading config`)
|
logger.Log.Error().Err(configErr).Msgf(`Error loading config`)
|
||||||
return
|
return
|
||||||
@@ -119,9 +102,7 @@ func init() {
|
|||||||
logger.Log.Info().Msgf(result)
|
logger.Log.Info().Msgf(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.RotateLogs()
|
logger.Log.Info().Msgf(`%s v%s started`, EXTENSION_NAME, EXTENSION_VERSION)
|
||||||
|
|
||||||
logger.ArmaOnly.Info().Msgf(`%s v%s started`, EXTENSION_NAME, "0.0.0")
|
|
||||||
logger.ArmaOnly.Info().Msgf(`Log path: %s`, logger.ActiveOptions.Path)
|
logger.ArmaOnly.Info().Msgf(`Log path: %s`, logger.ActiveOptions.Path)
|
||||||
|
|
||||||
db.SetConfig(db.ConfigStruct{
|
db.SetConfig(db.ConfigStruct{
|
||||||
@@ -151,9 +132,10 @@ func init() {
|
|||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Error().Err(err).Msgf(`Error migrating database schema`)
|
logger.Log.Error().Err(err).Msgf(`Error migrating database schema`)
|
||||||
|
} else {
|
||||||
|
logger.Log.Info().Msgf(`Database schema migrated`)
|
||||||
}
|
}
|
||||||
|
|
||||||
initSuccess = true
|
|
||||||
a3interface.WriteArmaCallback(
|
a3interface.WriteArmaCallback(
|
||||||
EXTENSION_NAME,
|
EXTENSION_NAME,
|
||||||
":READY:",
|
":READY:",
|
||||||
@@ -167,32 +149,22 @@ func onStartCommand(
|
|||||||
ctx a3interface.ArmaExtensionContext,
|
ctx a3interface.ArmaExtensionContext,
|
||||||
data string,
|
data string,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
logger.Log.Trace().Msgf(`RVExtension :START: requested`)
|
logger.Log.Debug().Msgf(`RVExtension :START: requested`)
|
||||||
if !initSuccess {
|
loadedWorld = nil
|
||||||
logger.Log.Warn().Msgf(`Received another :START: command before init was complete, ignoring.`)
|
loadedMission = nil
|
||||||
return "Initing!", nil
|
return fmt.Sprintf(
|
||||||
} else {
|
`["%s v%s started"]`,
|
||||||
logger.RotateLogs()
|
EXTENSION_NAME,
|
||||||
a3interface.WriteArmaCallback(
|
EXTENSION_VERSION,
|
||||||
EXTENSION_NAME,
|
), nil
|
||||||
":READY:",
|
|
||||||
)
|
|
||||||
return "Ready!", nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func onMissionHashCommand(
|
func onMissionHashCommand(
|
||||||
ctx a3interface.ArmaExtensionContext,
|
ctx a3interface.ArmaExtensionContext,
|
||||||
data string,
|
data string,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
logger.Log.Trace().Msgf(`RVExtension :MISSION:HASH: requested`)
|
logger.Log.Debug().Msgf(`RVExtension :MISSION:HASH: requested`)
|
||||||
timestamp, hash := getMissionHash()
|
timestamp, hash := getMissionHash()
|
||||||
a3interface.WriteArmaCallback(
|
|
||||||
EXTENSION_NAME,
|
|
||||||
":MISSION:HASH:",
|
|
||||||
timestamp,
|
|
||||||
hash,
|
|
||||||
)
|
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
`[%q, %q]`,
|
`[%q, %q]`,
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -204,19 +176,14 @@ func onGetSettingsCommand(
|
|||||||
ctx a3interface.ArmaExtensionContext,
|
ctx a3interface.ArmaExtensionContext,
|
||||||
data string,
|
data string,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
logger.Log.Trace().Msg(`Settings requested`)
|
logger.Log.Debug().Msg(`RVExtension :GET:SETTINGS: requested`)
|
||||||
armaConfig, err := util.ConfigArmaFormat()
|
// get arma config
|
||||||
if err != nil {
|
c := util.ConfigJSON.Get("armaConfig")
|
||||||
logger.Log.Error().Err(err).Msg(`Error when marshaling arma config`)
|
armaConfig := a3interface.ToArmaHashMap(c)
|
||||||
return "", err
|
return fmt.Sprintf(
|
||||||
}
|
`[%s]`,
|
||||||
logger.Log.Trace().Str("armaConfig", armaConfig).Send()
|
|
||||||
a3interface.WriteArmaCallback(
|
|
||||||
EXTENSION_NAME,
|
|
||||||
":GET:SETTINGS:",
|
|
||||||
armaConfig,
|
armaConfig,
|
||||||
)
|
), nil
|
||||||
return armaConfig, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func onLogMissionArgsCommand(
|
func onLogMissionArgsCommand(
|
||||||
@@ -224,12 +191,27 @@ func onLogMissionArgsCommand(
|
|||||||
command string,
|
command string,
|
||||||
args []string,
|
args []string,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
go func(data []string) {
|
thisLogger := logger.Log.With().Str("command", command).Interface("ctx", ctx).Logger()
|
||||||
writeWorldInfo(data[1])
|
thisLogger.Debug().Msgf(`RVExtension :LOG:MISSION: requested`)
|
||||||
writeMission(data[0])
|
var err error
|
||||||
}(args)
|
world, err := writeWorldInfo(args[0], thisLogger)
|
||||||
|
if err != nil {
|
||||||
|
return ``, err
|
||||||
|
}
|
||||||
|
loadedWorld = &world
|
||||||
|
|
||||||
return `["Logging mission data"]`, nil
|
mission, err := writeMission(args[1], thisLogger)
|
||||||
|
if err != nil {
|
||||||
|
return ``, err
|
||||||
|
}
|
||||||
|
loadedMission = &mission
|
||||||
|
|
||||||
|
a3interface.WriteArmaCallback(
|
||||||
|
EXTENSION_NAME,
|
||||||
|
":LOG:MISSION:SUCCESS:",
|
||||||
|
)
|
||||||
|
|
||||||
|
return ``, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func onLogPresenceArgsCommand(
|
func onLogPresenceArgsCommand(
|
||||||
@@ -237,8 +219,10 @@ func onLogPresenceArgsCommand(
|
|||||||
command string,
|
command string,
|
||||||
args []string,
|
args []string,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
go writeAttendance(args[0])
|
thisLogger := logger.Log.With().Str("command", command).Interface("ctx", ctx).Logger()
|
||||||
return `["Logging presence data"]`, nil
|
thisLogger.Debug().Msgf(`RVExtension :LOG:PRESENCE: requested`)
|
||||||
|
writeAttendance(args[0], thisLogger)
|
||||||
|
return ``, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMissionHash will return the current time in UTC and an md5 hash of that time
|
// getMissionHash will return the current time in UTC and an md5 hash of that time
|
||||||
@@ -248,7 +232,7 @@ func getMissionHash() (sqlTime, hashString string) {
|
|||||||
|
|
||||||
nowTime := time.Now().UTC()
|
nowTime := time.Now().UTC()
|
||||||
// mysql format
|
// mysql format
|
||||||
sqlTime = nowTime.Format("2006-01-02 15:04:05")
|
sqlTime = nowTime.Format(time.RFC3339)
|
||||||
hash := md5.Sum([]byte(sqlTime))
|
hash := md5.Sum([]byte(sqlTime))
|
||||||
hashString = fmt.Sprintf(`%x`, hash)
|
hashString = fmt.Sprintf(`%x`, hash)
|
||||||
|
|
||||||
@@ -291,86 +275,162 @@ func finalizeUnendedSessions() {
|
|||||||
logger.Log.Info().Msgf(`Filled disconnect time of %d events.`, len(events))
|
logger.Log.Info().Msgf(`Filled disconnect time of %d events.`, len(events))
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeWorldInfo(worldInfo string) {
|
func writeWorldInfo(worldInfo string, thisLogger zerolog.Logger) (World, error) {
|
||||||
// worldInfo is json, parse it
|
|
||||||
var wi World
|
parsedInterface, err := a3interface.ParseSQF(worldInfo)
|
||||||
fixedString := unescapeArmaQuotes(worldInfo)
|
|
||||||
err := json.Unmarshal([]byte(fixedString), &wi)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Error().Err(err).Msgf(`Error when unmarshalling world info`)
|
thisLogger.Error().Err(err).Msgf(`Error when parsing world info`)
|
||||||
return
|
return World{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedMap, err := a3interface.ParseSQFHashMap(parsedInterface)
|
||||||
|
if err != nil {
|
||||||
|
thisLogger.Error().Err(err).Msgf(`Error when parsing world info`)
|
||||||
|
return World{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
thisLogger.Trace().Msgf(`parsedMap: %+v`, parsedMap)
|
||||||
|
|
||||||
|
// create world object from map[string]interface{}
|
||||||
|
var wi = World{}
|
||||||
|
worldBytes, err := json.Marshal(parsedMap)
|
||||||
|
if err != nil {
|
||||||
|
thisLogger.Error().Err(err).Msgf(`Error when marshalling world info`)
|
||||||
|
return World{}, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(worldBytes, &wi)
|
||||||
|
if err != nil {
|
||||||
|
thisLogger.Error().Err(err).Msgf(`Error when unmarshalling world info`)
|
||||||
|
return World{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
thisLogger.Trace().Msgf(`World info: %+v`, wi)
|
||||||
|
|
||||||
|
var dbWorld World
|
||||||
|
db.Client().Where("world_name = ?", wi.WorldName).First(&dbWorld)
|
||||||
|
// if world exists, use it
|
||||||
|
if dbWorld.ID > 0 {
|
||||||
|
thisLogger.Debug().Msgf(`World %s exists with ID %d.`, wi.WorldName, dbWorld.ID)
|
||||||
|
return dbWorld, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// write world if not exist
|
// write world if not exist
|
||||||
var dbWorld World
|
db.Client().Create(&wi)
|
||||||
db.Client().Where("world_name = ?", wi.WorldName).First(&dbWorld)
|
if db.Client().Error != nil {
|
||||||
if dbWorld.ID == 0 {
|
thisLogger.Error().Err(db.Client().Error).Msgf(`Error when creating world`)
|
||||||
db.Client().Create(&wi)
|
return World{}, db.Client().Error
|
||||||
if db.Client().Error != nil {
|
|
||||||
logger.Log.Error().Err(db.Client().Error).Msgf(`Error when creating world`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.Log.Info().Msgf(`World %s created.`, wi.WorldName)
|
|
||||||
} else {
|
|
||||||
// don't do anything if exists
|
|
||||||
logger.Log.Debug().Msgf(`World %s exists with ID %d.`, wi.WorldName, dbWorld.ID)
|
|
||||||
}
|
}
|
||||||
|
thisLogger.Info().Msgf(`World %s created.`, wi.WorldName)
|
||||||
|
|
||||||
|
return wi, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeMission(missionJSON string) {
|
func writeMission(data string, thisLogger zerolog.Logger) (Mission, error) {
|
||||||
var err error
|
var err error
|
||||||
// writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, Mission))
|
parsedInterface, err := a3interface.ParseSQF(data)
|
||||||
// Mission is json, parse it
|
|
||||||
var mi Mission
|
|
||||||
fixedString := fixEscapeQuotes(trimQuotes(missionJSON))
|
|
||||||
err = json.Unmarshal([]byte(fixedString), &mi)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Error().Err(err).Msgf(`Error when unmarshalling mission`)
|
thisLogger.Error().Err(err).Msgf(`Error when parsing mission info`)
|
||||||
return
|
return Mission{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// get world from WorldName
|
parsedMap, err := a3interface.ParseSQFHashMap(parsedInterface)
|
||||||
var dbWorld World
|
if err != nil {
|
||||||
db.Client().Where("world_name = ?", mi.WorldName).First(&dbWorld)
|
thisLogger.Error().Err(err).Msgf(`Error when parsing mission info`)
|
||||||
if dbWorld.ID == 0 {
|
return Mission{}, err
|
||||||
logger.Log.Error().Msgf(`World %s not found.`, mi.WorldName)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mi.WorldID = dbWorld.ID
|
thisLogger.Trace().Msgf(`parsedMap: %+v`, parsedMap)
|
||||||
|
|
||||||
|
var mi Mission
|
||||||
|
// create mission object from map[string]interface{}
|
||||||
|
missionBytes, err := json.Marshal(parsedMap)
|
||||||
|
if err != nil {
|
||||||
|
thisLogger.Error().Err(err).Msgf(`Error when marshalling mission info`)
|
||||||
|
return Mission{}, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(missionBytes, &mi)
|
||||||
|
if err != nil {
|
||||||
|
thisLogger.Error().Err(err).Msgf(`Error when unmarshalling mission info`)
|
||||||
|
return Mission{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if loadedWorld == nil {
|
||||||
|
thisLogger.Error().Msgf(`Current world ID not set, cannot create mission`)
|
||||||
|
return Mission{}, err
|
||||||
|
}
|
||||||
|
if loadedWorld.ID == 0 {
|
||||||
|
thisLogger.Error().Msgf(`Current world ID is 0, cannot create mission`)
|
||||||
|
return Mission{}, err
|
||||||
|
}
|
||||||
|
mi.WorldID = loadedWorld.ID
|
||||||
|
|
||||||
// write mission to database
|
// write mission to database
|
||||||
db.Client().Create(&mi)
|
db.Client().Create(&mi)
|
||||||
if db.Client().Error != nil {
|
if db.Client().Error != nil {
|
||||||
logger.Log.Error().Err(db.Client().Error).Msgf(`Error when creating mission`)
|
thisLogger.Error().Err(db.Client().Error).Msgf(`Error when creating mission`)
|
||||||
return
|
return Mission{}, db.Client().Error
|
||||||
}
|
}
|
||||||
logger.Log.Info().Msgf(`Mission %s created with ID %d`, mi.MissionName, mi.ID)
|
thisLogger.Info().Msgf(`Mission %s created with ID %d`, mi.MissionName, mi.ID)
|
||||||
currentMissionID = mi.ID
|
|
||||||
|
a3interface.WriteArmaCallback(
|
||||||
|
EXTENSION_NAME,
|
||||||
|
":LOG:MISSION:SUCCESS:",
|
||||||
|
"World and mission logged successfully.",
|
||||||
|
)
|
||||||
|
|
||||||
|
return mi, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeAttendance(data string) {
|
func writeAttendance(data string, thisLogger zerolog.Logger) {
|
||||||
var err error
|
var err error
|
||||||
// data is json, parse it
|
|
||||||
stringjson := unescapeArmaQuotes(data)
|
parsedInterface, err := a3interface.ParseSQF(data)
|
||||||
var event Session
|
|
||||||
err = json.Unmarshal([]byte(stringjson), &event)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Error().Err(err).Msgf(`Error when unmarshalling attendance`)
|
thisLogger.Error().Err(err).Str("data", data).Msgf(`Error when parsing attendance info`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parsedMap, err := a3interface.ParseSQFHashMap(parsedInterface)
|
||||||
|
if err != nil {
|
||||||
|
thisLogger.Error().Err(err).Str("data", data).Msgf(`Error when parsing attendance info`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
thisLogger.Trace().Msgf(`parsedMap: %+v`, parsedMap)
|
||||||
|
|
||||||
|
var thisSession Session
|
||||||
|
// create session object from map[string]interface{}
|
||||||
|
sessionBytes, err := json.Marshal(parsedMap)
|
||||||
|
if err != nil {
|
||||||
|
thisLogger.Error().Err(err).Str("data", data).Msgf(`Error when marshalling attendance info`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(sessionBytes, &thisSession)
|
||||||
|
if err != nil {
|
||||||
|
thisLogger.Error().Err(err).Str("data", data).Msgf(`Error when unmarshalling attendance info`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
thisLogger2 := thisLogger.With().
|
||||||
|
Str("playerId", thisSession.PlayerId).
|
||||||
|
Str("playerUID", thisSession.PlayerUID).
|
||||||
|
Str("profileName", thisSession.ProfileName).
|
||||||
|
Logger()
|
||||||
|
|
||||||
// search existing event
|
// search existing event
|
||||||
var dbEvent Session
|
var dbEvent Session
|
||||||
|
|
||||||
db.Client().
|
db.Client().
|
||||||
Where(
|
Where(
|
||||||
"player_id = ? AND mission_hash = ?",
|
"player_id = ? AND mission_hash = ?",
|
||||||
event.PlayerId,
|
thisSession.PlayerId,
|
||||||
event.MissionHash,
|
thisSession.MissionHash,
|
||||||
).
|
).
|
||||||
Order("join_time_utc desc").
|
Order("join_time_utc desc").
|
||||||
First(&dbEvent)
|
First(&dbEvent)
|
||||||
if dbEvent.ID != 0 {
|
|
||||||
|
if dbEvent.ID > 0 {
|
||||||
// update disconnect time
|
// update disconnect time
|
||||||
dbEvent.DisconnectTimeUTC = sql.NullTime{
|
dbEvent.DisconnectTimeUTC = sql.NullTime{
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
@@ -378,34 +438,32 @@ func writeAttendance(data string) {
|
|||||||
}
|
}
|
||||||
err = db.Client().Save(&dbEvent).Error
|
err = db.Client().Save(&dbEvent).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Error().Err(err).
|
thisLogger2.Error().Err(err).
|
||||||
Msgf(`Error when updating disconnect time for event %d`, dbEvent.ID)
|
Msgf(`Error when updating disconnect time for event %d`, dbEvent.ID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Log.Debug().Msgf(`Attendance updated for %s (%s)`,
|
thisLogger2.Debug().Msgf(`Attendance updated with ID %d`,
|
||||||
dbEvent.ProfileName,
|
dbEvent.ID,
|
||||||
dbEvent.PlayerUID,
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// insert new row
|
// insert new row
|
||||||
event.JoinTimeUTC = sql.NullTime{
|
thisSession.JoinTimeUTC = sql.NullTime{
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
Valid: true,
|
Valid: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentMissionID == 0 {
|
if loadedMission == nil {
|
||||||
logger.Log.Error().Msgf(`Current mission ID not set, cannot create attendance event`)
|
thisLogger2.Error().Msgf(`Current mission ID not set, cannot create attendance event`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
event.MissionID = currentMissionID
|
thisSession.MissionID = loadedMission.ID
|
||||||
err = db.Client().Create(&event).Error
|
err = db.Client().Create(&thisSession).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Error().Err(err).Msgf(`Error when creating attendance event`)
|
thisLogger2.Error().Err(err).Msgf(`Error when creating attendance event`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Log.Debug().Msgf(`Attendance created for %s (%s)`,
|
thisLogger2.Info().Msgf(`Attendance created with ID %d`,
|
||||||
event.ProfileName,
|
thisSession.ID,
|
||||||
event.PlayerUID,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -416,20 +474,6 @@ func getTimestamp() string {
|
|||||||
return time.Now().Format("2006-01-02 15:04:05")
|
return time.Now().Format("2006-01-02 15:04:05")
|
||||||
}
|
}
|
||||||
|
|
||||||
func trimQuotes(s string) string {
|
|
||||||
// trim the start and end quotes from a string
|
|
||||||
return strings.Trim(s, `"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fixEscapeQuotes(s string) string {
|
|
||||||
// fix the escape quotes in a string
|
|
||||||
return strings.Replace(s, `""`, `"`, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func unescapeArmaQuotes(s string) string {
|
|
||||||
return fixEscapeQuotes(trimQuotes(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// loadConfig()
|
// loadConfig()
|
||||||
// fmt.Println("Running DB connect/migrate to build schema...")
|
// fmt.Println("Running DB connect/migrate to build schema...")
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ require (
|
|||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||||
golang.org/x/sys v0.13.0 // indirect
|
golang.org/x/sys v0.13.0 // indirect
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/text v0.13.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
|||||||
@@ -213,8 +213,12 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|||||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||||
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
@@ -234,6 +238,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
|
|||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||||
|
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
|||||||
@@ -52,9 +52,9 @@ func InitLoggers(o *LoggerOptionsType) {
|
|||||||
ll = &lumberjack.Logger{
|
ll = &lumberjack.Logger{
|
||||||
Filename: ActiveOptions.Path,
|
Filename: ActiveOptions.Path,
|
||||||
MaxSize: 5,
|
MaxSize: 5,
|
||||||
MaxBackups: 10,
|
MaxBackups: 8,
|
||||||
MaxAge: 14,
|
MaxAge: 14,
|
||||||
Compress: true,
|
Compress: false,
|
||||||
LocalTime: true,
|
LocalTime: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ func InitLoggers(o *LoggerOptionsType) {
|
|||||||
armaLogFormatLevel := func(i interface{}) string {
|
armaLogFormatLevel := func(i interface{}) string {
|
||||||
return strings.ToUpper(
|
return strings.ToUpper(
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"(%s)",
|
"%s:",
|
||||||
i,
|
i,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -117,13 +117,17 @@ func InitLoggers(o *LoggerOptionsType) {
|
|||||||
NoColor: true,
|
NoColor: true,
|
||||||
FormatTimestamp: armaLogFormatTimestamp,
|
FormatTimestamp: armaLogFormatTimestamp,
|
||||||
FormatLevel: armaLogFormatLevel,
|
FormatLevel: armaLogFormatLevel,
|
||||||
|
FieldsExclude: []string{zerolog.CallerFieldName, "ctx"},
|
||||||
},
|
},
|
||||||
)).With().Timestamp().Logger()
|
)).With().Timestamp().Caller().Logger()
|
||||||
|
|
||||||
if ActiveOptions.Debug {
|
if ActiveOptions.Debug {
|
||||||
Log = Log.Level(zerolog.DebugLevel)
|
Log = Log.Level(zerolog.DebugLevel)
|
||||||
} else {
|
} else {
|
||||||
Log = Log.Level(zerolog.InfoLevel)
|
Log = Log.Level(zerolog.InfoLevel)
|
||||||
}
|
}
|
||||||
|
if ActiveOptions.Trace {
|
||||||
|
Log = Log.Level(zerolog.TraceLevel)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
15
mariadb/docker-compose.yaml
Normal file
15
mariadb/docker-compose.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
version: '3.1'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: mariadb
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- "MARIADB_ROOT_PASSWORD=example"
|
||||||
|
- "MARIADB_DATABASE=a3attendance"
|
||||||
|
volumes:
|
||||||
|
- ./db:/var/lib/mysql
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
|
||||||
Reference in New Issue
Block a user