implement GORM, expand config, modify the way sessions are handled

This commit is contained in:
2023-07-06 01:48:32 -07:00
parent 9a859b403a
commit ee0c28d2c8
13 changed files with 324 additions and 75 deletions

View File

@@ -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 {};

View File

@@ -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 {

View File

@@ -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

View File

@@ -0,0 +1 @@
parseSimpleArray ('AttendanceTracker' callExtension "getSettings");

View File

@@ -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]];

View File

@@ -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 {};

View File

@@ -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",

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
}
} }

View File

@@ -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())