mirror of
https://github.com/indig0fox/Arma3-AttendanceTracker.git/
synced 2025-12-08 09:51:47 -06:00
bug fixes, 3 table split, update readme
This commit is contained in:
@@ -1,2 +0,0 @@
|
|||||||
params [["_value", "", [""]]];
|
|
||||||
("AttendanceTracker" callExtension ["getMissionHash", _value]) select 0;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
(parseSimpleArray ("AttendanceTracker" callExtension "getTimestamp")) select 0;
|
|
||||||
BIN
@AttendanceTracker.7z
Normal file
BIN
@AttendanceTracker.7z
Normal file
Binary file not shown.
Binary file not shown.
@@ -43,16 +43,25 @@ addMissionEventHandler ["ExtensionCallback", {
|
|||||||
missionNamespace setVariable ["AttendanceTracker_DBConnected", true];
|
missionNamespace setVariable ["AttendanceTracker_DBConnected", true];
|
||||||
|
|
||||||
// log mission info and get back the row Id to send with future messages
|
// log mission info and get back the row Id to send with future messages
|
||||||
private _response = "AttendanceTracker" callExtension ["logMission", [
|
private _response = "AttendanceTracker" callExtension [
|
||||||
[AttendanceTracker getVariable ["missionContext", createHashMap]] call CBA_fnc_encodeJSON
|
"logMission",
|
||||||
]];
|
[
|
||||||
AttendanceTracker_missionId = parseNumber _response;
|
[AttendanceTracker getVariable ["missionContext", createHashMap]] call CBA_fnc_encodeJSON
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
// log world info
|
// log world info
|
||||||
private _response = "AttendanceTracker" callExtension ["logWorld", [
|
private _response = "AttendanceTracker" callExtension [
|
||||||
[call attendanceTracker_fnc_getWorldInfo] call CBA_fnc_encodeJSON
|
"logWorld",
|
||||||
]];
|
[
|
||||||
|
[(call attendanceTracker_fnc_getWorldInfo)] call CBA_fnc_encodeJSON
|
||||||
|
]
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
case "writeMissionInfo": {
|
||||||
|
if (_response#0 == "MISSION_ID") then {
|
||||||
|
AttendanceTracker_missionId = parseNumber _response;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
case "writeAttendance": {
|
case "writeAttendance": {
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
(parseSimpleArray ("AttendanceTracker" callExtension "getMissionHash")) select 0;
|
||||||
@@ -14,7 +14,7 @@ _workshopID = '';
|
|||||||
} foreach getLoadedModsInfo;
|
} foreach getLoadedModsInfo;
|
||||||
|
|
||||||
// [_name, _author, _workshopID];
|
// [_name, _author, _workshopID];
|
||||||
[
|
_return = createHashMapFromArray [
|
||||||
["author", _author],
|
["author", _author],
|
||||||
["workshopID", _workshopID],
|
["workshopID", _workshopID],
|
||||||
["displayName", _name],
|
["displayName", _name],
|
||||||
@@ -24,3 +24,5 @@ _workshopID = '';
|
|||||||
["latitude", getNumber( _world >> "latitude" )],
|
["latitude", getNumber( _world >> "latitude" )],
|
||||||
["longitude", getNumber( _world >> "longitude" )]
|
["longitude", getNumber( _world >> "longitude" )]
|
||||||
];
|
];
|
||||||
|
diag_log format ["Attendance Tracker: WorldInfo is: %1", _return];
|
||||||
|
_return
|
||||||
@@ -2,7 +2,9 @@
|
|||||||
AttendanceTracker = false call CBA_fnc_createNamespace;
|
AttendanceTracker = false call CBA_fnc_createNamespace;
|
||||||
|
|
||||||
AttendanceTracker_missionStartTimestamp = call attendanceTracker_fnc_timestamp;
|
AttendanceTracker_missionStartTimestamp = call attendanceTracker_fnc_timestamp;
|
||||||
AttendanceTracker_missionHash = [AttendanceTracker_missionStartTimestamp] call attendanceTracker_fnc_getMissionHash;
|
diag_log format ["AttendanceTracker: Mission started at %1", AttendanceTracker_missionStartTimestamp];
|
||||||
|
AttendanceTracker_missionHash = call attendanceTracker_fnc_getMissionHash;
|
||||||
|
diag_log format ["AttendanceTracker: Mission hash is %1", AttendanceTracker_missionHash];
|
||||||
|
|
||||||
AttendanceTracker setVariable ["missionContext", createHashMapFromArray [
|
AttendanceTracker setVariable ["missionContext", createHashMapFromArray [
|
||||||
["missionName", missionName],
|
["missionName", missionName],
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// (parseSimpleArray ("AttendanceTracker" callExtension "getTimestamp")) select 0;
|
||||||
|
|
||||||
|
// need date for MySQL in format 2006-01-02 15:04:05
|
||||||
|
|
||||||
|
systemTimeUTC params [
|
||||||
|
"_year",
|
||||||
|
"_month",
|
||||||
|
"_day",
|
||||||
|
"_hour",
|
||||||
|
"_minute",
|
||||||
|
"_second"
|
||||||
|
"_millisecond"
|
||||||
|
];
|
||||||
|
|
||||||
|
format[
|
||||||
|
"%1-%2-%3 %4:%5:%6",
|
||||||
|
_year,
|
||||||
|
_month,
|
||||||
|
_day,
|
||||||
|
_hour,
|
||||||
|
_minute,
|
||||||
|
_second
|
||||||
|
];
|
||||||
|
|
||||||
7
@AttendanceTracker/config.json
Normal file
7
@AttendanceTracker/config.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"mysqlHost": "127.0.0.1",
|
||||||
|
"mysqlPort": 12730,
|
||||||
|
"mysqlUser": "root",
|
||||||
|
"mysqlPassword": "i&Lz8A3RuPcY5b326ALXgjl",
|
||||||
|
"mysqlDatabase": "testdb"
|
||||||
|
}
|
||||||
93
README.md
93
README.md
@@ -9,58 +9,65 @@ Create a database with a name of your choosing. Then, run the following SQL comm
|
|||||||
```sql
|
```sql
|
||||||
-- a3server.attendancelog definition
|
-- a3server.attendancelog definition
|
||||||
|
|
||||||
CREATE TABLE `attendancelog` (
|
CREATE TABLE `attendance` (
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
`timestamp` datetime NOT NULL,
|
`join_time` DATETIME NULL DEFAULT NULL,
|
||||||
`event_hash` varchar(100) NOT NULL,
|
`disconnect_time` DATETIME NULL DEFAULT NULL,
|
||||||
`event_type` varchar(100) NOT NULL,
|
`mission_hash` VARCHAR(100) NULL DEFAULT '' COLLATE 'utf8mb3_general_ci',
|
||||||
`player_id` varchar(30) NOT NULL,
|
`event_type` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`player_uid` varchar(100) NOT NULL,
|
`player_id` VARCHAR(30) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`profile_name` varchar(100) NOT NULL,
|
`player_uid` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`steam_name` varchar(100) DEFAULT NULL,
|
`profile_name` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`is_jip` tinyint(4) DEFAULT NULL,
|
`steam_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`role_description` varchar(100) DEFAULT NULL,
|
`is_jip` TINYINT(4) NULL DEFAULT NULL,
|
||||||
`mission_start` datetime NOT NULL,
|
`role_description` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`mission_name` varchar(100) DEFAULT NULL,
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
`briefing_name` varchar(100) DEFAULT NULL,
|
)
|
||||||
`mission_name_source` varchar(100) DEFAULT NULL,
|
COLLATE='utf8mb3_general_ci'
|
||||||
`on_load_name` varchar(100) DEFAULT NULL,
|
ENGINE=InnoDB
|
||||||
`author` varchar(100) DEFAULT NULL,
|
AUTO_INCREMENT=5868
|
||||||
`server_name` varchar(100) NOT NULL,
|
;
|
||||||
`server_profile` varchar(100) NOT NULL,
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb3;
|
|
||||||
|
|
||||||
-- a3server.`missions` definition
|
-- a3server.`missions` definition
|
||||||
|
|
||||||
CREATE TABLE `missions` (
|
CREATE TABLE `missions` (
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
`mission_name` varchar(100) NOT NULL,
|
`mission_name` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`mission_name_source` varchar(100) DEFAULT NULL,
|
`mission_name_source` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`briefing_name` varchar(100) DEFAULT NULL,
|
`briefing_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`on_load_name` varchar(100) DEFAULT NULL,
|
`on_load_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`author` varchar(100) DEFAULT NULL,
|
`author` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`server_name` varchar(100) DEFAULT NULL,
|
`server_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`server_profile` varchar(100) DEFAULT NULL,
|
`server_profile` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`mission_start` datetime DEFAULT NULL,
|
`mission_start` DATETIME NULL DEFAULT NULL,
|
||||||
`mission_hash` varchar(100) DEFAULT NULL,
|
`mission_hash` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb3;
|
)
|
||||||
|
COLLATE='utf8mb3_general_ci'
|
||||||
|
ENGINE=InnoDB
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- a3server.`worlds` definition
|
-- a3server.`worlds` definition
|
||||||
|
|
||||||
CREATE TABLE `worlds` (
|
CREATE TABLE `worlds` (
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
`author` varchar(100) DEFAULT NULL,
|
`author` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`display_name` varchar(100) DEFAULT NULL,
|
`display_name` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`world_name` varchar(100) NOT NULL,
|
`world_name` VARCHAR(100) NOT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`world_name_original` varchar(100) DEFAULT NULL,
|
`world_name_original` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
`world_size` int(11) DEFAULT NULL,
|
`world_size` INT(11) NULL DEFAULT NULL,
|
||||||
`latitude` float DEFAULT NULL,
|
`latitude` FLOAT NULL DEFAULT NULL,
|
||||||
`longitude` float DEFAULT NULL,
|
`longitude` FLOAT NULL DEFAULT NULL,
|
||||||
PRIMARY KEY (`id`)
|
`workshop_id` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb3_general_ci',
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb3;
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
|
)
|
||||||
|
COLLATE='utf8mb3_general_ci'
|
||||||
|
ENGINE=InnoDB
|
||||||
|
AUTO_INCREMENT=2
|
||||||
|
;
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -16,6 +16,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -108,11 +110,11 @@ func loadConfig() {
|
|||||||
writeLog(functionName, `["Config loaded", "INFO"]`)
|
writeLog(functionName, `["Config loaded", "INFO"]`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMissionHash(time string) string {
|
func getMissionHash() string {
|
||||||
functionName := "getMissionHash"
|
functionName := "getMissionHash"
|
||||||
// get md5 hash of string
|
// get md5 hash of string
|
||||||
// https://stackoverflow.com/questions/2377881/how-to-get-a-md5-hash-from-a-string-in-golang
|
// https://stackoverflow.com/questions/2377881/how-to-get-a-md5-hash-from-a-string-in-golang
|
||||||
hash := md5.Sum([]byte(time))
|
hash := md5.Sum([]byte(time.Now().Format("2006-01-02 15:04:05")))
|
||||||
|
|
||||||
// convert to string
|
// convert to string
|
||||||
hashString := fmt.Sprintf("%x", hash)
|
hashString := fmt.Sprintf("%x", hash)
|
||||||
@@ -176,9 +178,11 @@ type WorldInfo struct {
|
|||||||
|
|
||||||
func writeWorldInfo(worldInfo string) {
|
func writeWorldInfo(worldInfo string) {
|
||||||
functionName := "writeWorldInfo"
|
functionName := "writeWorldInfo"
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, worldInfo))
|
||||||
// worldInfo is json, parse it
|
// worldInfo is json, parse it
|
||||||
var wi WorldInfo
|
var wi WorldInfo
|
||||||
err := json.Unmarshal([]byte(worldInfo), &wi)
|
fixedString := fixEscapeQuotes(trimQuotes(worldInfo))
|
||||||
|
err := json.Unmarshal([]byte(fixedString), &wi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
@@ -256,9 +260,11 @@ type MissionInfo struct {
|
|||||||
|
|
||||||
func writeMissionInfo(missionInfo string) {
|
func writeMissionInfo(missionInfo string) {
|
||||||
functionName := "writeMissionInfo"
|
functionName := "writeMissionInfo"
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, missionInfo))
|
||||||
// missionInfo is json, parse it
|
// missionInfo is json, parse it
|
||||||
var mi MissionInfo
|
var mi MissionInfo
|
||||||
err := json.Unmarshal([]byte(missionInfo), &mi)
|
fixedString := fixEscapeQuotes(trimQuotes(missionInfo))
|
||||||
|
err := json.Unmarshal([]byte(fixedString), &mi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
@@ -286,7 +292,7 @@ func writeMissionInfo(missionInfo string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
defer stmt.Close()
|
||||||
res, err := stmt.Exec(mi.MissionName, mi.BriefingName, mi.MissionNameSource, mi.OnLoadName, mi.Author, mi.ServerName, mi.ServerProfile, t, mi.MissionStart, mi.MissionHash)
|
res, err := stmt.Exec(mi.MissionName, mi.BriefingName, mi.MissionNameSource, mi.OnLoadName, mi.Author, mi.ServerName, mi.ServerProfile, mi.MissionStart, mi.MissionHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
@@ -297,6 +303,7 @@ func writeMissionInfo(missionInfo string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
writeLog(functionName, fmt.Sprintf(`["Mission inserted with ID %d", "INFO"]`, lastID))
|
writeLog(functionName, fmt.Sprintf(`["Mission inserted with ID %d", "INFO"]`, lastID))
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["MISSION_ID", "%d"]`, lastID))
|
||||||
}
|
}
|
||||||
|
|
||||||
type AttendanceLogItem struct {
|
type AttendanceLogItem struct {
|
||||||
@@ -332,13 +339,11 @@ func writeAttendance(data string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// send to DB
|
// send to DB
|
||||||
|
|
||||||
result, err := db.ExecContext(
|
result, err := db.ExecContext(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
fmt.Sprintf(
|
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 (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
`INSERT INTO %s (join_time, event_type, player_id, player_uid, profile_name, steam_name, is_jip, role_description, mission_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
ATTENDANCE_TABLE,
|
ATTENDANCE_TABLE,
|
||||||
),
|
),
|
||||||
now,
|
now,
|
||||||
@@ -364,7 +369,7 @@ func writeAttendance(data string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["Saved attendance for %s to row id %d", "INFO"]`, event.ProfileName, id))
|
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))
|
writeLog(functionName, fmt.Sprintf(`["ATT_LOG", ["%s", "%d"]]`, event.PlayerId, id))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,12 +474,6 @@ func goRVExtensionArgs(output *C.char, outputsize C.size_t, input *C.char, argv
|
|||||||
if argc == 1 {
|
if argc == 1 {
|
||||||
go writeWorldInfo(out[0])
|
go writeWorldInfo(out[0])
|
||||||
}
|
}
|
||||||
case "getMissionHash":
|
|
||||||
{
|
|
||||||
if argc == 1 {
|
|
||||||
go getMissionHash(out[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a result to Arma
|
// Return a result to Arma
|
||||||
@@ -502,9 +501,10 @@ func callBackExample() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTimestamp() int64 {
|
func getTimestamp() string {
|
||||||
// get the current unix timestamp in nanoseconds
|
// get the current unix timestamp in nanoseconds
|
||||||
return time.Now().UnixNano()
|
// return time.Now().Local().Unix()
|
||||||
|
return time.Now().Format("2006-01-02 15:04:05")
|
||||||
}
|
}
|
||||||
|
|
||||||
func trimQuotes(s string) string {
|
func trimQuotes(s string) string {
|
||||||
@@ -526,6 +526,9 @@ func writeLog(functionName string, data string) {
|
|||||||
defer C.free(unsafe.Pointer(statusParam))
|
defer C.free(unsafe.Pointer(statusParam))
|
||||||
runExtensionCallback(statusName, statusFunction, statusParam)
|
runExtensionCallback(statusName, statusFunction, statusParam)
|
||||||
|
|
||||||
|
// get calling function & line
|
||||||
|
_, file, line, _ := runtime.Caller(1)
|
||||||
|
log.Printf(`%s:%d: %s`, path.Base(file), line, data)
|
||||||
log.Printf(`%s: %s`, functionName, data)
|
log.Printf(`%s: %s`, functionName, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,12 +545,12 @@ func goRVExtension(output *C.char, outputsize C.size_t, input *C.char) {
|
|||||||
case "getDir":
|
case "getDir":
|
||||||
temp = getDir()
|
temp = getDir()
|
||||||
case "getTimestamp":
|
case "getTimestamp":
|
||||||
time := getTimestamp()
|
temp = fmt.Sprintf(`["%s"]`, getTimestamp())
|
||||||
temp = fmt.Sprintf(`["%s"]`, strconv.FormatInt(time, 10))
|
|
||||||
case "connectDB":
|
case "connectDB":
|
||||||
go connectDB()
|
go connectDB()
|
||||||
temp = fmt.Sprintf(`["%s"]`, "Connecting to DB")
|
temp = fmt.Sprintf(`["%s"]`, "Connecting to DB")
|
||||||
|
case "getMissionHash":
|
||||||
|
temp = fmt.Sprintf(`["%s"]`, getMissionHash())
|
||||||
default:
|
default:
|
||||||
temp = fmt.Sprintf(`["%s"]`, "Unknown Function")
|
temp = fmt.Sprintf(`["%s"]`, "Unknown Function")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user