mirror of
https://github.com/indig0fox/Arma3-AttendanceTracker.git/
synced 2025-12-08 09:51:47 -06:00
intro
This commit is contained in:
BIN
@AttendanceTracker/AttendanceTracker_x64.dll
Normal file
BIN
@AttendanceTracker/AttendanceTracker_x64.dll
Normal file
Binary file not shown.
86
@AttendanceTracker/AttendanceTracker_x64.h
Normal file
86
@AttendanceTracker/AttendanceTracker_x64.h
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/* Code generated by cmd/cgo; DO NOT EDIT. */
|
||||||
|
|
||||||
|
/* package main.go */
|
||||||
|
|
||||||
|
|
||||||
|
#line 1 "cgo-builtin-export-prolog"
|
||||||
|
|
||||||
|
#include <stddef.h> /* for ptrdiff_t below */
|
||||||
|
|
||||||
|
#ifndef GO_CGO_EXPORT_PROLOGUE_H
|
||||||
|
#define GO_CGO_EXPORT_PROLOGUE_H
|
||||||
|
|
||||||
|
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||||
|
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Start of preamble from import "C" comments. */
|
||||||
|
|
||||||
|
|
||||||
|
#line 3 "main.go"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "extensionCallback.h"
|
||||||
|
|
||||||
|
#line 1 "cgo-generated-wrapper"
|
||||||
|
|
||||||
|
|
||||||
|
/* End of preamble from import "C" comments. */
|
||||||
|
|
||||||
|
|
||||||
|
/* Start of boilerplate cgo prologue. */
|
||||||
|
#line 1 "cgo-gcc-export-header-prolog"
|
||||||
|
|
||||||
|
#ifndef GO_CGO_PROLOGUE_H
|
||||||
|
#define GO_CGO_PROLOGUE_H
|
||||||
|
|
||||||
|
typedef signed char GoInt8;
|
||||||
|
typedef unsigned char GoUint8;
|
||||||
|
typedef short GoInt16;
|
||||||
|
typedef unsigned short GoUint16;
|
||||||
|
typedef int GoInt32;
|
||||||
|
typedef unsigned int GoUint32;
|
||||||
|
typedef long long GoInt64;
|
||||||
|
typedef unsigned long long GoUint64;
|
||||||
|
typedef GoInt64 GoInt;
|
||||||
|
typedef GoUint64 GoUint;
|
||||||
|
typedef __SIZE_TYPE__ GoUintptr;
|
||||||
|
typedef float GoFloat32;
|
||||||
|
typedef double GoFloat64;
|
||||||
|
typedef float _Complex GoComplex64;
|
||||||
|
typedef double _Complex GoComplex128;
|
||||||
|
|
||||||
|
/*
|
||||||
|
static assertion to make sure the file is being used on architecture
|
||||||
|
at least with matching size of GoInt.
|
||||||
|
*/
|
||||||
|
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
|
||||||
|
|
||||||
|
#ifndef GO_CGO_GOSTRING_TYPEDEF
|
||||||
|
typedef _GoString_ GoString;
|
||||||
|
#endif
|
||||||
|
typedef void *GoMap;
|
||||||
|
typedef void *GoChan;
|
||||||
|
typedef struct { void *t; void *v; } GoInterface;
|
||||||
|
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* End of boilerplate cgo prologue. */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern __declspec(dllexport) void goRVExtensionVersion(char* output, size_t outputsize);
|
||||||
|
extern __declspec(dllexport) void goRVExtensionArgs(char* output, size_t outputsize, char* input, char** argv, int argc);
|
||||||
|
extern __declspec(dllexport) void goRVExtension(char* output, size_t outputsize, char* input);
|
||||||
|
extern __declspec(dllexport) void goRVExtensionRegisterCallback(extensionCallback fnc);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
7
extension/@AttendanceTracker/config.json
Normal file
7
extension/@AttendanceTracker/config.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"mysqlHost": "127.0.0.1",
|
||||||
|
"mysqlPort": 3306,
|
||||||
|
"mysqlUser": "root",
|
||||||
|
"mysqlPassword": "root",
|
||||||
|
"mysqlDatabase": "testgorm"
|
||||||
|
}
|
||||||
@@ -2,4 +2,8 @@ module main.go
|
|||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require github.com/go-sql-driver/mysql v1.6.0
|
require (
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0
|
||||||
|
gorm.io/driver/mysql v1.5.1 // indirect
|
||||||
|
gorm.io/gorm v1.25.2 // indirect
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,2 +1,13 @@
|
|||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
|
||||||
|
gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
|
||||||
|
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
|
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
|
||||||
|
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ package main
|
|||||||
import "C" // This is required to import the C code
|
import "C" // This is required to import the C code
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"database/sql"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@@ -23,6 +21,8 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var EXTENSION_VERSION string = "0.0.1"
|
var EXTENSION_VERSION string = "0.0.1"
|
||||||
@@ -50,7 +50,7 @@ type AttendanceTrackerConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// database connection
|
// database connection
|
||||||
var db *sql.DB
|
var db *gorm.DB
|
||||||
|
|
||||||
// configure log output
|
// configure log output
|
||||||
func init() {
|
func init() {
|
||||||
@@ -74,7 +74,8 @@ func version() {
|
|||||||
func getDir() string {
|
func getDir() string {
|
||||||
dir, err := os.Getwd()
|
dir, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
writeLog("getDir", fmt.Sprintf(`["Error getting working directory: %v", "ERROR"]`, err))
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
return dir
|
return dir
|
||||||
}
|
}
|
||||||
@@ -95,7 +96,7 @@ func loadConfig() {
|
|||||||
// LOG_FILE = ADDON_FOLDER + "\\attendanceTracker.log"
|
// LOG_FILE = ADDON_FOLDER + "\\attendanceTracker.log"
|
||||||
// CONFIG_FILE = ADDON_FOLDER + "\\config.json"
|
// CONFIG_FILE = ADDON_FOLDER + "\\config.json"
|
||||||
|
|
||||||
file, err := os.Open(CONFIG_FILE)
|
file, err := os.OpenFile(CONFIG_FILE, os.O_RDONLY|os.O_CREATE, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
@@ -123,50 +124,41 @@ func getMissionHash() string {
|
|||||||
return hashString
|
return hashString
|
||||||
}
|
}
|
||||||
|
|
||||||
func connectDB() string {
|
func connectDB() error {
|
||||||
functionName := "connectDB"
|
|
||||||
var err error
|
var err error
|
||||||
|
dsn := fmt.Sprintf(
|
||||||
|
"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||||
|
ATConfig.MySQLUser,
|
||||||
|
ATConfig.MySQLPassword,
|
||||||
|
ATConfig.MySQLHost,
|
||||||
|
ATConfig.MySQLPort,
|
||||||
|
ATConfig.MySQLDatabase,
|
||||||
|
)
|
||||||
|
|
||||||
// load config
|
// log dsn and pause
|
||||||
loadConfig()
|
writeLog("connectDB", fmt.Sprintf(`["DSN: %s", "INFO"]`, dsn))
|
||||||
|
var input string
|
||||||
|
fmt.Scanln(&input)
|
||||||
|
|
||||||
// connect to database
|
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||||
connectionString := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", ATConfig.MySQLUser, ATConfig.MySQLPassword, ATConfig.MySQLHost, ATConfig.MySQLPort, ATConfig.MySQLDatabase)
|
|
||||||
|
|
||||||
db, err = sql.Open("mysql", connectionString)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog("connectDB", fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return "ERROR"
|
return err
|
||||||
}
|
|
||||||
if db == nil {
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, "db is nil"))
|
|
||||||
return "ERROR"
|
|
||||||
}
|
|
||||||
// defer db.Close()
|
|
||||||
|
|
||||||
db.SetConnMaxLifetime(time.Minute * 3)
|
|
||||||
db.SetMaxOpenConns(10)
|
|
||||||
db.SetMaxIdleConns(10)
|
|
||||||
|
|
||||||
pingErr := db.Ping()
|
|
||||||
if pingErr != nil {
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, pingErr))
|
|
||||||
return "ERROR"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the server version
|
// Migrate the schema
|
||||||
var version string
|
err = db.AutoMigrate(&World{}, &Mission{}, &AttendanceItem{})
|
||||||
err = db.QueryRow("SELECT VERSION()").Scan(&version)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog("connectDB", fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return "ERROR"
|
return err
|
||||||
}
|
}
|
||||||
writeLog(functionName, fmt.Sprintf(`["Connected to MySQL/MariaDB version %s", "INFO"]`, version))
|
|
||||||
writeLog(functionName, `["SUCCESS", "INFO"]`)
|
writeLog("connectDB", `["Database connected", "INFO"]`)
|
||||||
return version
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type WorldInfo struct {
|
type World struct {
|
||||||
|
gorm.Model
|
||||||
Author string `json:"author"`
|
Author string `json:"author"`
|
||||||
WorkshopID string `json:"workshopID"`
|
WorkshopID string `json:"workshopID"`
|
||||||
DisplayName string `json:"displayName"`
|
DisplayName string `json:"displayName"`
|
||||||
@@ -175,79 +167,45 @@ type WorldInfo struct {
|
|||||||
WorldSize int `json:"worldSize"`
|
WorldSize int `json:"worldSize"`
|
||||||
Latitude float32 `json:"latitude"`
|
Latitude float32 `json:"latitude"`
|
||||||
Longitude float32 `json:"longitude"`
|
Longitude float32 `json:"longitude"`
|
||||||
|
Missions []Mission
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeWorldInfo(worldInfo string) {
|
func writeWorldInfo(worldInfo string) {
|
||||||
functionName := "writeWorldInfo"
|
functionName := "writeWorldInfo"
|
||||||
// writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, worldInfo))
|
// writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, worldInfo))
|
||||||
// worldInfo is json, parse it
|
// worldInfo is json, parse it
|
||||||
var wi WorldInfo
|
var wi World
|
||||||
fixedString := fixEscapeQuotes(trimQuotes(worldInfo))
|
fixedString := fixEscapeQuotes(trimQuotes(worldInfo))
|
||||||
err := json.Unmarshal([]byte(fixedString), &wi)
|
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
|
||||||
}
|
}
|
||||||
// write to log as json
|
|
||||||
// writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, json.Marshal(wi)))
|
|
||||||
|
|
||||||
// write to database
|
// prevent crash
|
||||||
// check if world exists
|
if db == nil {
|
||||||
var worldID int
|
connectDB()
|
||||||
err = db.QueryRow("SELECT id FROM worlds WHERE world_name = ?", wi.WorldName).Scan(&worldID)
|
}
|
||||||
if err != nil {
|
|
||||||
if err == sql.ErrNoRows {
|
// write world if not exist
|
||||||
// world does not exist, insert it
|
var world World
|
||||||
stmt, err := db.Prepare(fmt.Sprintf(
|
db.Where("world_name = ?", wi.WorldName).First(&world)
|
||||||
"INSERT INTO %s (author, workshop_id, display_name, world_name, world_name_original, world_size, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
if world.ID == 0 {
|
||||||
WORLDS_TABLE,
|
writeLog(functionName, `["World not found, writing new world", "INFO"]`)
|
||||||
))
|
result := db.Create(&wi)
|
||||||
if err != nil {
|
if result.Error != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, result.Error))
|
||||||
return
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
res, err := stmt.Exec(wi.Author, wi.WorkshopID, wi.DisplayName, wi.WorldName, wi.WorldNameOriginal, wi.WorldSize, wi.Latitude, wi.Longitude)
|
|
||||||
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
|
return
|
||||||
}
|
}
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["World written with ID %d", "INFO"]`, wi.ID))
|
||||||
} else {
|
} else {
|
||||||
// world exists, update it
|
// return ID
|
||||||
stmt, err := db.Prepare(fmt.Sprintf(
|
writeLog(functionName, fmt.Sprintf(`["World exists with ID %d", "INFO"]`, world.ID))
|
||||||
"UPDATE %s SET author = ?, workshop_id = ?, display_name = ?, world_name = ?, world_name_original = ?, world_size = ?, latitude = ?, longitude = ? WHERE id = ?",
|
|
||||||
WORLDS_TABLE,
|
|
||||||
))
|
|
||||||
if err != nil {
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
res, err := stmt.Exec(wi.Author, wi.WorkshopID, wi.DisplayName, wi.WorldName, wi.WorldNameOriginal, wi.WorldSize, wi.Latitude, wi.Longitude, 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 {
|
type Mission struct {
|
||||||
|
gorm.Model
|
||||||
MissionName string `json:"missionName"`
|
MissionName string `json:"missionName"`
|
||||||
BriefingName string `json:"briefingName"`
|
BriefingName string `json:"briefingName"`
|
||||||
MissionNameSource string `json:"missionNameSource"`
|
MissionNameSource string `json:"missionNameSource"`
|
||||||
@@ -257,85 +215,47 @@ type MissionInfo struct {
|
|||||||
ServerProfile string `json:"serverProfile"`
|
ServerProfile string `json:"serverProfile"`
|
||||||
MissionStart string `json:"missionStart"`
|
MissionStart string `json:"missionStart"`
|
||||||
MissionHash string `json:"missionHash"`
|
MissionHash string `json:"missionHash"`
|
||||||
WorldName string `json:"worldName"`
|
WorldName string `json:"worldName" gorm:"-"`
|
||||||
|
WorldID int
|
||||||
|
World World `gorm:"foreignkey:WorldID"`
|
||||||
|
Attendees []AttendanceItem
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeMissionInfo(missionInfo string) {
|
func writeMission(missionJSON string) {
|
||||||
functionName := "writeMissionInfo"
|
functionName := "writeMission"
|
||||||
var err error
|
var err error
|
||||||
// writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, missionInfo))
|
// writeLog(functionName, fmt.Sprintf(`["%s", "DEBUG"]`, Mission))
|
||||||
// missionInfo is json, parse it
|
// Mission is json, parse it
|
||||||
var mi MissionInfo
|
var mi Mission
|
||||||
fixedString := fixEscapeQuotes(trimQuotes(missionInfo))
|
fixedString := fixEscapeQuotes(trimQuotes(missionJSON))
|
||||||
err = json.Unmarshal([]byte(fixedString), &mi)
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if mission exists based on hash
|
// write mission to database
|
||||||
var worldID int
|
db.Create(&mi)
|
||||||
err = db.QueryRow("SELECT id FROM worlds WHERE world_name = ?", mi.WorldName).Scan(&worldID)
|
if db.Error != nil {
|
||||||
if err != nil {
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, db.Error))
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["Mission written with ID %d", "INFO"]`, mi.ID))
|
||||||
var stmt *sql.Stmt
|
|
||||||
var res sql.Result
|
|
||||||
|
|
||||||
if worldID != 0 {
|
|
||||||
sqlWorld := fmt.Sprintf(
|
|
||||||
"INSERT INTO %s (mission_name, briefing_name, mission_name_source, on_load_name, author, server_name, server_profile, mission_start, mission_hash, world_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
||||||
MISSIONS_TABLE,
|
|
||||||
)
|
|
||||||
stmt, err = db.Prepare(sqlWorld)
|
|
||||||
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, mi.MissionStart, mi.MissionHash, worldID)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// if no world was found, write without it
|
|
||||||
sqlNoWorld := fmt.Sprintf(
|
|
||||||
"INSERT INTO %s (mission_name, briefing_name, mission_name_source, on_load_name, author, server_name, server_profile, mission_start, mission_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
||||||
MISSIONS_TABLE,
|
|
||||||
)
|
|
||||||
stmt, err = db.Prepare(sqlNoWorld)
|
|
||||||
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, mi.MissionStart, 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))
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["MISSION_ID", "%d"]`, lastID))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AttendanceLogItem struct {
|
type AttendanceItem struct {
|
||||||
|
gorm.Model
|
||||||
|
MissionHash string `json:"missionHash"`
|
||||||
EventType string `json:"eventType"`
|
EventType string `json:"eventType"`
|
||||||
PlayerId string `json:"playerId"`
|
PlayerId string `json:"playerId"`
|
||||||
PlayerUID string `json:"playerUID"`
|
PlayerUID string `json:"playerUID"`
|
||||||
|
JoinTime time.Time
|
||||||
|
DisconnectTime time.Time
|
||||||
ProfileName string `json:"profileName"`
|
ProfileName string `json:"profileName"`
|
||||||
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"`
|
MissionID int
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeAttendance(data string) {
|
func writeAttendance(data string) {
|
||||||
@@ -343,192 +263,74 @@ func writeAttendance(data string) {
|
|||||||
var err error
|
var err error
|
||||||
// data is json, parse it
|
// data is json, parse it
|
||||||
stringjson := fixEscapeQuotes(trimQuotes(data))
|
stringjson := fixEscapeQuotes(trimQuotes(data))
|
||||||
var event AttendanceLogItem
|
var event AttendanceItem
|
||||||
err = json.Unmarshal([]byte(stringjson), &event)
|
err = json.Unmarshal([]byte(stringjson), &event)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get MySQL friendly NOW
|
|
||||||
now := time.Now().UTC().Format("2006-01-02 15:04:05")
|
|
||||||
|
|
||||||
// prevent crash
|
// prevent crash
|
||||||
if db == nil {
|
if db == nil {
|
||||||
writeLog(functionName, `["db is nil", "ERROR"]`)
|
connectDB()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// send to DB
|
var playerUid string
|
||||||
var result sql.Result
|
var rowId uint
|
||||||
|
|
||||||
if event.EventType == "Server" {
|
if event.EventType == "Server" {
|
||||||
sql := fmt.Sprintf(
|
// check for most recent existing attendance row
|
||||||
`INSERT INTO %s (join_time, event_type, player_id, player_uid, profile_name, steam_name, is_jip, role_description) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
var attendance AttendanceItem
|
||||||
ATTENDANCE_TABLE,
|
db.Where("player_uid = ? AND event_type = ?", event.PlayerId, "Server").Last(&attendance)
|
||||||
)
|
if attendance.ID != 0 {
|
||||||
result, err = db.ExecContext(
|
// update disconnect time
|
||||||
context.Background(),
|
row := db.Model(&attendance).Update("disconnect_time", time.Now().UTC().Format("2006-01-02 15:04:05"))
|
||||||
sql,
|
if row.Error != nil {
|
||||||
now,
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, row.Error))
|
||||||
event.EventType,
|
return
|
||||||
event.PlayerId,
|
}
|
||||||
event.PlayerUID,
|
rowId, playerUid = attendance.ID, attendance.PlayerUID
|
||||||
event.ProfileName,
|
|
||||||
event.SteamName,
|
} else {
|
||||||
event.IsJIP,
|
// insert new row
|
||||||
event.RoleDescription,
|
row := db.Create(&event)
|
||||||
)
|
if row.Error != nil {
|
||||||
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, row.Error))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rowId, playerUid = event.ID, event.PlayerUID
|
||||||
|
}
|
||||||
} else if event.EventType == "Mission" {
|
} else if event.EventType == "Mission" {
|
||||||
sql := fmt.Sprintf(
|
// check for most recent join_time for this player within 6 hours without a disconnect_time
|
||||||
`INSERT INTO %s (join_time, event_type, player_id, player_uid, profile_name, steam_name, is_jip, role_description, mission_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
var attendance AttendanceItem
|
||||||
ATTENDANCE_TABLE,
|
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)
|
||||||
)
|
if attendance.ID != 0 {
|
||||||
result, err = db.ExecContext(
|
// update disconnect time
|
||||||
context.Background(),
|
row := db.Model(&attendance).Update("disconnect_time", time.Now().UTC().Format("2006-01-02 15:04:05"))
|
||||||
sql,
|
if row.Error != nil {
|
||||||
now,
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, row.Error))
|
||||||
event.EventType,
|
return
|
||||||
event.PlayerId,
|
}
|
||||||
event.PlayerUID,
|
rowId, playerUid = attendance.ID, attendance.PlayerUID
|
||||||
event.ProfileName,
|
} else {
|
||||||
event.SteamName,
|
// insert new row
|
||||||
event.IsJIP,
|
row := db.Create(&event)
|
||||||
event.RoleDescription,
|
if row.Error != nil {
|
||||||
event.MissionHash,
|
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, row.Error))
|
||||||
)
|
return
|
||||||
|
}
|
||||||
|
rowId, playerUid = event.ID, event.PlayerUID
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
writeLog(functionName, fmt.Sprintf(`["Saved attendance for %s to row id %d", "INFO"]`, playerUid, rowId))
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := result.LastInsertId()
|
|
||||||
if err != nil {
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["Saved attendance for %s to row id %d", "INFO"]`, event.ProfileName, id))
|
|
||||||
if event.EventType == "Server" {
|
if event.EventType == "Server" {
|
||||||
writeLog(functionName, fmt.Sprintf(`["ATT_LOG", ["SERVER", "%s", "%d"]]`, event.PlayerId, id))
|
writeLog(functionName, fmt.Sprintf(`["ATT_LOG", ["SERVER", "%s", "%d"]]`, playerUid, rowId))
|
||||||
} else if event.EventType == "Mission" {
|
} else if event.EventType == "Mission" {
|
||||||
writeLog(functionName, fmt.Sprintf(`["ATT_LOG", ["MISSION", "%s", "%d"]]`, event.PlayerId, id))
|
writeLog(functionName, fmt.Sprintf(`["ATT_LOG", ["MISSION", "%s", "%d"]]`, playerUid, rowId))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DisconnectItem struct {
|
|
||||||
EventType string `json:"eventType"`
|
|
||||||
PlayerId string `json:"playerId"`
|
|
||||||
MissionHash string `json:"missionHash"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeDisconnectEvent(data string) {
|
|
||||||
functionName := "writeDisconnectEvent"
|
|
||||||
// data is json, parse it
|
|
||||||
stringjson := fixEscapeQuotes(trimQuotes(data))
|
|
||||||
var event DisconnectItem
|
|
||||||
err := json.Unmarshal([]byte(stringjson), &event)
|
|
||||||
if err != nil {
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// get MySQL friendly NOW
|
|
||||||
now := time.Now().UTC().Format("2006-01-02 15:04:05")
|
|
||||||
|
|
||||||
// prevent crash
|
|
||||||
if db == nil {
|
|
||||||
writeLog(functionName, `["db is nil", "ERROR"]`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// first, check if a row exists for this player
|
|
||||||
var sql string
|
|
||||||
if event.EventType == "Mission" {
|
|
||||||
sql = fmt.Sprintf(
|
|
||||||
`
|
|
||||||
SELECT id FROM attendance
|
|
||||||
WHERE player_id = '%s' and event_type = '%s' and mission_hash = '%s' and disconnect_time IS NULL and join_time >= (NOW() - INTERVAL 24 hour)
|
|
||||||
ORDER BY join_time DESC
|
|
||||||
`,
|
|
||||||
event.PlayerId,
|
|
||||||
event.EventType,
|
|
||||||
event.MissionHash,
|
|
||||||
)
|
|
||||||
} else if event.EventType == "Server" {
|
|
||||||
sql = fmt.Sprintf(
|
|
||||||
`
|
|
||||||
SELECT id FROM attendance
|
|
||||||
WHERE player_id = '%s' and event_type = '%s' and disconnect_time IS NULL and join_time >= (NOW() - INTERVAL 24 hour)
|
|
||||||
ORDER BY join_time DESC
|
|
||||||
`,
|
|
||||||
event.PlayerId,
|
|
||||||
event.EventType,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["Unknown event type %s", "ERROR"]`, event.EventType))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := db.QueryContext(context.Background(), sql)
|
|
||||||
if err != nil {
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
// if there is a row, update it
|
|
||||||
if rows.Next() {
|
|
||||||
// create interface to hold values
|
|
||||||
var rowId int64
|
|
||||||
|
|
||||||
err = rows.Scan(&rowId)
|
|
||||||
if err != nil {
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the row
|
|
||||||
sql = fmt.Sprintf(
|
|
||||||
`UPDATE attendance SET disconnect_time = '%s' WHERE id = %d`,
|
|
||||||
now,
|
|
||||||
rowId,
|
|
||||||
)
|
|
||||||
|
|
||||||
_, err := db.ExecContext(context.Background(), sql)
|
|
||||||
if err != nil {
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["Saved disconnect event for %s to row id %d", "INFO"]`, event.PlayerId, rowId))
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// otherwise, log an error
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["No row found for %s, %s", "ERROR"]`, event.PlayerId, event.EventType))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fillLastMissionNull() {
|
|
||||||
functionName := "fillLastMissionNull"
|
|
||||||
// prevent crash
|
|
||||||
if db == nil {
|
|
||||||
writeLog(functionName, `["db is nil", "ERROR"]`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sql := `call proc_filllastmissionnull`
|
|
||||||
|
|
||||||
_, err := db.ExecContext(context.Background(), sql)
|
|
||||||
if err != nil {
|
|
||||||
writeLog(functionName, fmt.Sprintf(`["%s", "ERROR"]`, err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writeLog(functionName, `["Filled mission event NULLs", "INFO"]`)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
return C.runExtensionCallback(extensionCallbackFnc, name, function, data)
|
return C.runExtensionCallback(extensionCallbackFnc, name, function, data)
|
||||||
}
|
}
|
||||||
@@ -559,7 +361,7 @@ func goRVExtensionArgs(output *C.char, outputsize C.size_t, input *C.char, argv
|
|||||||
switch C.GoString(input) {
|
switch C.GoString(input) {
|
||||||
case "fillLastMissionNull":
|
case "fillLastMissionNull":
|
||||||
{
|
{
|
||||||
go fillLastMissionNull()
|
// go fillLastMissionNull()
|
||||||
}
|
}
|
||||||
case "writeAttendance":
|
case "writeAttendance":
|
||||||
{ // callExtension ["logAttendance", [_hash] call CBA_fnc_encodeJSON]];
|
{ // callExtension ["logAttendance", [_hash] call CBA_fnc_encodeJSON]];
|
||||||
@@ -571,12 +373,13 @@ func goRVExtensionArgs(output *C.char, outputsize C.size_t, input *C.char, argv
|
|||||||
{ // 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 writeMissionInfo(out[0])
|
go writeMission(out[0])
|
||||||
}
|
}
|
||||||
case "logWorld":
|
case "logWorld":
|
||||||
if argc == 1 {
|
if argc == 1 {
|
||||||
|
|||||||
Reference in New Issue
Block a user