diff --git a/@17thAttendanceTracker/addons/AttendanceTracker.pbo b/@17thAttendanceTracker/addons/AttendanceTracker.pbo index 0b0a605..ec8fe03 100644 Binary files a/@17thAttendanceTracker/addons/AttendanceTracker.pbo and b/@17thAttendanceTracker/addons/AttendanceTracker.pbo differ diff --git a/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_callbackHandler.sqf b/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_callbackHandler.sqf index b5a0223..7404ded 100644 --- a/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_callbackHandler.sqf +++ b/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_callbackHandler.sqf @@ -55,6 +55,12 @@ addMissionEventHandler ["ExtensionCallback", { ]]; }; }; + case "writeAttendance": { + if (_response#0 == "ATT_LOG") then { + _response params ["_netId", "_rowId"]; + ((AttendanceTracker getVariable ["allUsers", createHashMap]) get _netId) set ["_rowID", _rowID]; + }; + }; default { _response call attendanceTracker_fnc_log; }; diff --git a/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_eventHandlers.sqf b/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_eventHandlers.sqf index ce1d0c6..fe610f9 100644 --- a/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_eventHandlers.sqf +++ b/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_eventHandlers.sqf @@ -8,15 +8,16 @@ _userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"]; if (_isHC) exitWith {}; + (AttendanceTracker getVariable ["allUsers", createHashMap]) set [_networkId, _userInfo]; [ - "ConnectedServer", + "Server", _playerID, _playerUID, _profileName, - _steamName - ] call attendanceTracker_fnc_logServerEvent; + _steamName, + nil // send rowId on d/c only + ] call attendanceTracker_fnc_writeAttendance; - (AttendanceTracker getVariable ["allUsers", createHashMap]) set [_networkId, _userInfo]; }], ["OnUserDisconnected", { params ["_networkId", "_clientStateNumber", "_clientState"]; @@ -26,16 +27,17 @@ private _userInfo = (AttendanceTracker getVariable ["allUsers", createHashMap]) get _networkId; if (isNil "_userInfo") exitWith {}; - _userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"]; + _userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit", "_rowId"]; if (_isHC) exitWith {}; [ - "DisconnectedServer", + "Server", _playerID, _playerUID, _profileName, - _steamName - ] call attendanceTracker_fnc_logServerEvent; + _steamName, + (if (!isNil "_rowId") then {_rowId} else {nil}) // send rowId on d/c only + ] call attendanceTracker_fnc_writeAttendance; }], ["PlayerConnected", { params ["_id", "_uid", "_name", "_jip", "_owner", "_idstr"]; @@ -46,19 +48,18 @@ if (isNil "_userInfo") exitWith {}; _userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"]; + if (_isHC) exitWith {}; (AttendanceTracker getVariable ["allUsers", createHashMap]) set [_playerID, _userInfo]; - - if (_isHC) exitWith {}; - [ - "ConnectedMission", + "Mission", _playerID, _playerUID, _profileName, _steamName, _jip, - roleDescription _unit + roleDescription _unit, + nil // send rowId on d/c only ] call attendanceTracker_fnc_logMissionEvent; }], ["PlayerDisconnected", { @@ -72,17 +73,18 @@ [format ["(EventHandler) HandleDisconnect: No user info found for %1", _idstr], "DEBUG"] call attendanceTracker_fnc_log; }; - _userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit"]; - + _userInfo params ["_playerID", "_ownerId", "_playerUID", "_profileName", "_displayName", "_steamName", "_clientState", "_isHC", "_adminState", "_networkInfo", "_unit", "_rowId"]; if (_isHC) exitWith {}; [ - "DisconnectedMission", + "Mission", _playerID, _playerUID, _profileName, _steamName, - _jip + _jip, + nil, + (if (!isNil "_rowId") then {_rowId} else {nil}) // send rowId on d/c only ] call attendanceTracker_fnc_logMissionEvent; diff --git a/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_logMissionEvent.sqf b/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_logMissionEvent.sqf index be3c8ce..5684c44 100644 --- a/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_logMissionEvent.sqf +++ b/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_logMissionEvent.sqf @@ -5,7 +5,8 @@ params [ ["_profileName", ""], ["_steamName", ""], ["_isJIP", false, [true, false]], - ["_roleDescription", ""] + ["_roleDescription", ""], + ["_rowID", nil] ]; private _hash = + (AttendanceTracker getVariable ["missionContext", createHashMap]); @@ -18,6 +19,11 @@ _hash set ["isJIP", _isJIP]; _hash set ["roleDescription", _roleDescription]; _hash set ["missionHash", missionNamespace getVariable ["AttendanceTracker_missionHash", ""]]; -"AttendanceTracker" callExtension ["logAttendance", [[_hash] call CBA_fnc_encodeJSON]]; +if (!isNil "_rowID") then { + _hash set ["rowID", _rowID]; + "AttendanceTracker" callExtension ["writeDisconnectEvent", [[_hash] call CBA_fnc_encodeJSON]]; +} else { + "AttendanceTracker" callExtension ["writeAttendance", [[_hash] call CBA_fnc_encodeJSON]]; +}; true; \ No newline at end of file diff --git a/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_logServerEvent.sqf b/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_logServerEvent.sqf index 613ecce..2d72a74 100644 --- a/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_logServerEvent.sqf +++ b/@17thAttendanceTracker/addons/AttendanceTracker/functions/fn_logServerEvent.sqf @@ -3,11 +3,13 @@ params [ ["_playerId", ""], ["_playerUID", ""], ["_profileName", ""], - ["_steamName", ""] + ["_steamName", ""], + ["_rowID", nil] ]; private _hash = + (AttendanceTracker getVariable ["missionContext", createHashMap]); +_hash set ["networkId", netID player] _hash set ["eventType", _eventType]; _hash set ["playerId", _playerId]; _hash set ["playerUID", _playerUID]; @@ -17,6 +19,11 @@ _hash set ["isJIP", false]; _hash set ["roleDescription", ""]; _hash set ["missionHash", missionNamespace getVariable ["AttendanceTracker_missionHash", ""]]; -"AttendanceTracker" callExtension ["logAttendance", [[_hash] call CBA_fnc_encodeJSON]]; +if (!isNil "_rowID") then { + _hash set ["rowID", _rowID]; + "AttendanceTracker" callExtension ["writeDisconnectEvent", [[_hash] call CBA_fnc_encodeJSON]]; +} else { + "AttendanceTracker" callExtension ["writeAttendance", [[_hash] call CBA_fnc_encodeJSON]]; +}; true; \ No newline at end of file diff --git a/README.md b/README.md index 183512a..0cb1612 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ CREATE TABLE `attendancelog` ( `server_name` varchar(100) NOT NULL, `server_profile` varchar(100) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2713 DEFAULT CHARSET=utf8mb3; +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb3; -- a3server.`missions` definition diff --git a/extension/AttendanceTracker_x64.dll b/extension/AttendanceTracker_x64.dll index 9169ea6..586b556 100644 Binary files a/extension/AttendanceTracker_x64.dll and b/extension/AttendanceTracker_x64.dll differ diff --git a/extension/main.go b/extension/main.go index 1d62a69..f75d166 100644 --- a/extension/main.go +++ b/extension/main.go @@ -32,6 +32,10 @@ var ADDON_FOLDER string = getDir() + "\\@AttendanceTracker" var LOG_FILE string = ADDON_FOLDER + "\\attendanceTracker.log" var CONFIG_FILE string = ADDON_FOLDER + "\\config.json" +var ATTENDANCE_TABLE string = "attendance" +var MISSIONS_TABLE string = "missions" +var WORLDS_TABLE string = "worlds" + var ATConfig AttendanceTrackerConfig type AttendanceTrackerConfig struct { @@ -189,7 +193,10 @@ func writeWorldInfo(worldInfo string) { if err != nil { if err == sql.ErrNoRows { // world does not exist, insert it - stmt, err := db.Prepare("INSERT INTO worlds (author, workshop_id, display_name, world_name, world_name_original, world_size, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?, ?, ?)") + stmt, err := db.Prepare(fmt.Sprintf( + "INSERT INTO %s (author, workshop_id, display_name, world_name, world_name_original, world_size, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + WORLDS_TABLE, + )) if err != nil { writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err)) return @@ -212,7 +219,10 @@ func writeWorldInfo(worldInfo string) { } } else { // world exists, update it - stmt, err := db.Prepare("UPDATE worlds SET author = ?, workshop_id = ?, display_name = ?, world_name = ?, world_name_original = ?, world_size = ?, latitude = ?, longitude = ? WHERE id = ?") + stmt, err := db.Prepare(fmt.Sprintf( + "UPDATE %s SET author = ?, workshop_id = ?, display_name = ?, world_name = ?, world_name_original = ?, world_size = ?, latitude = ?, longitude = ? WHERE id = ?", + WORLDS_TABLE, + )) if err != nil { writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err)) return @@ -267,7 +277,10 @@ func writeMissionInfo(missionInfo string) { // write to database // every mission is unique, so insert it - stmt, err := db.Prepare("INSERT INTO missions (mission_name, briefing_name, mission_name_source, on_load_name, author, server_name, server_profile, mission_start, mission_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)") + stmt, err := db.Prepare(fmt.Sprintf( + "INSERT INTO %s (mission_name, briefing_name, mission_name_source, on_load_name, author, server_name, server_profile, mission_start, mission_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", + MISSIONS_TABLE, + )) if err != nil { writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err)) return @@ -295,6 +308,8 @@ type AttendanceLogItem struct { IsJIP bool `json:"isJIP"` RoleDescription string `json:"roleDescription"` MissionHash string `json:"missionHash"` + // + RowID int64 `json:"rowID"` // optional } func writeAttendance(data string) { @@ -317,9 +332,15 @@ func writeAttendance(data string) { return } + // // send to DB - result, err := db.ExecContext(context.Background(), `INSERT INTO AttendanceLog (event_time, event_type, player_id, player_uid, profile_name, steam_name, is_jip, role_description, mission_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, + result, err := db.ExecContext( + context.Background(), + fmt.Sprintf( + `INSERT INTO %s (join_time, disconnect_time, event_type, player_id, player_uid, profile_name, steam_name, is_jip, role_description, mission_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, + ATTENDANCE_TABLE, + ), now, event.EventType, event.PlayerId, @@ -343,9 +364,62 @@ func writeAttendance(data string) { } writeLog(functionName, fmt.Sprintf(`["Saved attendance for %s to row id %d", "INFO"]`, event.ProfileName, id)) + writeLog(functionName, fmt.Sprintf(`["ATT_LOG", ["%s", %d]]`, event.PlayerId, id)) } +type DisconnectItem struct { + PlayerId string `json:"playerId"` + RowId int64 `json:"rowId"` +} + +func writeDisconnectEvent(data string) { + functionName := "writeDisconnectEvent" + // data is json, parse it + stringjson := fixEscapeQuotes(trimQuotes(data)) + var event DisconnectItem + err := json.Unmarshal([]byte(stringjson), &event) + if err != nil { + writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err)) + return + } + + // get MySQL friendly NOW + now := time.Now().Format("2006-01-02 15:04:05") + + // prevent crash + if db == nil { + writeLog(functionName, `["db is nil", "ERROR"]`) + return + } + + // send to DB + result, err := db.ExecContext( + context.Background(), + fmt.Sprintf( + `UPDATE %s SET disconnect_time = ? WHERE id = ?`, + ATTENDANCE_TABLE, + ), + now, + event.RowId, + ) + + if err != nil { + writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err)) + return + } + + rowsAffected, err := result.RowsAffected() + if err != nil { + writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err)) + return + } + + if rowsAffected == 1 { + writeLog(functionName, fmt.Sprintf(`["Saved disconnect event for %s to row id %d", "INFO"]`, event.PlayerId, event.RowId)) + } +} + func runExtensionCallback(name *C.char, function *C.char, data *C.char) C.int { return C.runExtensionCallback(extensionCallbackFnc, name, function, data) } @@ -374,12 +448,19 @@ func goRVExtensionArgs(output *C.char, outputsize C.size_t, input *C.char, argv temp := fmt.Sprintf("Function: %s nb params: %d", C.GoString(input), argc) switch C.GoString(input) { - case "logAttendance": - { // callExtension ["serverEvent", [_hash] call CBA_fnc_encodeJSON]; + case "writeAttendance": + { // callExtension ["logAttendance", [_hash] call CBA_fnc_encodeJSON]]; if argc == 1 { go writeAttendance(out[0]) } } + case "writeDisconnectEvent": + { // callExtension ["writeDisconnectEvent", [[_hash] call CBA_fnc_encodeJSON]]; + + if argc == 1 { + go writeDisconnectEvent(out[0]) + } + } case "logMission": if argc == 1 { go writeMissionInfo(out[0])