diff --git a/.gitignore b/.gitignore index 0c43e33..9ad997a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ hemtt.exe *.bk releases/ +mariadb/db/ +AttendanceTracker.config.json diff --git a/.hemtt/project.toml b/.hemtt/project.toml index 5da5f16..54b1fd9 100644 --- a/.hemtt/project.toml +++ b/.hemtt/project.toml @@ -10,6 +10,7 @@ git_hash=6 # Default: 8 [files] include=[ "AttendanceTracker.config.example.json", + # "AttendanceTracker.config.json", # used for copying active config during debugging "LICENSE", "README", "mod.cpp", diff --git a/AttendanceTracker.config.example.json b/AttendanceTracker.config.example.json index 0a3ef99..e5feb5e 100644 --- a/AttendanceTracker.config.example.json +++ b/AttendanceTracker.config.example.json @@ -3,12 +3,12 @@ "mysqlHost": "localhost", "mysqlPort": 3306, "mysqlUser": "root", - "mysqlPassword": "password", + "mysqlPassword": "example", "mysqlDatabase": "a3attendance" }, "armaConfig": { "dbUpdateInterval": "90s", "debug": false, - "traceLogToFile": false + "trace": false } } \ No newline at end of file diff --git a/README.md b/README.md index 500a814..f109f15 100644 --- a/README.md +++ b/README.md @@ -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. | | 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 @@ -166,7 +166,7 @@ docker pull x1unix/go-mingw:1.20 # version is semantic + build date + git hash # e.g. 1.0.0-2021-05-30-1a2b3c4d -$versionSem = '1.0.1' +$versionSem = '1.1.1' $dateStr = Get-Date -Format 'yyyyMMdd' $version = "$versionSem-$dateStr-$(git rev-parse --short HEAD)" diff --git a/addons/main/config.cpp b/addons/main/config.cpp index adf2bde..d7e4881 100644 --- a/addons/main/config.cpp +++ b/addons/main/config.cpp @@ -1,7 +1,7 @@ -#include "script_mod.hpp" +#include "script_component.hpp" class CfgPatches { - class AttendanceTracker { + class ADDON { units[] = {}; weapons[] = {}; requiredVersion = 2.10; @@ -17,20 +17,17 @@ class CfgPatches { }; class CfgFunctions { - class attendanceTracker { + class ADDON { class functions { - file = "x\addons\attendancetracker\main\functions"; - class postInit {postInit = 1;}; - class callbackHandler {postInit = 1;}; - class getMissionHash {}; - class getMissionInfo {}; - class getSettings {}; - class getWorldInfo {}; - class log {}; - class missionLoaded {}; - class onPlayerConnected {}; - class timestamp {}; - class writePlayer {}; + class postInit { + file = QPATHTOF(DOUBLES(fnc,postInit).sqf); + postInit = 1; + }; + PATHTO_FNC(getMissionInfo); + PATHTO_FNC(getWorldInfo); + PATHTO_FNC(log); + PATHTO_FNC(missionLoaded); + PATHTO_FNC(onPlayerConnected); }; }; }; \ No newline at end of file diff --git a/addons/main/fnc_getMissionInfo.sqf b/addons/main/fnc_getMissionInfo.sqf new file mode 100644 index 0000000..42ff4bc --- /dev/null +++ b/addons/main/fnc_getMissionInfo.sqf @@ -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] +]; \ No newline at end of file diff --git a/addons/main/functions/fn_getWorldInfo.sqf b/addons/main/fnc_getWorldInfo.sqf similarity index 78% rename from addons/main/functions/fn_getWorldInfo.sqf rename to addons/main/fnc_getWorldInfo.sqf index 3f7220b..5198be1 100644 --- a/addons/main/functions/fn_getWorldInfo.sqf +++ b/addons/main/fnc_getWorldInfo.sqf @@ -1,3 +1,5 @@ +#include "script_component.hpp" + _world = ( configfile >> "CfgWorlds" >> worldName ); _author = getText( _world >> "author" ); _name = getText ( _world >> "description" ); @@ -5,7 +7,6 @@ _name = getText ( _world >> "description" ); _source = configSourceMod ( _world ); _workshopID = ''; - { if ( ( _x#1 ) == _source ) then { _workshopID = _x#7; @@ -13,8 +14,12 @@ _workshopID = ''; }; } foreach getLoadedModsInfo; +if (_workshopID isEqualTo "") then { + _workshopID = "0"; +}; + // [_name, _author, _workshopID]; -_return = createHashMapFromArray [ +_return = [ ["author", _author], ["workshopID", _workshopID], ["displayName", _name], @@ -24,5 +29,5 @@ _return = createHashMapFromArray [ ["latitude", -1 * getNumber( _world >> "latitude" )], ["longitude", getNumber( _world >> "longitude" )] ]; -[format["WorldInfo is: %1", _return]] call attendanceTracker_fnc_log; +["DEBUG", format["WorldInfo is: %1", _return]] call FUNC(log); _return diff --git a/addons/main/fnc_log.sqf b/addons/main/fnc_log.sqf new file mode 100644 index 0000000..0bf5224 --- /dev/null +++ b/addons/main/fnc_log.sqf @@ -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 +]; + diff --git a/addons/main/functions/fn_missionLoaded.sqf b/addons/main/fnc_missionLoaded.sqf similarity index 64% rename from addons/main/functions/fn_missionLoaded.sqf rename to addons/main/fnc_missionLoaded.sqf index b526f86..44c4305 100644 --- a/addons/main/functions/fn_missionLoaded.sqf +++ b/addons/main/fnc_missionLoaded.sqf @@ -1 +1,3 @@ +#include "script_component.hpp" + !(getClientStateNumber <= 5 || getClientStateNumber isEqualTo 11); \ No newline at end of file diff --git a/addons/main/fnc_onPlayerConnected.sqf b/addons/main/fnc_onPlayerConnected.sqf new file mode 100644 index 0000000..649ab11 --- /dev/null +++ b/addons/main/fnc_onPlayerConnected.sqf @@ -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; \ No newline at end of file diff --git a/addons/main/fnc_postInit.sqf b/addons/main/fnc_postInit.sqf new file mode 100644 index 0000000..683e7d7 --- /dev/null +++ b/addons/main/fnc_postInit.sqf @@ -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]; +}]; \ No newline at end of file diff --git a/addons/main/functions/fn_callbackHandler.sqf b/addons/main/functions/fn_callbackHandler.sqf deleted file mode 100644 index 3946037..0000000 --- a/addons/main/functions/fn_callbackHandler.sqf +++ /dev/null @@ -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; -}]; \ No newline at end of file diff --git a/addons/main/functions/fn_getMissionHash.sqf b/addons/main/functions/fn_getMissionHash.sqf deleted file mode 100644 index afc2a79..0000000 --- a/addons/main/functions/fn_getMissionHash.sqf +++ /dev/null @@ -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:"; \ No newline at end of file diff --git a/addons/main/functions/fn_getMissionInfo.sqf b/addons/main/functions/fn_getMissionInfo.sqf deleted file mode 100644 index 6e8cb0f..0000000 --- a/addons/main/functions/fn_getMissionInfo.sqf +++ /dev/null @@ -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] -]; \ No newline at end of file diff --git a/addons/main/functions/fn_getSettings.sqf b/addons/main/functions/fn_getSettings.sqf deleted file mode 100644 index 3f11cdf..0000000 --- a/addons/main/functions/fn_getSettings.sqf +++ /dev/null @@ -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:"; \ No newline at end of file diff --git a/addons/main/functions/fn_log.sqf b/addons/main/functions/fn_log.sqf deleted file mode 100644 index a226f04..0000000 --- a/addons/main/functions/fn_log.sqf +++ /dev/null @@ -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; \ No newline at end of file diff --git a/addons/main/functions/fn_onPlayerConnected.sqf b/addons/main/functions/fn_onPlayerConnected.sqf deleted file mode 100644 index 1c179f7..0000000 --- a/addons/main/functions/fn_onPlayerConnected.sqf +++ /dev/null @@ -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; \ No newline at end of file diff --git a/addons/main/functions/fn_postInit.sqf b/addons/main/functions/fn_postInit.sqf deleted file mode 100644 index 5ea3a5d..0000000 --- a/addons/main/functions/fn_postInit.sqf +++ /dev/null @@ -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 - ]; -}]; \ No newline at end of file diff --git a/addons/main/functions/fn_timestamp.sqf b/addons/main/functions/fn_timestamp.sqf deleted file mode 100644 index 2881080..0000000 --- a/addons/main/functions/fn_timestamp.sqf +++ /dev/null @@ -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 -]; - diff --git a/addons/main/functions/fn_writePlayer.sqf b/addons/main/functions/fn_writePlayer.sqf deleted file mode 100644 index 62d0994..0000000 --- a/addons/main/functions/fn_writePlayer.sqf +++ /dev/null @@ -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; \ No newline at end of file diff --git a/addons/main/script_component.hpp b/addons/main/script_component.hpp new file mode 100644 index 0000000..3fdee14 --- /dev/null +++ b/addons/main/script_component.hpp @@ -0,0 +1,4 @@ +#define COMPONENT main +#define COMPONENT_BEAUTIFIED Main + +#include "\x\attendancetracker\addons\main\script_mod.hpp" \ No newline at end of file diff --git a/addons/main/script_mod.hpp b/addons/main/script_mod.hpp index c34d125..e4c2f36 100644 --- a/addons/main/script_mod.hpp +++ b/addons/main/script_mod.hpp @@ -1,12 +1,8 @@ #include "script_version.hpp" -#define COMPONENT main -#define COMPONENT_BEAUTIFIED Main - #define MAINPREFIX x +#define PREFIX attendancetracker +#define PREFIX_BEAUTIFIED AttendanceTracker #define SUBPREFIX addons -#define PREFIX AttendanceTracker - - #include "\x\cba\addons\main\script_macros_common.hpp" \ No newline at end of file diff --git a/addons/main/script_version.hpp b/addons/main/script_version.hpp index 298af03..a99229e 100644 --- a/addons/main/script_version.hpp +++ b/addons/main/script_version.hpp @@ -1,7 +1,7 @@ #define MAJOR 1 #define MINOR 1 #define PATCH 0 -#define BUILD 20231003 +#define BUILD 20231012 #define VERSION 1.1 #define VERSION_STR MAJOR##.##MINOR##.##PATCH##.##BUILD diff --git a/extension/AttendanceTracker/cmd/main.go b/extension/AttendanceTracker/cmd/main.go index dc9b77c..132c8af 100644 --- a/extension/AttendanceTracker/cmd/main.go +++ b/extension/AttendanceTracker/cmd/main.go @@ -13,7 +13,6 @@ import ( "encoding/json" "fmt" "path/filepath" - "strings" "time" _ "github.com/go-sql-driver/mysql" @@ -22,35 +21,25 @@ import ( "github.com/indig0fox/Arma3-AttendanceTracker/internal/util" "github.com/indig0fox/a3go/a3interface" "github.com/indig0fox/a3go/assemblyfinder" + "github.com/rs/zerolog" ) const EXTENSION_NAME string = "AttendanceTracker" const ADDON_NAME string = "AttendanceTracker" -const EXTENSION_VERSION string = "dev" // file paths const ATTENDANCE_TABLE string = "attendance" const MISSIONS_TABLE string = "missions" 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 ( + EXTENSION_VERSION string = "DEVELOPMENT" + modulePath string modulePathDir string - initSuccess bool // default false + loadedMission *Mission + loadedWorld *World ) // configure log output @@ -58,31 +47,28 @@ func init() { a3interface.SetVersion(EXTENSION_VERSION) a3interface.NewRegistration(":START:"). - SetDefaultResponse(`["Extension beginning init process"]`). SetFunction(onStartCommand). - SetRunInBackground(true). + SetRunInBackground(false). Register() a3interface.NewRegistration(":MISSION:HASH:"). - SetDefaultResponse(`["Retrieving mission hash"]`). SetFunction(onMissionHashCommand). - SetRunInBackground(true). + SetRunInBackground(false). Register() a3interface.NewRegistration(":GET:SETTINGS:"). - SetDefaultResponse(`["Retrieving settings"]`). SetFunction(onGetSettingsCommand). - SetRunInBackground(true). + SetRunInBackground(false). Register() a3interface.NewRegistration(":LOG:MISSION:"). - SetDefaultResponse(`["Logging mission data"]`). + SetDefaultResponse(`Logging mission data`). SetArgsFunction(onLogMissionArgsCommand). SetRunInBackground(true). Register() a3interface.NewRegistration(":LOG:PRESENCE:"). - SetDefaultResponse(`["Logging presence data"]`). + SetDefaultResponse(`Logging presence data`). SetArgsFunction(onLogPresenceArgsCommand). SetRunInBackground(true). Register() @@ -91,12 +77,7 @@ func init() { var err error modulePath = assemblyfinder.GetModulePath() - // get absolute path of module path - modulePathAbs, err := filepath.Abs(modulePath) - if err != nil { - panic(err) - } - modulePathDir = filepath.Dir(modulePathAbs) + modulePathDir = filepath.Dir(modulePath) result, configErr := util.LoadConfig(modulePathDir) logger.InitLoggers(&logger.LoggerOptionsType{ @@ -107,11 +88,13 @@ func init() { EXTENSION_NAME, EXTENSION_VERSION, )), - AddonName: ADDON_NAME, - ExtensionName: EXTENSION_NAME, - Debug: util.ConfigJSON.GetBool("armaConfig.debug"), - Trace: util.ConfigJSON.GetBool("armaConfig.traceLogToFile"), + AddonName: ADDON_NAME, + ExtensionName: EXTENSION_NAME, + ExtensionVersion: EXTENSION_VERSION, + Debug: util.ConfigJSON.GetBool("armaConfig.debug"), + Trace: util.ConfigJSON.GetBool("armaConfig.trace"), }) + logger.RotateLogs() if configErr != nil { logger.Log.Error().Err(configErr).Msgf(`Error loading config`) return @@ -119,9 +102,7 @@ func init() { logger.Log.Info().Msgf(result) } - logger.RotateLogs() - - logger.ArmaOnly.Info().Msgf(`%s v%s started`, EXTENSION_NAME, "0.0.0") + logger.Log.Info().Msgf(`%s v%s started`, EXTENSION_NAME, EXTENSION_VERSION) logger.ArmaOnly.Info().Msgf(`Log path: %s`, logger.ActiveOptions.Path) db.SetConfig(db.ConfigStruct{ @@ -151,9 +132,10 @@ func init() { ) if err != nil { logger.Log.Error().Err(err).Msgf(`Error migrating database schema`) + } else { + logger.Log.Info().Msgf(`Database schema migrated`) } - initSuccess = true a3interface.WriteArmaCallback( EXTENSION_NAME, ":READY:", @@ -167,32 +149,22 @@ func onStartCommand( ctx a3interface.ArmaExtensionContext, data string, ) (string, error) { - logger.Log.Trace().Msgf(`RVExtension :START: requested`) - if !initSuccess { - logger.Log.Warn().Msgf(`Received another :START: command before init was complete, ignoring.`) - return "Initing!", nil - } else { - logger.RotateLogs() - a3interface.WriteArmaCallback( - EXTENSION_NAME, - ":READY:", - ) - return "Ready!", nil - } + logger.Log.Debug().Msgf(`RVExtension :START: requested`) + loadedWorld = nil + loadedMission = nil + return fmt.Sprintf( + `["%s v%s started"]`, + EXTENSION_NAME, + EXTENSION_VERSION, + ), nil } func onMissionHashCommand( ctx a3interface.ArmaExtensionContext, data string, ) (string, error) { - logger.Log.Trace().Msgf(`RVExtension :MISSION:HASH: requested`) + logger.Log.Debug().Msgf(`RVExtension :MISSION:HASH: requested`) timestamp, hash := getMissionHash() - a3interface.WriteArmaCallback( - EXTENSION_NAME, - ":MISSION:HASH:", - timestamp, - hash, - ) return fmt.Sprintf( `[%q, %q]`, timestamp, @@ -204,19 +176,14 @@ func onGetSettingsCommand( ctx a3interface.ArmaExtensionContext, data string, ) (string, error) { - logger.Log.Trace().Msg(`Settings requested`) - armaConfig, err := util.ConfigArmaFormat() - if err != nil { - logger.Log.Error().Err(err).Msg(`Error when marshaling arma config`) - return "", err - } - logger.Log.Trace().Str("armaConfig", armaConfig).Send() - a3interface.WriteArmaCallback( - EXTENSION_NAME, - ":GET:SETTINGS:", + logger.Log.Debug().Msg(`RVExtension :GET:SETTINGS: requested`) + // get arma config + c := util.ConfigJSON.Get("armaConfig") + armaConfig := a3interface.ToArmaHashMap(c) + return fmt.Sprintf( + `[%s]`, armaConfig, - ) - return armaConfig, nil + ), nil } func onLogMissionArgsCommand( @@ -224,12 +191,27 @@ func onLogMissionArgsCommand( command string, args []string, ) (string, error) { - go func(data []string) { - writeWorldInfo(data[1]) - writeMission(data[0]) - }(args) + thisLogger := logger.Log.With().Str("command", command).Interface("ctx", ctx).Logger() + thisLogger.Debug().Msgf(`RVExtension :LOG:MISSION: requested`) + var err error + 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( @@ -237,8 +219,10 @@ func onLogPresenceArgsCommand( command string, args []string, ) (string, error) { - go writeAttendance(args[0]) - return `["Logging presence data"]`, nil + thisLogger := logger.Log.With().Str("command", command).Interface("ctx", ctx).Logger() + 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 @@ -248,7 +232,7 @@ func getMissionHash() (sqlTime, hashString string) { nowTime := time.Now().UTC() // mysql format - sqlTime = nowTime.Format("2006-01-02 15:04:05") + sqlTime = nowTime.Format(time.RFC3339) hash := md5.Sum([]byte(sqlTime)) hashString = fmt.Sprintf(`%x`, hash) @@ -291,86 +275,162 @@ func finalizeUnendedSessions() { logger.Log.Info().Msgf(`Filled disconnect time of %d events.`, len(events)) } -func writeWorldInfo(worldInfo string) { - // worldInfo is json, parse it - var wi World - fixedString := unescapeArmaQuotes(worldInfo) - err := json.Unmarshal([]byte(fixedString), &wi) +func writeWorldInfo(worldInfo string, thisLogger zerolog.Logger) (World, error) { + + parsedInterface, err := a3interface.ParseSQF(worldInfo) if err != nil { - logger.Log.Error().Err(err).Msgf(`Error when unmarshalling world info`) - return + thisLogger.Error().Err(err).Msgf(`Error when parsing world info`) + 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 - var dbWorld World - db.Client().Where("world_name = ?", wi.WorldName).First(&dbWorld) - if dbWorld.ID == 0 { - db.Client().Create(&wi) - 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) + db.Client().Create(&wi) + if db.Client().Error != nil { + thisLogger.Error().Err(db.Client().Error).Msgf(`Error when creating world`) + return World{}, db.Client().Error } + 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 - // writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, Mission)) - // Mission is json, parse it - var mi Mission - fixedString := fixEscapeQuotes(trimQuotes(missionJSON)) - err = json.Unmarshal([]byte(fixedString), &mi) + parsedInterface, err := a3interface.ParseSQF(data) if err != nil { - logger.Log.Error().Err(err).Msgf(`Error when unmarshalling mission`) - return + thisLogger.Error().Err(err).Msgf(`Error when parsing mission info`) + return Mission{}, err } - // get world from WorldName - var dbWorld World - db.Client().Where("world_name = ?", mi.WorldName).First(&dbWorld) - if dbWorld.ID == 0 { - logger.Log.Error().Msgf(`World %s not found.`, mi.WorldName) - return + parsedMap, err := a3interface.ParseSQFHashMap(parsedInterface) + if err != nil { + thisLogger.Error().Err(err).Msgf(`Error when parsing mission info`) + return Mission{}, err } - 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 db.Client().Create(&mi) if db.Client().Error != nil { - logger.Log.Error().Err(db.Client().Error).Msgf(`Error when creating mission`) - return + thisLogger.Error().Err(db.Client().Error).Msgf(`Error when creating mission`) + return Mission{}, db.Client().Error } - logger.Log.Info().Msgf(`Mission %s created with ID %d`, mi.MissionName, mi.ID) - currentMissionID = mi.ID + thisLogger.Info().Msgf(`Mission %s created with ID %d`, mi.MissionName, 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 - // data is json, parse it - stringjson := unescapeArmaQuotes(data) - var event Session - err = json.Unmarshal([]byte(stringjson), &event) + + parsedInterface, err := a3interface.ParseSQF(data) 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 } + 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 var dbEvent Session + db.Client(). Where( "player_id = ? AND mission_hash = ?", - event.PlayerId, - event.MissionHash, + thisSession.PlayerId, + thisSession.MissionHash, ). Order("join_time_utc desc"). First(&dbEvent) - if dbEvent.ID != 0 { + + if dbEvent.ID > 0 { // update disconnect time dbEvent.DisconnectTimeUTC = sql.NullTime{ Time: time.Now(), @@ -378,34 +438,32 @@ func writeAttendance(data string) { } err = db.Client().Save(&dbEvent).Error if err != nil { - logger.Log.Error().Err(err). + thisLogger2.Error().Err(err). Msgf(`Error when updating disconnect time for event %d`, dbEvent.ID) return } - logger.Log.Debug().Msgf(`Attendance updated for %s (%s)`, - dbEvent.ProfileName, - dbEvent.PlayerUID, + thisLogger2.Debug().Msgf(`Attendance updated with ID %d`, + dbEvent.ID, ) } else { // insert new row - event.JoinTimeUTC = sql.NullTime{ + thisSession.JoinTimeUTC = sql.NullTime{ Time: time.Now(), Valid: true, } - if currentMissionID == 0 { - logger.Log.Error().Msgf(`Current mission ID not set, cannot create attendance event`) + if loadedMission == nil { + thisLogger2.Error().Msgf(`Current mission ID not set, cannot create attendance event`) return } - event.MissionID = currentMissionID - err = db.Client().Create(&event).Error + thisSession.MissionID = loadedMission.ID + err = db.Client().Create(&thisSession).Error 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 } - logger.Log.Debug().Msgf(`Attendance created for %s (%s)`, - event.ProfileName, - event.PlayerUID, + thisLogger2.Info().Msgf(`Attendance created with ID %d`, + thisSession.ID, ) } } @@ -416,20 +474,6 @@ func getTimestamp() string { 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() { // loadConfig() // fmt.Println("Running DB connect/migrate to build schema...") diff --git a/extension/AttendanceTracker/go.mod b/extension/AttendanceTracker/go.mod index 2ca5ea8..4be2dfd 100644 --- a/extension/AttendanceTracker/go.mod +++ b/extension/AttendanceTracker/go.mod @@ -30,9 +30,9 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.9.0 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/extension/AttendanceTracker/go.sum b/extension/AttendanceTracker/go.sum index 0c1a890..5822704 100644 --- a/extension/AttendanceTracker/go.sum +++ b/extension/AttendanceTracker/go.sum @@ -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.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.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/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-20190510104115-cbcb75029529/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-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-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-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/extension/AttendanceTracker/internal/logger/logger.go b/extension/AttendanceTracker/internal/logger/logger.go index 6231a1a..b006f38 100644 --- a/extension/AttendanceTracker/internal/logger/logger.go +++ b/extension/AttendanceTracker/internal/logger/logger.go @@ -52,9 +52,9 @@ func InitLoggers(o *LoggerOptionsType) { ll = &lumberjack.Logger{ Filename: ActiveOptions.Path, MaxSize: 5, - MaxBackups: 10, + MaxBackups: 8, MaxAge: 14, - Compress: true, + Compress: false, LocalTime: true, } @@ -66,7 +66,7 @@ func InitLoggers(o *LoggerOptionsType) { armaLogFormatLevel := func(i interface{}) string { return strings.ToUpper( fmt.Sprintf( - "(%s)", + "%s:", i, )) } @@ -117,13 +117,17 @@ func InitLoggers(o *LoggerOptionsType) { NoColor: true, FormatTimestamp: armaLogFormatTimestamp, FormatLevel: armaLogFormatLevel, + FieldsExclude: []string{zerolog.CallerFieldName, "ctx"}, }, - )).With().Timestamp().Logger() + )).With().Timestamp().Caller().Logger() if ActiveOptions.Debug { Log = Log.Level(zerolog.DebugLevel) } else { Log = Log.Level(zerolog.InfoLevel) } + if ActiveOptions.Trace { + Log = Log.Level(zerolog.TraceLevel) + } } diff --git a/go.work b/go.work new file mode 100644 index 0000000..b4f9585 --- /dev/null +++ b/go.work @@ -0,0 +1,3 @@ +go 1.20 + +use ./extension/AttendanceTracker diff --git a/mariadb/docker-compose.yaml b/mariadb/docker-compose.yaml new file mode 100644 index 0000000..bc5575e --- /dev/null +++ b/mariadb/docker-compose.yaml @@ -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" + \ No newline at end of file