mirror of
https://github.com/indig0fox/Arma3-AttendanceTracker.git/
synced 2025-12-08 09:51:47 -06:00
add mission md5 hash as unique id, break out db connect ask, add world
This commit is contained in:
@@ -14,6 +14,7 @@ class CfgFunctions {
|
|||||||
class functions {
|
class functions {
|
||||||
file = "\AttendanceTracker\functions";
|
file = "\AttendanceTracker\functions";
|
||||||
class postInit {postInit = 1;};
|
class postInit {postInit = 1;};
|
||||||
|
class connectDB {};
|
||||||
class eventHandlers {};
|
class eventHandlers {};
|
||||||
class callbackHandler {postInit = 1;};
|
class callbackHandler {postInit = 1;};
|
||||||
class log {};
|
class log {};
|
||||||
|
|||||||
@@ -39,6 +39,21 @@ 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 {
|
||||||
|
missionNamespace setVariable ["AttendanceTracker_DBConnected", true];
|
||||||
|
|
||||||
|
// log mission info and get back the row Id to send with future messages
|
||||||
|
private _response = "AttendanceTracker" callExtension ["logMission", [
|
||||||
|
[AttendanceTracker getVariable ["missionContext", createHashMap]] call CBA_fnc_encodeJSON
|
||||||
|
]];
|
||||||
|
AttendanceTracker_missionId = parseNumber _response;
|
||||||
|
|
||||||
|
|
||||||
|
// log world info
|
||||||
|
private _response = "AttendanceTracker" callExtension ["logWorld", [
|
||||||
|
[call attendanceTracker_fnc_getWorldInfo] call CBA_fnc_encodeJSON
|
||||||
|
]];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
default {
|
default {
|
||||||
_response call attendanceTracker_fnc_log;
|
_response call attendanceTracker_fnc_log;
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
private _database = "AttendanceTracker" callExtension "connectDB";
|
||||||
|
// systemChat "AttendanceTracker: Connecting to database...";
|
||||||
|
["Connecting to database...", "INFO"] call attendanceTracker_fnc_log;
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
_world = ( configfile >> "CfgWorlds" >> worldName );
|
||||||
|
_author = getText( _world >> "author" );
|
||||||
|
_name = getText ( _world >> "description" );
|
||||||
|
|
||||||
|
_source = configSourceMod ( _world );
|
||||||
|
|
||||||
|
_workshopID = '';
|
||||||
|
|
||||||
|
{
|
||||||
|
if ( ( _x#1 ) == _source ) then {
|
||||||
|
_workshopID = _x#7;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
} foreach getLoadedModsInfo;
|
||||||
|
|
||||||
|
// [_name, _author, _workshopID];
|
||||||
|
|
||||||
|
[
|
||||||
|
["worldName", _name],
|
||||||
|
["author", _author],
|
||||||
|
["worldSize", worldSize],
|
||||||
|
["workshopID", _workshopID]
|
||||||
|
];
|
||||||
@@ -16,6 +16,7 @@ _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 ["logAttendance", [[_hash] call CBA_fnc_encodeJSON]];
|
"AttendanceTracker" callExtension ["logAttendance", [[_hash] call CBA_fnc_encodeJSON]];
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ _hash set ["profileName", _profileName];
|
|||||||
_hash set ["steamName", _steamName];
|
_hash set ["steamName", _steamName];
|
||||||
_hash set ["isJIP", false];
|
_hash set ["isJIP", false];
|
||||||
_hash set ["roleDescription", ""];
|
_hash set ["roleDescription", ""];
|
||||||
|
_hash set ["missionHash", missionNamespace getVariable ["AttendanceTracker_missionHash", ""]];
|
||||||
|
|
||||||
"AttendanceTracker" callExtension ["logAttendance", [[_hash] call CBA_fnc_encodeJSON]];
|
"AttendanceTracker" callExtension ["logAttendance", [[_hash] call CBA_fnc_encodeJSON]];
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
|
||||||
AttendanceTracker = false call CBA_fnc_createNamespace;
|
AttendanceTracker = false call CBA_fnc_createNamespace;
|
||||||
|
|
||||||
|
AttendanceTracker_missionStartTimestamp = call attendanceTracker_fnc_timestamp;
|
||||||
|
AttendanceTracker_missionHash = "AttendanceTracker" callExtension ["getMissionHash", AttendanceTracker_missionStartTimestamp];
|
||||||
|
|
||||||
AttendanceTracker setVariable ["missionContext", createHashMapFromArray [
|
AttendanceTracker setVariable ["missionContext", createHashMapFromArray [
|
||||||
["missionName", missionName],
|
["missionName", missionName],
|
||||||
["briefingName", briefingName],
|
["briefingName", briefingName],
|
||||||
@@ -9,16 +12,17 @@ AttendanceTracker setVariable ["missionContext", createHashMapFromArray [
|
|||||||
["author", getMissionConfigValue ["author", ""]],
|
["author", getMissionConfigValue ["author", ""]],
|
||||||
["serverName", serverName],
|
["serverName", serverName],
|
||||||
["serverProfile", profileName],
|
["serverProfile", profileName],
|
||||||
["missionStart", call attendanceTracker_fnc_timestamp]
|
["missionStart", AttendanceTracker_missionStartTimestamp],
|
||||||
|
["missionHash", AttendanceTracker_missionHash]
|
||||||
]];
|
]];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 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];
|
||||||
missionNamespace getVariable ["AttendanceTracker_debug", false];
|
missionNamespace setVariable ["AttendanceTracker_debug", false];
|
||||||
|
|
||||||
private _database = "AttendanceTracker" callExtension "connectDB";
|
call attendanceTracker_fnc_connectDB;
|
||||||
// systemChat "AttendanceTracker: Connecting to database...";
|
|
||||||
["Connecting to database...", "INFO"] call attendanceTracker_fnc_log;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
if (!isServer) exitWith {};
|
if (!isServer) exitWith {};
|
||||||
|
|||||||
Binary file not shown.
@@ -10,6 +10,7 @@ import "C" // This is required to import the C code
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/md5"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -103,6 +104,18 @@ func loadConfig() {
|
|||||||
writeLog(functionName, `["Config loaded", "INFO"]`)
|
writeLog(functionName, `["Config loaded", "INFO"]`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMissionHash(time string) string {
|
||||||
|
functionName := "getMissionHash"
|
||||||
|
// get md5 hash of string
|
||||||
|
// https://stackoverflow.com/questions/2377881/how-to-get-a-md5-hash-from-a-string-in-golang
|
||||||
|
hash := md5.Sum([]byte(time))
|
||||||
|
|
||||||
|
// convert to string
|
||||||
|
hashString := fmt.Sprintf("%x", hash)
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["Mission hash: %s", "INFO"]`, hashString))
|
||||||
|
return hashString
|
||||||
|
}
|
||||||
|
|
||||||
func connectDB() string {
|
func connectDB() string {
|
||||||
functionName := "connectDB"
|
functionName := "connectDB"
|
||||||
var err error
|
var err error
|
||||||
@@ -142,10 +155,80 @@ func connectDB() string {
|
|||||||
return "ERROR"
|
return "ERROR"
|
||||||
}
|
}
|
||||||
writeLog(functionName, fmt.Sprintf(`["Connected to MySQL/MariaDB version %s", "INFO"]`, version))
|
writeLog(functionName, fmt.Sprintf(`["Connected to MySQL/MariaDB version %s", "INFO"]`, version))
|
||||||
|
writeLog(functionName, `["SUCCESS", "INFO"]`)
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|
||||||
type AttendanceLogItem struct {
|
type WorldInfo struct {
|
||||||
|
WorldName string `json:"worldName"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
WorldSize int `json:"worldSize"`
|
||||||
|
WorkshopID string `json:"workshopID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeWorldInfo(worldInfo string) {
|
||||||
|
functionName := "writeWorldInfo"
|
||||||
|
// worldInfo is json, parse it
|
||||||
|
var wi WorldInfo
|
||||||
|
err := json.Unmarshal([]byte(worldInfo), &wi)
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// write to log
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["WorldName:%s Author:%s WorldSize:%d WorkshopID:%s", "INFO"]`, wi.WorldName, wi.Author, wi.WorldSize, wi.WorkshopID))
|
||||||
|
|
||||||
|
// write to database
|
||||||
|
// check if world exists
|
||||||
|
var worldID int
|
||||||
|
err = db.QueryRow("SELECT id FROM worlds WHERE workshop_id = ?", wi.WorkshopID).Scan(&worldID)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
// world does not exist, insert it
|
||||||
|
stmt, err := db.Prepare("INSERT INTO worlds (world_name, author, world_size, workshop_id) VALUES (?, ?, ?, ?)")
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
res, err := stmt.Exec(wi.WorldName, wi.Author, wi.WorldSize, wi.WorkshopID)
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastID, err := res.LastInsertId()
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["World inserted with ID %d", "INFO"]`, lastID))
|
||||||
|
} else {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// world exists, update it
|
||||||
|
stmt, err := db.Prepare("UPDATE worlds SET world_name = ?, author = ?, world_size = ? WHERE id = ?")
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
res, err := stmt.Exec(wi.WorldName, wi.Author, wi.WorldSize, worldID)
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rowsAffected, err := res.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["World updated with %d rows affected", "INFO"]`, rowsAffected))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MissionInfo struct {
|
||||||
MissionName string `json:"missionName"`
|
MissionName string `json:"missionName"`
|
||||||
BriefingName string `json:"briefingName"`
|
BriefingName string `json:"briefingName"`
|
||||||
MissionNameSource string `json:"missionNameSource"`
|
MissionNameSource string `json:"missionNameSource"`
|
||||||
@@ -154,7 +237,52 @@ type AttendanceLogItem struct {
|
|||||||
ServerName string `json:"serverName"`
|
ServerName string `json:"serverName"`
|
||||||
ServerProfile string `json:"serverProfile"`
|
ServerProfile string `json:"serverProfile"`
|
||||||
MissionStart string `json:"missionStart"`
|
MissionStart string `json:"missionStart"`
|
||||||
// situational
|
MissionHash string `json:"missionHash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeMissionInfo(missionInfo string) {
|
||||||
|
functionName := "writeMissionInfo"
|
||||||
|
// missionInfo is json, parse it
|
||||||
|
var mi MissionInfo
|
||||||
|
err := json.Unmarshal([]byte(missionInfo), &mi)
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get MySQL friendly datetime
|
||||||
|
// first, convert string to int
|
||||||
|
missionStartTime, err := strconv.ParseInt(mi.MissionStart, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t := time.Unix(0, missionStartTime).Format("2006-01-02 15:04:05")
|
||||||
|
// write to log
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["MissionName:%s BriefingName:%s MissionNameSource:%s OnLoadName:%s Author:%s ServerName:%s ServerProfile:%s MissionStart:%s MissionHash:%s", "INFO"]`, mi.MissionName, mi.BriefingName, mi.MissionNameSource, mi.OnLoadName, mi.Author, mi.ServerName, mi.ServerProfile, t, mi.MissionHash))
|
||||||
|
|
||||||
|
// 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 (?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
res, err := stmt.Exec(mi.MissionName, mi.BriefingName, mi.MissionNameSource, mi.OnLoadName, mi.Author, mi.ServerName, mi.ServerProfile, t, mi.MissionHash)
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastID, err := res.LastInsertId()
|
||||||
|
if err != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["Mission inserted with ID %d", "INFO"]`, lastID))
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttendanceLogItem struct {
|
||||||
EventType string `json:"eventType"`
|
EventType string `json:"eventType"`
|
||||||
PlayerId string `json:"playerId"`
|
PlayerId string `json:"playerId"`
|
||||||
PlayerUID string `json:"playerUID"`
|
PlayerUID string `json:"playerUID"`
|
||||||
@@ -162,6 +290,7 @@ type AttendanceLogItem struct {
|
|||||||
SteamName string `json:"steamName"`
|
SteamName string `json:"steamName"`
|
||||||
IsJIP bool `json:"isJIP"`
|
IsJIP bool `json:"isJIP"`
|
||||||
RoleDescription string `json:"roleDescription"`
|
RoleDescription string `json:"roleDescription"`
|
||||||
|
MissionHash string `json:"missionHash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeAttendance(data string) {
|
func writeAttendance(data string) {
|
||||||
@@ -175,14 +304,6 @@ func writeAttendance(data string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get MySQL friendly datetime
|
|
||||||
// first, convert string to int
|
|
||||||
missionStartTime, err := strconv.ParseInt(event.MissionStart, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t := time.Unix(0, missionStartTime).Format("2006-01-02 15:04:05")
|
|
||||||
// get MySQL friendly NOW
|
// get MySQL friendly NOW
|
||||||
now := time.Now().Format("2006-01-02 15:04:05")
|
now := time.Now().Format("2006-01-02 15:04:05")
|
||||||
|
|
||||||
@@ -194,16 +315,8 @@ func writeAttendance(data string) {
|
|||||||
|
|
||||||
// send to DB
|
// send to DB
|
||||||
|
|
||||||
result, err := db.ExecContext(context.Background(), `INSERT INTO AttendanceLog (timestamp, mission_name, briefing_name, mission_name_source, on_load_name, author, server_name, server_profile, mission_start, event_type, player_id, player_uid, profile_name, steam_name, is_jip, role_description) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
result, err := db.ExecContext(context.Background(), `INSERT INTO AttendanceLog (timestamp, event_type, player_id, player_uid, profile_name, steam_name, is_jip, role_description, mission_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
now,
|
now,
|
||||||
event.MissionName,
|
|
||||||
event.BriefingName,
|
|
||||||
event.MissionNameSource,
|
|
||||||
event.OnLoadName,
|
|
||||||
event.Author,
|
|
||||||
event.ServerName,
|
|
||||||
event.ServerProfile,
|
|
||||||
t,
|
|
||||||
event.EventType,
|
event.EventType,
|
||||||
event.PlayerId,
|
event.PlayerId,
|
||||||
event.PlayerUID,
|
event.PlayerUID,
|
||||||
@@ -211,6 +324,7 @@ func writeAttendance(data string) {
|
|||||||
event.SteamName,
|
event.SteamName,
|
||||||
event.IsJIP,
|
event.IsJIP,
|
||||||
event.RoleDescription,
|
event.RoleDescription,
|
||||||
|
event.MissionHash,
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -262,6 +376,14 @@ func goRVExtensionArgs(output *C.char, outputsize C.size_t, input *C.char, argv
|
|||||||
go writeAttendance(out[0])
|
go writeAttendance(out[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case "logMission":
|
||||||
|
if argc == 1 {
|
||||||
|
go writeMissionInfo(out[0])
|
||||||
|
}
|
||||||
|
case "logWorld":
|
||||||
|
if argc == 1 {
|
||||||
|
go writeWorldInfo(out[0])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a result to Arma
|
// Return a result to Arma
|
||||||
|
|||||||
Reference in New Issue
Block a user