mirror of
https://github.com/indig0fox/Arma3-AttendanceTracker.git/
synced 2025-12-08 09:51:47 -06:00
implement GORM, expand config, modify the way sessions are handled
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -21,6 +21,7 @@ class CfgFunctions {
|
|||||||
class writeConnect {};
|
class writeConnect {};
|
||||||
class writeDisconnect {};
|
class writeDisconnect {};
|
||||||
class timestamp {};
|
class timestamp {};
|
||||||
|
class getSettings {};
|
||||||
class getMissionHash {};
|
class getMissionHash {};
|
||||||
class getWorldInfo {};
|
class getWorldInfo {};
|
||||||
class missionLoaded {};
|
class missionLoaded {};
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ addMissionEventHandler ["ExtensionCallback", {
|
|||||||
false;
|
false;
|
||||||
};
|
};
|
||||||
|
|
||||||
diag_log format ["Raw callback: %1: %2", _function, _data];
|
if (missionNamespace getVariable ["AttendanceTracker_" + "debug", true]) then {
|
||||||
|
diag_log format ["Raw callback: %1: %2", _function, _data];
|
||||||
|
};
|
||||||
|
|
||||||
// Parse response from string array
|
// Parse response from string array
|
||||||
private "_response";
|
private "_response";
|
||||||
@@ -44,12 +46,7 @@ addMissionEventHandler ["ExtensionCallback", {
|
|||||||
case "connectDB": {
|
case "connectDB": {
|
||||||
systemChat format ["AttendanceTracker: %1", _response#0];
|
systemChat format ["AttendanceTracker: %1", _response#0];
|
||||||
[_response#0, _response#1, _function] call attendanceTracker_fnc_log;
|
[_response#0, _response#1, _function] call attendanceTracker_fnc_log;
|
||||||
if (_response#0 == "SUCCESS") then {
|
if (_response#0 == "SUCCESS") then {
|
||||||
missionNamespace setVariable ["AttendanceTracker_DBConnected", true];
|
|
||||||
|
|
||||||
// close any null disconnect values from previous mission
|
|
||||||
"AttendanceTracker" callExtension ["fillLastMissionNull", []];
|
|
||||||
|
|
||||||
// log world info
|
// log world info
|
||||||
private _response = "AttendanceTracker" callExtension [
|
private _response = "AttendanceTracker" callExtension [
|
||||||
"logWorld",
|
"logWorld",
|
||||||
@@ -65,21 +62,13 @@ addMissionEventHandler ["ExtensionCallback", {
|
|||||||
[AttendanceTracker getVariable ["missionContext", createHashMap]] call CBA_fnc_encodeJSON
|
[AttendanceTracker getVariable ["missionContext", createHashMap]] call CBA_fnc_encodeJSON
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
missionNamespace setVariable ["AttendanceTracker_DBConnected", true];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
case "writeMissionInfo": {
|
case "writeMission": {
|
||||||
if (_response#0 == "MISSION_ID") then {
|
if (_response#0 == "MISSION_ID") then {
|
||||||
AttendanceTracker_missionId = parseNumber (_response#1);
|
AttendanceTracker_missionId = _response#1;
|
||||||
};
|
|
||||||
};
|
|
||||||
case "writeAttendance": {
|
|
||||||
if (_response#0 == "ATT_LOG") then {
|
|
||||||
(_response#1) params ["_eventType", "_netId", "_rowId"];
|
|
||||||
private _storeIndex = ["SERVER", "MISSION"] find _eventType;
|
|
||||||
((AttendanceTracker getVariable ["rowIds", createHashMap]) getOrDefault [
|
|
||||||
_netId,
|
|
||||||
[nil, nil]
|
|
||||||
]) set [_storeIndex, _rowId];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
default {
|
default {
|
||||||
|
|||||||
@@ -16,16 +16,52 @@
|
|||||||
|
|
||||||
(AttendanceTracker getVariable ["allUsers", createHashMap]) set [_networkId, _userInfo];
|
(AttendanceTracker getVariable ["allUsers", createHashMap]) set [_networkId, _userInfo];
|
||||||
|
|
||||||
[
|
|
||||||
|
[ // write d/c for past events
|
||||||
"Server",
|
"Server",
|
||||||
_playerID,
|
_playerID,
|
||||||
_playerUID,
|
_playerUID,
|
||||||
_profileName,
|
_profileName,
|
||||||
_steamName,
|
_steamName
|
||||||
nil,
|
] call attendanceTracker_fnc_writeDisconnect;
|
||||||
nil
|
|
||||||
] call attendanceTracker_fnc_writeConnect;
|
|
||||||
|
|
||||||
|
// [
|
||||||
|
// "Server",
|
||||||
|
// _playerID,
|
||||||
|
// _playerUID,
|
||||||
|
// _profileName,
|
||||||
|
// _steamName,
|
||||||
|
// nil,
|
||||||
|
// nil
|
||||||
|
// ] call attendanceTracker_fnc_writeConnect;
|
||||||
|
|
||||||
|
// start CBA PFH
|
||||||
|
[format ["(EventHandler) OnUserConnected: Starting CBA PFH for %1", _playerID], "DEBUG"] call attendanceTracker_fnc_log;
|
||||||
|
[
|
||||||
|
{
|
||||||
|
params ["_args", "_handle"];
|
||||||
|
// check if player is still connected
|
||||||
|
private _playerID = _args select 1;
|
||||||
|
private _playerUID = _args select 2;
|
||||||
|
if (allUsers find _playerID == -1) exitWith {
|
||||||
|
[format ["(EventHandler) OnUserConnected: %1 (UID %2) is no longer connected, exiting CBA PFH", _playerUID], "DEBUG"] call attendanceTracker_fnc_log;
|
||||||
|
_args call attendanceTracker_fnc_writeDisconnect;
|
||||||
|
[_handle] call CBA_fnc_removePerFrameHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
_args call attendanceTracker_fnc_writeConnect;
|
||||||
|
},
|
||||||
|
missionNamespace getVariable ["AttendanceTracker_" + "dbupdateintervalseconds", 300],
|
||||||
|
[
|
||||||
|
"Server",
|
||||||
|
_playerID,
|
||||||
|
_playerUID,
|
||||||
|
_profileName,
|
||||||
|
_steamName,
|
||||||
|
nil,
|
||||||
|
nil
|
||||||
|
]
|
||||||
|
] call CBA_fnc_addPerFrameHandler;
|
||||||
}],
|
}],
|
||||||
["OnUserDisconnected", {
|
["OnUserDisconnected", {
|
||||||
params ["_networkId", "_clientStateNumber", "_clientState"];
|
params ["_networkId", "_clientStateNumber", "_clientState"];
|
||||||
@@ -75,15 +111,56 @@
|
|||||||
|
|
||||||
(AttendanceTracker getVariable ["allUsers", createHashMap]) set [_playerID, _userInfo];
|
(AttendanceTracker getVariable ["allUsers", createHashMap]) set [_playerID, _userInfo];
|
||||||
|
|
||||||
[
|
|
||||||
|
[ // write d/c for past events
|
||||||
"Mission",
|
"Mission",
|
||||||
_playerID,
|
_playerID,
|
||||||
_playerUID,
|
_playerUID,
|
||||||
_profileName,
|
_profileName,
|
||||||
_steamName,
|
_steamName,
|
||||||
_jip,
|
_jip,
|
||||||
roleDescription _unit
|
nil
|
||||||
] call attendanceTracker_fnc_writeConnect;
|
] call attendanceTracker_fnc_writeDisconnect;
|
||||||
|
|
||||||
|
// [
|
||||||
|
// "Mission",
|
||||||
|
// _playerID,
|
||||||
|
// _playerUID,
|
||||||
|
// _profileName,
|
||||||
|
// _steamName,
|
||||||
|
// _jip,
|
||||||
|
// roleDescription _unit
|
||||||
|
// ] call attendanceTracker_fnc_writeConnect;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
private _playerID = _args select 1;
|
||||||
|
private _playerUID = _args select 2;
|
||||||
|
private _userInfo = getUserInfo _playerID;
|
||||||
|
private _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;
|
||||||
|
_args call attendanceTracker_fnc_writeDisconnect;
|
||||||
|
[_handle] call CBA_fnc_removePerFrameHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
_args call attendanceTracker_fnc_writeConnect;
|
||||||
|
},
|
||||||
|
missionNamespace getVariable ["AttendanceTracker_" + "dbupdateintervalseconds", 300],
|
||||||
|
[
|
||||||
|
"Mission",
|
||||||
|
_playerID,
|
||||||
|
_playerUID,
|
||||||
|
_profileName,
|
||||||
|
_steamName,
|
||||||
|
_jip,
|
||||||
|
roleDescription _unit
|
||||||
|
]
|
||||||
|
] call CBA_fnc_addPerFrameHandler;
|
||||||
}],
|
}],
|
||||||
["PlayerDisconnected", {
|
["PlayerDisconnected", {
|
||||||
// NOTE: HandleDisconnect returns a DIFFERENT _id than PlayerDisconnected and above handlers, so we can't use it here
|
// NOTE: HandleDisconnect returns a DIFFERENT _id than PlayerDisconnected and above handlers, so we can't use it here
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
parseSimpleArray ('AttendanceTracker' callExtension "getSettings");
|
||||||
@@ -7,7 +7,7 @@ params [
|
|||||||
if (isNil "_message") exitWith {false};
|
if (isNil "_message") exitWith {false};
|
||||||
if (
|
if (
|
||||||
missionNamespace getVariable ["AttendanceTracker_debug", false] &&
|
missionNamespace getVariable ["AttendanceTracker_debug", false] &&
|
||||||
_level == "DEBUG"
|
_level != "WARN" && _level != "ERROR"
|
||||||
) exitWith {};
|
) exitWith {};
|
||||||
|
|
||||||
"AttendanceTracker" callExtension ["log", [_level, _message]];
|
"AttendanceTracker" callExtension ["log", [_level, _message]];
|
||||||
|
|||||||
@@ -6,7 +6,22 @@ diag_log format ["AttendanceTracker: Mission started at %1", AttendanceTracker_m
|
|||||||
AttendanceTracker_missionHash = call attendanceTracker_fnc_getMissionHash;
|
AttendanceTracker_missionHash = call attendanceTracker_fnc_getMissionHash;
|
||||||
diag_log format ["AttendanceTracker: Mission hash is %1", AttendanceTracker_missionHash];
|
diag_log format ["AttendanceTracker: Mission hash is %1", AttendanceTracker_missionHash];
|
||||||
|
|
||||||
|
_settings = call attendanceTracker_fnc_getSettings;
|
||||||
|
if (count _settings > 0) then {
|
||||||
|
for "_i" from 0 to (count _settings) - 1 do {
|
||||||
|
_setting = _settings select _i;
|
||||||
|
_key = _setting select 0;
|
||||||
|
_value = _setting select 1;
|
||||||
|
missionNamespace setVariable ["AttendanceTracker_" + _key, _value];
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
[format["Failed to parse settings: %1", _settings], "ERROR"] call attendanceTracker_fnc_log;
|
||||||
|
};
|
||||||
|
call attendanceTracker_fnc_connectDB;
|
||||||
|
|
||||||
AttendanceTracker setVariable ["missionContext", createHashMapFromArray [
|
AttendanceTracker setVariable ["missionContext", createHashMapFromArray [
|
||||||
|
["missionHash", AttendanceTracker_missionHash],
|
||||||
|
["missionStart", AttendanceTracker_missionStartTimestamp],
|
||||||
["missionName", missionName],
|
["missionName", missionName],
|
||||||
["briefingName", briefingName],
|
["briefingName", briefingName],
|
||||||
["missionNameSource", missionNameSource],
|
["missionNameSource", missionNameSource],
|
||||||
@@ -24,9 +39,6 @@ AttendanceTracker setVariable ["missionContext", createHashMapFromArray [
|
|||||||
// store all user details in a hash when they connect so we can reference it in disconnect events
|
// store all user details in a hash when they connect so we can reference it in disconnect events
|
||||||
AttendanceTracker setVariable ["allUsers", createHashMap];
|
AttendanceTracker setVariable ["allUsers", createHashMap];
|
||||||
AttendanceTracker setVariable ["rowIds", createHashMap];
|
AttendanceTracker setVariable ["rowIds", createHashMap];
|
||||||
missionNamespace setVariable ["AttendanceTracker_debug", false];
|
|
||||||
|
|
||||||
call attendanceTracker_fnc_connectDB;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
if (!isServer) exitWith {};
|
if (!isServer) exitWith {};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// need date for MySQL in format 2006-01-02 15:04:05
|
// need date for MySQL in format 2006-01-02 15:04:05
|
||||||
|
|
||||||
systemTimeUTC params [
|
systemTimeUTC apply {if (_x < 10) then {"0" + str _x} else {str _x}} params [
|
||||||
"_year",
|
"_year",
|
||||||
"_month",
|
"_month",
|
||||||
"_day",
|
"_day",
|
||||||
|
|||||||
@@ -17,8 +17,12 @@ _hash set ["profileName", _profileName];
|
|||||||
_hash set ["steamName", _steamName];
|
_hash set ["steamName", _steamName];
|
||||||
_hash set ["isJIP", _isJIP];
|
_hash set ["isJIP", _isJIP];
|
||||||
_hash set ["roleDescription", _roleDescription];
|
_hash set ["roleDescription", _roleDescription];
|
||||||
_hash set ["missionHash", missionNamespace getVariable ["AttendanceTracker_missionHash", ""]];
|
|
||||||
|
|
||||||
"AttendanceTracker" callExtension ["writeAttendance", [[_hash] call CBA_fnc_encodeJSON]];
|
[
|
||||||
|
{missionNamespace getVariable ["AttendanceTracker_DBConnected", false]},
|
||||||
|
{"AttendanceTracker" callExtension ["writeAttendance", [[_this] call CBA_fnc_encodeJSON]]},
|
||||||
|
_hash, // args
|
||||||
|
30 // timeout in seconds. if DB never connects, we don't want these building up
|
||||||
|
] call CBA_fnc_waitUntilAndExecute;
|
||||||
|
|
||||||
true;
|
true;
|
||||||
@@ -17,8 +17,12 @@ _hash set ["profileName", _profileName];
|
|||||||
_hash set ["steamName", _steamName];
|
_hash set ["steamName", _steamName];
|
||||||
_hash set ["isJIP", _isJIP];
|
_hash set ["isJIP", _isJIP];
|
||||||
_hash set ["roleDescription", _roleDescription];
|
_hash set ["roleDescription", _roleDescription];
|
||||||
_hash set ["missionHash", missionNamespace getVariable ["AttendanceTracker_missionHash", ""]];
|
|
||||||
|
|
||||||
"AttendanceTracker" callExtension ["writeDisconnectEvent", [[_hash] call CBA_fnc_encodeJSON]];
|
[
|
||||||
|
{missionNamespace getVariable ["AttendanceTracker_DBConnected", false]},
|
||||||
|
{"AttendanceTracker" callExtension ["writeDisconnectEvent", [[_this] call CBA_fnc_encodeJSON]]},
|
||||||
|
_hash, // args
|
||||||
|
30 // timeout in seconds. if DB never connects, we don't want these building up
|
||||||
|
] call CBA_fnc_waitUntilAndExecute;
|
||||||
|
|
||||||
true;
|
true;
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
{
|
{
|
||||||
"mysqlHost": "127.0.0.1",
|
"sqlConfig": {
|
||||||
"mysqlPort": 3306,
|
"mysqlHost": "127.0.0.1",
|
||||||
"mysqlUser": "root",
|
"mysqlPort": 3306,
|
||||||
"mysqlPassword": "root",
|
"mysqlUser": "root",
|
||||||
"mysqlDatabase": "arma3_attendance"
|
"mysqlPassword": "root",
|
||||||
|
"mysqlDatabase": "arma3_attendance"
|
||||||
|
},
|
||||||
|
"armaConfig": {
|
||||||
|
"dbUpdateIntervalSeconds": 90,
|
||||||
|
"debug": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -37,11 +38,20 @@ var ATTENDANCE_TABLE string = "attendance"
|
|||||||
var MISSIONS_TABLE string = "missions"
|
var MISSIONS_TABLE string = "missions"
|
||||||
var WORLDS_TABLE string = "worlds"
|
var WORLDS_TABLE string = "worlds"
|
||||||
|
|
||||||
|
var LAST_SERVER_TIME uint64 = 0
|
||||||
|
|
||||||
// ! TODO make a hash to save key:netId from A3 value:rowId from join event
|
// ! TODO make a hash to save key:netId from A3 value:rowId from join event
|
||||||
|
|
||||||
var ATConfig AttendanceTrackerConfig
|
var Config AttendanceTrackerConfig
|
||||||
|
var ATConfig ATSQLConfig
|
||||||
|
var A3Config ArmaConfig
|
||||||
|
|
||||||
type AttendanceTrackerConfig struct {
|
type ArmaConfig struct {
|
||||||
|
DBUpdateIntervalSeconds int `json:"dbUpdateIntervalSeconds"`
|
||||||
|
Debug bool `json:"debug"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ATSQLConfig struct {
|
||||||
MySQLHost string `json:"mysqlHost"`
|
MySQLHost string `json:"mysqlHost"`
|
||||||
MySQLPort int `json:"mysqlPort"`
|
MySQLPort int `json:"mysqlPort"`
|
||||||
MySQLUser string `json:"mysqlUser"`
|
MySQLUser string `json:"mysqlUser"`
|
||||||
@@ -49,6 +59,11 @@ type AttendanceTrackerConfig struct {
|
|||||||
MySQLDatabase string `json:"mysqlDatabase"`
|
MySQLDatabase string `json:"mysqlDatabase"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AttendanceTrackerConfig struct {
|
||||||
|
ArmaConfig ArmaConfig `json:"armaConfig"`
|
||||||
|
SQLConfig ATSQLConfig `json:"sqlConfig"`
|
||||||
|
}
|
||||||
|
|
||||||
// database connection
|
// database connection
|
||||||
var db *gorm.DB
|
var db *gorm.DB
|
||||||
|
|
||||||
@@ -104,14 +119,42 @@ func loadConfig() {
|
|||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
decoder := json.NewDecoder(file)
|
decoder := json.NewDecoder(file)
|
||||||
err = decoder.Decode(&ATConfig)
|
err = decoder.Decode(&Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
A3Config = Config.ArmaConfig
|
||||||
|
ATConfig = Config.SQLConfig
|
||||||
writeLog(functionName, `["Config loaded", "INFO"]`)
|
writeLog(functionName, `["Config loaded", "INFO"]`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getSettings() string {
|
||||||
|
// get settings from A3Config and send to Arma
|
||||||
|
var settings string = `[`
|
||||||
|
// iterate through keys in A3Config struct
|
||||||
|
v := reflect.ValueOf(A3Config)
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
// get field name
|
||||||
|
fieldName := v.Type().Field(i).Name
|
||||||
|
// get field value
|
||||||
|
fieldValue := v.Field(i).Interface()
|
||||||
|
// if field value is a string, add quotes
|
||||||
|
fieldValueString := fmt.Sprintf("%v", fieldValue)
|
||||||
|
if reflect.TypeOf(fieldValue).Kind() == reflect.String {
|
||||||
|
fieldValueString = fmt.Sprintf(`"%v"`, fieldValue)
|
||||||
|
}
|
||||||
|
// add to settings, key should be lowercase
|
||||||
|
settings += fmt.Sprintf(`["%s", %s],`, strings.ToLower(fieldName), fieldValueString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove last comma
|
||||||
|
settings = strings.TrimSuffix(settings, ",")
|
||||||
|
settings += `]`
|
||||||
|
|
||||||
|
return settings
|
||||||
|
}
|
||||||
|
|
||||||
func getMissionHash() string {
|
func getMissionHash() string {
|
||||||
functionName := "getMissionHash"
|
functionName := "getMissionHash"
|
||||||
// get md5 hash of string
|
// get md5 hash of string
|
||||||
@@ -124,7 +167,36 @@ func getMissionHash() string {
|
|||||||
return hashString
|
return hashString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateServerTime(serverTime uint64) {
|
||||||
|
functionName := "updateServerTime"
|
||||||
|
// if serverTime is less than last server time, close server events
|
||||||
|
if serverTime < LAST_SERVER_TIME {
|
||||||
|
writeLog(functionName, `["Server has restarted, closing pending server sessions in attendance", "INFO"]`)
|
||||||
|
closeServerEvents()
|
||||||
|
}
|
||||||
|
LAST_SERVER_TIME = serverTime
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeServerEvents() {
|
||||||
|
functionName := "closeServerEvents"
|
||||||
|
writeLog(functionName, `["Closing server events", "INFO"]`)
|
||||||
|
// get all server events with null DisconnectTime & set DisconnectTime to current time
|
||||||
|
op := db.Model(&AttendanceItem{}).Where(`event_type = ? AND disconnect_time IS NULL`).Update("disconnect_time", time.Now().UTC())
|
||||||
|
if op.Error != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, op.Error))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// log how many
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%d server events closed", "INFO"]`, op.RowsAffected))
|
||||||
|
}
|
||||||
|
|
||||||
func connectDB() error {
|
func connectDB() error {
|
||||||
|
|
||||||
|
// load config
|
||||||
|
loadConfig()
|
||||||
|
|
||||||
|
// connect to database
|
||||||
var err error
|
var err error
|
||||||
dsn := fmt.Sprintf(
|
dsn := fmt.Sprintf(
|
||||||
"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||||
@@ -136,9 +208,14 @@ func connectDB() error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// log dsn and pause
|
// log dsn and pause
|
||||||
writeLog("connectDB", fmt.Sprintf(`["DSN: %s", "INFO"]`, dsn))
|
// writeLog("connectDB", fmt.Sprintf(`["DSN: %s", "INFO"]`, dsn))
|
||||||
var input string
|
|
||||||
fmt.Scanln(&input)
|
if db != nil {
|
||||||
|
// log success and return
|
||||||
|
writeLog("connectDB", `["Database already connected", "INFO"]`)
|
||||||
|
writeLog("connectDB", `["SUCCESS", "INFO"]`)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -154,6 +231,7 @@ func connectDB() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
writeLog("connectDB", `["Database connected", "INFO"]`)
|
writeLog("connectDB", `["Database connected", "INFO"]`)
|
||||||
|
writeLog("connectDB", `["SUCCESS", "INFO"]`)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +262,11 @@ func writeWorldInfo(worldInfo string) {
|
|||||||
|
|
||||||
// prevent crash
|
// prevent crash
|
||||||
if db == nil {
|
if db == nil {
|
||||||
connectDB()
|
err := connectDB()
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// write world if not exist
|
// write world if not exist
|
||||||
@@ -216,7 +298,7 @@ type Mission struct {
|
|||||||
MissionStart string `json:"missionStart"`
|
MissionStart string `json:"missionStart"`
|
||||||
MissionHash string `json:"missionHash"`
|
MissionHash string `json:"missionHash"`
|
||||||
WorldName string `json:"worldName" gorm:"-"`
|
WorldName string `json:"worldName" gorm:"-"`
|
||||||
WorldID int
|
WorldID uint
|
||||||
World World `gorm:"foreignkey:WorldID"`
|
World World `gorm:"foreignkey:WorldID"`
|
||||||
Attendees []AttendanceItem
|
Attendees []AttendanceItem
|
||||||
}
|
}
|
||||||
@@ -234,6 +316,24 @@ func writeMission(missionJSON string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prevent crash
|
||||||
|
if db == nil {
|
||||||
|
err := connectDB()
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get world from WorldName
|
||||||
|
var world World
|
||||||
|
db.Where("world_name = ?", mi.WorldName).First(&world)
|
||||||
|
if world.ID == 0 {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["World not found for %s, cannot write mission!", "ERROR"]`, mi.WorldName))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mi.WorldID = world.ID
|
||||||
|
|
||||||
// write mission to database
|
// write mission to database
|
||||||
db.Create(&mi)
|
db.Create(&mi)
|
||||||
if db.Error != nil {
|
if db.Error != nil {
|
||||||
@@ -241,6 +341,7 @@ func writeMission(missionJSON string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
writeLog(functionName, fmt.Sprintf(`["Mission written with ID %d", "INFO"]`, mi.ID))
|
writeLog(functionName, fmt.Sprintf(`["Mission written with ID %d", "INFO"]`, mi.ID))
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["MISSION_ID", %d]`, mi.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
type AttendanceItem struct {
|
type AttendanceItem struct {
|
||||||
@@ -258,6 +359,54 @@ type AttendanceItem struct {
|
|||||||
MissionID int
|
MissionID int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeDisconnectEvent(data string) {
|
||||||
|
functionName := "writeDisconnectEvent"
|
||||||
|
var err error
|
||||||
|
// data is json, parse it
|
||||||
|
stringjson := fixEscapeQuotes(trimQuotes(data))
|
||||||
|
var event AttendanceItem
|
||||||
|
err = json.Unmarshal([]byte(stringjson), &event)
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent crash
|
||||||
|
if db == nil {
|
||||||
|
err := connectDB()
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all attendance rows of type without disconnect rows
|
||||||
|
var attendanceRows []AttendanceItem
|
||||||
|
db.Where("player_uid = ? AND event_type = ? AND disconnect_time = '0000-00-00 00:00:00'", event.PlayerUID, event.EventType).Find(&attendanceRows)
|
||||||
|
for _, row := range attendanceRows {
|
||||||
|
// put to json
|
||||||
|
json, err := json.Marshal(row)
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["Updating disconnect time for %s", "INFO"]`, json))
|
||||||
|
if row.JoinTime.Before(time.Now().UTC().Add(-1*time.Hour)) && row.EventType == "Mission" {
|
||||||
|
// if mission JoinTime is more than 1 hour ago, simplify this to write DisconnectTime as 1 hour from JoinTime. this to account for crashes where people don't immediately rejoin
|
||||||
|
row.DisconnectTime = time.Now().UTC().Add(-1 * time.Hour)
|
||||||
|
} else if row.JoinTime.Before(time.Now().UTC().Add(-6*time.Hour)) && row.EventType == "Server" {
|
||||||
|
// if server JoinTime is more than 6 hours ago, simplify this to write DisconnectTime as 6 hours from JoinTime. this to account for server crashes where people don't immediately rejoin without overwriting valid (potentially lengthy) server sessions
|
||||||
|
row.DisconnectTime = time.Now().UTC().Add(-6 * time.Hour)
|
||||||
|
} else {
|
||||||
|
// otherwise, update DisconnectTime to now
|
||||||
|
row.DisconnectTime = time.Now().UTC()
|
||||||
|
}
|
||||||
|
db.Save(&row)
|
||||||
|
}
|
||||||
|
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["Disconnect events written for %s", "DEBUG"]`, event.PlayerUID))
|
||||||
|
}
|
||||||
|
|
||||||
func writeAttendance(data string) {
|
func writeAttendance(data string) {
|
||||||
functionName := "writeAttendance"
|
functionName := "writeAttendance"
|
||||||
var err error
|
var err error
|
||||||
@@ -272,7 +421,11 @@ func writeAttendance(data string) {
|
|||||||
|
|
||||||
// prevent crash
|
// prevent crash
|
||||||
if db == nil {
|
if db == nil {
|
||||||
connectDB()
|
err := connectDB()
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var playerUid string
|
var playerUid string
|
||||||
@@ -280,10 +433,10 @@ func writeAttendance(data string) {
|
|||||||
if event.EventType == "Server" {
|
if event.EventType == "Server" {
|
||||||
// check for most recent existing attendance row
|
// check for most recent existing attendance row
|
||||||
var attendance AttendanceItem
|
var attendance AttendanceItem
|
||||||
db.Where("player_uid = ? AND event_type = ?", event.PlayerId, "Server").Last(&attendance)
|
db.Where("player_id = ? AND player_uid = ? AND event_type = ?", event.PlayerId, event.PlayerUID, event.EventType).Order("join_time desc").First(&attendance)
|
||||||
if attendance.ID != 0 {
|
if attendance.ID != 0 {
|
||||||
// update disconnect time
|
// update disconnect time
|
||||||
row := db.Model(&attendance).Update("disconnect_time", time.Now().UTC().Format("2006-01-02 15:04:05"))
|
row := db.Model(&attendance).Update("disconnect_time", attendance.DisconnectTime)
|
||||||
if row.Error != nil {
|
if row.Error != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, row.Error))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, row.Error))
|
||||||
return
|
return
|
||||||
@@ -292,6 +445,7 @@ func writeAttendance(data string) {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// insert new row
|
// insert new row
|
||||||
|
event.JoinTime = time.Now().UTC()
|
||||||
row := db.Create(&event)
|
row := db.Create(&event)
|
||||||
if row.Error != nil {
|
if row.Error != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, row.Error))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, row.Error))
|
||||||
@@ -300,18 +454,29 @@ func writeAttendance(data string) {
|
|||||||
rowId, playerUid = event.ID, event.PlayerUID
|
rowId, playerUid = event.ID, event.PlayerUID
|
||||||
}
|
}
|
||||||
} else if event.EventType == "Mission" {
|
} else if event.EventType == "Mission" {
|
||||||
// check for most recent join_time for this player within 6 hours without a disconnect_time
|
// use gorm to associate this event with the mission sharing a mission hash
|
||||||
|
var mission Mission
|
||||||
|
db.Where("mission_hash = ?", event.MissionHash).First(&mission)
|
||||||
|
if mission.ID != 0 {
|
||||||
|
event.MissionID = int(mission.ID)
|
||||||
|
} else {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["Mission not found for hash %s", "ERROR"]`, event.MissionHash))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for most recent JoinTime for this player and event type
|
||||||
var attendance AttendanceItem
|
var attendance AttendanceItem
|
||||||
db.Where("player_uid = ? AND join_time > ? AND disconnect_time IS NULL", event.PlayerUID, time.Now().UTC().Add(-6*time.Hour).Format("2006-01-02 15:04:05")).Last(&attendance)
|
db.Where("player_id = ? AND player_uid = ? AND event_type = ? AND mission_hash = ?", event.PlayerId, event.PlayerUID, event.EventType, event.MissionHash).Order("join_time desc").First(&attendance)
|
||||||
if attendance.ID != 0 {
|
if attendance.ID != 0 {
|
||||||
// update disconnect time
|
// update disconnect time
|
||||||
row := db.Model(&attendance).Update("disconnect_time", time.Now().UTC().Format("2006-01-02 15:04:05"))
|
row := db.Model(&attendance).Update("disconnect_time", time.Now().UTC())
|
||||||
if row.Error != nil {
|
if row.Error != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, row.Error))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, row.Error))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rowId, playerUid = attendance.ID, attendance.PlayerUID
|
rowId, playerUid = attendance.ID, attendance.PlayerUID
|
||||||
} else {
|
} else {
|
||||||
|
event.JoinTime = time.Now().UTC()
|
||||||
// insert new row
|
// insert new row
|
||||||
row := db.Create(&event)
|
row := db.Create(&event)
|
||||||
if row.Error != nil {
|
if row.Error != nil {
|
||||||
@@ -322,13 +487,7 @@ func writeAttendance(data string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["Saved attendance for %s to row id %d", "INFO"]`, playerUid, rowId))
|
writeLog(functionName, fmt.Sprintf(`["Saved attendance for %s to row id %d", "DEBUG"]`, playerUid, rowId))
|
||||||
if event.EventType == "Server" {
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["ATT_LOG", ["SERVER", "%s", "%d"]]`, playerUid, rowId))
|
|
||||||
} else if event.EventType == "Mission" {
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["ATT_LOG", ["MISSION", "%s", "%d"]]`, playerUid, rowId))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runExtensionCallback(name *C.char, function *C.char, data *C.char) C.int {
|
func runExtensionCallback(name *C.char, function *C.char, data *C.char) C.int {
|
||||||
@@ -359,10 +518,6 @@ func goRVExtensionArgs(output *C.char, outputsize C.size_t, input *C.char, argv
|
|||||||
temp := fmt.Sprintf("Function: %s nb params: %d", C.GoString(input), argc)
|
temp := fmt.Sprintf("Function: %s nb params: %d", C.GoString(input), argc)
|
||||||
|
|
||||||
switch C.GoString(input) {
|
switch C.GoString(input) {
|
||||||
case "fillLastMissionNull":
|
|
||||||
{
|
|
||||||
// go fillLastMissionNull()
|
|
||||||
}
|
|
||||||
case "writeAttendance":
|
case "writeAttendance":
|
||||||
{ // callExtension ["logAttendance", [_hash] call CBA_fnc_encodeJSON]];
|
{ // callExtension ["logAttendance", [_hash] call CBA_fnc_encodeJSON]];
|
||||||
if argc == 1 {
|
if argc == 1 {
|
||||||
@@ -370,20 +525,18 @@ func goRVExtensionArgs(output *C.char, outputsize C.size_t, input *C.char, argv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "writeDisconnectEvent":
|
case "writeDisconnectEvent":
|
||||||
{ // callExtension ["writeDisconnectEvent", [[_hash] call CBA_fnc_encodeJSON]];
|
{ // callExtension ["writeDisconnectEvent", [_hash] call CBA_fnc_encodeJSON]];
|
||||||
|
|
||||||
if argc == 1 {
|
if argc == 1 {
|
||||||
// go writeDisconnectEvent(out[0])
|
go writeDisconnectEvent(out[0])
|
||||||
go writeAttendance(out[0])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "logMission":
|
case "logMission":
|
||||||
if argc == 1 {
|
if argc == 1 {
|
||||||
go writeMission(out[0])
|
writeMission(out[0])
|
||||||
}
|
}
|
||||||
case "logWorld":
|
case "logWorld":
|
||||||
if argc == 1 {
|
if argc == 1 {
|
||||||
go writeWorldInfo(out[0])
|
writeWorldInfo(out[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,8 +592,7 @@ func writeLog(functionName string, data string) {
|
|||||||
|
|
||||||
// get calling function & line
|
// get calling function & line
|
||||||
_, file, line, _ := runtime.Caller(1)
|
_, file, line, _ := runtime.Caller(1)
|
||||||
log.Printf(`%s:%d: %s`, path.Base(file), line, data)
|
log.Printf(`%s:%d:%s %s`, path.Base(file), line, functionName, data)
|
||||||
log.Printf(`%s: %s`, functionName, data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export goRVExtension
|
//export goRVExtension
|
||||||
@@ -455,10 +607,13 @@ func goRVExtension(output *C.char, outputsize C.size_t, input *C.char) {
|
|||||||
temp = EXTENSION_VERSION
|
temp = EXTENSION_VERSION
|
||||||
case "getDir":
|
case "getDir":
|
||||||
temp = getDir()
|
temp = getDir()
|
||||||
|
case "getSettings":
|
||||||
|
loadConfig()
|
||||||
|
temp = getSettings()
|
||||||
case "getTimestamp":
|
case "getTimestamp":
|
||||||
temp = fmt.Sprintf(`["%s"]`, getTimestamp())
|
temp = fmt.Sprintf(`["%s"]`, getTimestamp())
|
||||||
case "connectDB":
|
case "connectDB":
|
||||||
go connectDB()
|
connectDB()
|
||||||
temp = fmt.Sprintf(`["%s"]`, "Connecting to DB")
|
temp = fmt.Sprintf(`["%s"]`, "Connecting to DB")
|
||||||
case "getMissionHash":
|
case "getMissionHash":
|
||||||
temp = fmt.Sprintf(`["%s"]`, getMissionHash())
|
temp = fmt.Sprintf(`["%s"]`, getMissionHash())
|
||||||
|
|||||||
Reference in New Issue
Block a user