diff --git a/.gitignore b/.gitignore
index 274d5fc..e54744b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,4 +8,5 @@ hemtt.exe
*.biprivatekey
*.dll
-*.so
\ No newline at end of file
+*.so
+*.exe
\ No newline at end of file
diff --git a/.hemtt/project.toml b/.hemtt/project.toml
index 8438561..dd0bdbd 100644
--- a/.hemtt/project.toml
+++ b/.hemtt/project.toml
@@ -10,7 +10,7 @@ include=["mod.cpp", "LICENSE.md", "README.md", "*.dll", "*.so"]
path="addons/main/script_version.hpp" # Default
major=0 # Overrides path when set
-minor=3
+minor=4
patch=0
# build = 0 # Optional
diff --git a/addons/main/config.cpp b/addons/main/config.cpp
index 033626e..606e39b 100644
--- a/addons/main/config.cpp
+++ b/addons/main/config.cpp
@@ -5,9 +5,9 @@ class CfgPatches {
author = "ilbinek";
authors[] = {"ilbinek"};
url = "https://github.com/ilbinek/statsLogger";
- version = 0.3;
- versionStr = "0.3";
- versionAr[] = {0, 3};
+ version = 0.4;
+ versionStr = "0.4";
+ versionAr[] = {0, 4};
requiredAddons[] = {};
requiredVersion = 2.04;
units[] = {};
diff --git a/addons/main/functions/fn_init.sqf b/addons/main/functions/fn_init.sqf
index d15890e..a4e9064 100644
--- a/addons/main/functions/fn_init.sqf
+++ b/addons/main/functions/fn_init.sqf
@@ -22,7 +22,7 @@
"Status",
(
"Stats Addon initialised
" +
- "Version 0.3
" +
+ "Version 0.4
" +
"Capture is running."
)
]
@@ -65,3 +65,6 @@ diag_log(text ('[STATS] Called Stats with ' + str(_tmp)));
diag_log(text ('[STATS] Starting FPS LOOP'));
call statslogger_fnc_fpsLoop;
diag_log(text ('[STATS] Started FPS LOOP'));
+
+
+"stats_logger" callExtension ":SET:DEBUG:OFF:";
\ No newline at end of file
diff --git a/extension/data.go b/extension/data.go
index 2ac89cc..b0aedbc 100644
--- a/extension/data.go
+++ b/extension/data.go
@@ -1,114 +1,55 @@
package main
import (
- "sync"
"time"
)
type Mission struct {
- mu sync.Mutex `json:"-"`
- MissionName string `json:"missionName"`
- Worldname string `json:"worldname"`
- MissionAuthor string `json:"missionAuthor"`
- MissionType string `json:"missionType"`
- Victory string `json:"victory"`
- MissionStart string `json:"missionStart"`
- MissionEnd string `json:"missionEnd"`
- Date string `json:"date"`
- ScoreBlue string `json:"scoreBlue"`
- ScoreRed string `json:"scoreRed"`
- ScoreGreen string `json:"scoreGreen"`
- Players []*Player `json:"players"`
- Kills []*Kill `json:"kills"`
- FPS []*FPSRecord `json:"fps"`
-}
-
-func (m *Mission) AddPlayer(p *Player) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.Players = append(m.Players, p)
-}
-
-func (m *Mission) AddKill(k *Kill) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.Kills = append(m.Kills, k)
-}
-
-func (m *Mission) AddFPS(f *FPSRecord) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.FPS = append(m.FPS, f)
+ ID uint `json:"id" gorm:"primaryKey"` // Primary key
+ MissionName string `json:"missionName"`
+ Worldname string `json:"worldname"`
+ MissionAuthor string `json:"missionAuthor"`
+ MissionType string `json:"missionType"`
+ Victory string `json:"victory"`
+ MissionStart string `json:"missionStart"`
+ MissionEnd string `json:"missionEnd"`
+ Date string `json:"date"`
+ ScoreBlue string `json:"scoreBlue"`
+ ScoreRed string `json:"scoreRed"`
+ ScoreGreen string `json:"scoreGreen"`
+ Players []Player `json:"players"`
+ Kills []Kill `json:"kills"`
+ FPSRecords []FPSRecord `json:"fps"`
}
type Player struct {
- mu sync.Mutex `json:"-"`
- UID string `json:"uid"`
- Name string `json:"name"`
- Side string `json:"side"`
- Shots int `json:"shots"`
- Hits int `json:"hits"`
- Kills int `json:"kills"`
- Squad string `json:"squad"`
- Role string `json:"role"`
- Class string `json:"class"`
-}
-
-type PlayerUpdateOptions struct {
- Name string `json:"name"`
- Side string `json:"side"`
- Squad string `json:"squad"`
- Role string `json:"role"`
- Class string `json:"class"`
-}
-
-func (p *Player) Update(options PlayerUpdateOptions) {
- p.mu.Lock()
- if options.Name != "" {
- p.Name = options.Name
- }
- if options.Side != "" {
- p.Side = options.Side
- }
- if options.Squad != "" {
- p.Squad = options.Squad
- }
- if options.Role != "" {
- p.Role = options.Role
- }
- if options.Class != "" {
- p.Class = options.Class
- }
- p.mu.Unlock()
-}
-
-func (p *Player) AddShot() {
- p.mu.Lock()
- p.Shots++
- p.mu.Unlock()
-}
-
-func (p *Player) AddHit() {
- p.mu.Lock()
- p.Hits++
- p.mu.Unlock()
-}
-
-func (p *Player) AddKill() {
- p.mu.Lock()
- p.Kills++
- p.mu.Unlock()
+ ID uint `json:"id" gorm:"uniqueIndex"`
+ PlayerUID string `json:"playerUID" gorm:"index"` // Player UID
+ Name string `json:"name"`
+ Side string `json:"side"`
+ Shots int `json:"shots"`
+ Hits int `json:"hits"`
+ Squad string `json:"squad"`
+ Role string `json:"role"`
+ Class string `json:"class"`
+ Mission *Mission `json:"-" gorm:"foreignKey:MissionID"`
+ MissionID uint `json:"-" gorm:"index"`
}
type Kill struct {
- Time string `json:"time"`
- Victim string `json:"victim"`
- Killer string `json:"killer"`
- Weapon string `json:"weapon"`
- Distance string `json:"distance"`
+ ID uint `json:"id" gorm:"primaryKey"` // Primary key
+ Time string `json:"time"`
+ Victim string `json:"victim"`
+ Killer string `json:"killer"`
+ Weapon string `json:"weapon"`
+ Distance string `json:"distance"`
+ Mission *Mission `json:"-"`
+ MissionID uint `json:"-" gorm:"index"`
}
type FPSRecord struct {
- TimeUTC time.Time `json:"timeUTC"`
- FPS float64 `json:"fps"`
+ TimeUTC time.Time `json:"timeUTC" gorm:"primaryKey"` // Primary key
+ FPS float64 `json:"fps"`
+ Mission *Mission `json:"-"`
+ MissionID uint `json:"-" gorm:"index"`
}
diff --git a/extension/db/db.go b/extension/db/db.go
new file mode 100644
index 0000000..a0ff18f
--- /dev/null
+++ b/extension/db/db.go
@@ -0,0 +1,35 @@
+package db
+
+import (
+ "path/filepath"
+
+ "github.com/indig0fox/a3go/assemblyfinder"
+ "gorm.io/driver/sqlite"
+ "gorm.io/gorm"
+)
+
+var db *gorm.DB
+var configuredDbPath string = filepath.Join(
+ filepath.Dir(assemblyfinder.GetModulePath()),
+ "stats.db",
+)
+
+func Client() *gorm.DB {
+ if db == nil {
+ err := Connect(configuredDbPath)
+ if err != nil {
+ panic(err)
+ }
+ }
+ return db
+}
+
+func Connect(dbPath string) error {
+ var err error
+ configuredDbPath = dbPath
+ db, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
+ if err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/extension/go.mod b/extension/go.mod
index 8097543..fb26fec 100644
--- a/extension/go.mod
+++ b/extension/go.mod
@@ -1,5 +1,19 @@
-module statsLoggerExtension
+module github.com/ilbinek/statsLogger
go 1.20
-require github.com/indig0fox/a3go v0.3.2
+require (
+ github.com/indig0fox/a3go v0.3.2
+ github.com/rs/zerolog v1.31.0
+ gorm.io/driver/sqlite v1.5.4
+ gorm.io/gorm v1.25.5
+)
+
+require (
+ github.com/jinzhu/inflection v1.0.0 // indirect
+ github.com/jinzhu/now v1.1.5 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-isatty v0.0.19 // indirect
+ github.com/mattn/go-sqlite3 v1.14.17 // indirect
+ golang.org/x/sys v0.13.0 // indirect
+)
diff --git a/extension/go.sum b/extension/go.sum
index bd4776c..a746719 100644
--- a/extension/go.sum
+++ b/extension/go.sum
@@ -1,4 +1,30 @@
-github.com/indig0fox/a3go v0.2.1 h1:Ixr/182pGd5qPbFYgHINWt5YtyKaO7Yo/va/17nF/Vg=
-github.com/indig0fox/a3go v0.2.1/go.mod h1:8htVwBiIAVKpT1Jyb+5dm7GuLAAevTXgw7UKxSlOawY=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/indig0fox/a3go v0.3.2 h1:bNL90pffeOnS6Qtjoo5JHpdpZn1f0BZmRZR8nz/xcvQ=
github.com/indig0fox/a3go v0.3.2/go.mod h1:8htVwBiIAVKpT1Jyb+5dm7GuLAAevTXgw7UKxSlOawY=
+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=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
+github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
+github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
+github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
+golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
+gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
+gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
+gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
+gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
diff --git a/extension/main.go b/extension/main.go
index 4dc4356..3cf6ca4 100644
--- a/extension/main.go
+++ b/extension/main.go
@@ -4,13 +4,17 @@ import (
"encoding/json"
"errors"
"fmt"
- "log"
"os"
"path/filepath"
"strconv"
"strings"
"time"
+ "github.com/ilbinek/statsLogger/db"
+ "gorm.io/gorm"
+
+ "github.com/rs/zerolog"
+
"github.com/indig0fox/a3go/a3interface"
"github.com/indig0fox/a3go/assemblyfinder"
)
@@ -25,6 +29,9 @@ var EXTENSION_NAME = "STATS_LOGGER"
var mission Mission
+var logFile *os.File
+var logger zerolog.Logger
+
func init() {
a3interface.SetVersion("1.0.0")
a3interface.NewRegistration(":GET:TIME:").
@@ -32,6 +39,43 @@ func init() {
SetRunInBackground(false).
Register()
+ // when a3 sends this, set logging to INFO+
+ a3interface.NewRegistration(":SET:DEBUG:OFF:").
+ SetFunction(func(ctx a3interface.ArmaExtensionContext, data string) (string, error) {
+ thisLogger := logger.With().
+ Interface("context", ctx).
+ Str("call_type", "RvExtension").
+ Str("command", ":SET:DEBUG:OFF:").
+ Str("data", data).
+ Logger()
+
+ thisLogger.Debug().Send()
+
+ zerolog.SetGlobalLevel(zerolog.InfoLevel)
+
+ return "", nil
+ }).
+ SetRunInBackground(false).
+ Register()
+
+ a3interface.NewRegistration(":SET:DEBUG:ON:").
+ SetFunction(func(ctx a3interface.ArmaExtensionContext, data string) (string, error) {
+ thisLogger := logger.With().
+ Interface("context", ctx).
+ Str("call_type", "RvExtension").
+ Str("command", ":SET:DEBUG:ON:").
+ Str("data", data).
+ Logger()
+
+ thisLogger.Debug().Send()
+
+ zerolog.SetGlobalLevel(zerolog.DebugLevel)
+
+ return "", nil
+ }).
+ SetRunInBackground(false).
+ Register()
+
a3interface.NewRegistration(":RESET:").
SetFunction(onReset).
SetRunInBackground(false).
@@ -80,6 +124,18 @@ func init() {
SetFunction(onExport).
SetRunInBackground(false).
Register()
+
+ logFile, err := os.Create(filepath.Join(modulePathDir, "stats.log"))
+ if err != nil {
+ panic(err)
+ }
+
+ // default to debug on logger
+ logger = zerolog.New(zerolog.ConsoleWriter{
+ Out: logFile,
+ TimeFormat: time.RFC3339,
+ NoColor: true,
+ }).With().Timestamp().Caller().Logger().Level(zerolog.DebugLevel)
}
// onTimeNowUTC :GET:TIME: returns the current time in UTC
@@ -87,10 +143,21 @@ func onTimeNowUTC(
ctx a3interface.ArmaExtensionContext,
data string,
) (string, error) {
+ thisLogger := logger.With().
+ Interface("context", ctx).
+ Str("call_type", "RvExtension").
+ Str("command", ":GET:TIME:").
+ Str("data", data).
+ Logger()
+
+ thisLogger.Debug().Send()
+
+ // get time
t := time.Now().UTC()
// format time
timeNow := t.Format("2006-01-02 15:04:05")
// send data back to Arma
+ thisLogger.Debug().Msgf("Returning time: %s", timeNow)
return string(timeNow), nil
}
@@ -99,6 +166,14 @@ func onReset(
ctx a3interface.ArmaExtensionContext,
data string,
) (string, error) {
+ thisLogger := logger.With().
+ Interface("context", ctx).
+ Str("call_type", "RvExtension").
+ Str("command", ":RESET:").
+ Str("data", data).
+ Logger()
+
+ thisLogger.Debug().Send()
// reset mission struct
mission = Mission{}
// send data back to Arma
@@ -118,9 +193,17 @@ func onSetupMissionArgs(
// 3: mission type (ex. public)
// 4: time of day at mission start (daytime)
+ thisLogger := logger.With().
+ Interface("context", ctx).
+ Str("call_type", "RvExtensionArgs").
+ Str("command", ":MISSION:").
+ Logger()
+
+ thisLogger.Debug().Send()
+
// Check size
if len(args) != 5 {
- log.Println("Error: Mission array size is not 5")
+ thisLogger.Error().Err(errors.New("mission array size is not 5")).Strs("args", args).Send()
a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "MISSION ERROR", "WRONG MISSION PARAMS COUNT - ["+strings.Join(args, ", ")+"]")
return "", errors.New("mission array size is not 5")
}
@@ -138,6 +221,25 @@ func onSetupMissionArgs(
// Victory: args[4], // not in sqf
MissionStart: args[4],
}
+
+ thisLogger.Info().Errs(
+ "migrations", []error{
+ db.Client().AutoMigrate(&Mission{}),
+ db.Client().AutoMigrate(&Player{}),
+ db.Client().AutoMigrate(&Kill{}),
+ db.Client().AutoMigrate(&FPSRecord{}),
+ },
+ ).Msg("Migrating tables")
+
+ // Create new mission
+ thisLogger.Debug().Interface("mission", &mission).Msg("Creating mission")
+
+ err := db.Client().Create(&mission).Error
+ if err != nil {
+ thisLogger.Error().Err(err).Msg("Could not create mission")
+ a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "MISSION ERROR", err.Error())
+ return "", err
+ }
// send data back to Arma
return `["Saved mission data!"]`, nil
}
@@ -154,9 +256,17 @@ func onWinArgs(
// 2: blue score
// 3: red score
+ thisLogger := logger.With().
+ Interface("context", ctx).
+ Str("call_type", "RvExtensionArgs").
+ Str("command", ":WIN:").
+ Logger()
+
+ thisLogger.Debug().Send()
+
// Check size
if len(args) != 4 {
- log.Println("Error: Win array size is not 4")
+ thisLogger.Error().Err(errors.New("win array size is not 4")).Strs("args", args).Send()
a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "WIN ERROR", "WRONG WIN PARAMS COUNT")
return "", errors.New("win array size is not 4")
}
@@ -171,6 +281,16 @@ func onWinArgs(
mission.ScoreBlue = args[2]
mission.ScoreRed = args[3]
+ thisLogger.Trace().Interface("mission", &mission).Msg("Updating mission")
+
+ // Update mission
+ err := db.Client().Save(&mission).Error
+ if err != nil {
+ thisLogger.Error().Err(err).Msg("Could not update mission")
+ a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "WIN ERROR", err.Error())
+ return "", err
+ }
+
// send data back to Arma
return `["Saved win data!"]`, nil
}
@@ -189,9 +309,17 @@ func onAddPlayerArgs(
// 4: side (WEST, EAST, etc.)
// 5: groupID (str group _x)
+ thisLogger := logger.With().
+ Interface("context", ctx).
+ Str("call_type", "RvExtensionArgs").
+ Str("command", ":PLAYER:").
+ Logger()
+
+ thisLogger.Debug().Send()
+
// Check size
if len(args) != 6 {
- log.Println("Error: Player array size is not 6")
+ thisLogger.Error().Err(errors.New("player array size is not 6")).Strs("args", args).Send()
a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "PLAYER ERROR", "WRONG PLAYER PARAMS COUNT")
return "", errors.New("player array size is not 6")
}
@@ -201,32 +329,55 @@ func onAddPlayerArgs(
}
// Create new player
- player := Player{
- UID: args[0],
- Name: args[1],
- Role: args[2],
- Class: args[3],
- Side: args[4],
- Squad: args[5],
+ receivedPlayer := Player{
+ PlayerUID: args[0],
+ Name: args[1],
+ Role: args[2],
+ Class: args[3],
+ Side: args[4],
+ Squad: args[5],
+ Mission: &mission,
}
- // Check if this player is already in the mission
- for _, p := range mission.Players {
- if p.UID == player.UID {
- // Player already in mission so just update
- p.Update(PlayerUpdateOptions{
- Name: player.Name,
- Side: player.Side,
- Squad: player.Squad,
- Role: player.Role,
- Class: player.Class,
- })
- return `["Updated player data!"]`, nil
- }
+ var dbPlayer Player
+ db.Client().Model(&Player{}).Where(
+ "player_uid = ? AND mission_id = ?",
+ receivedPlayer.PlayerUID,
+ mission.ID,
+ ).First(&dbPlayer)
+ if (db.Client().Error != nil) && !errors.Is(db.Client().Error, gorm.ErrRecordNotFound) {
+ thisLogger.Error().Err(db.Client().Error).
+ Str("player_uid", receivedPlayer.PlayerUID).
+ Msg("DB Error")
+ a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "PLAYER ERROR", "DB ERROR")
+ return "", db.Client().Error
}
- // Add player to mission
- mission.AddPlayer(&player)
+ if dbPlayer.ID == 0 {
+ err := db.Client().Model(&mission).Association("Players").Append(&receivedPlayer)
+ if err != nil {
+ thisLogger.Error().Err(err).
+ Interface("player", &receivedPlayer).
+ Msg("Could not create player")
+ a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "PLAYER ERROR", err.Error())
+ return "", err
+ }
+ } else {
+ err := db.Client().Model(&dbPlayer).Updates(Player{
+ Name: receivedPlayer.Name,
+ Side: receivedPlayer.Side,
+ Squad: receivedPlayer.Squad,
+ Role: receivedPlayer.Role,
+ Class: receivedPlayer.Class,
+ }).Error
+ if err != nil {
+ thisLogger.Error().Err(err).
+ Interface("player", &dbPlayer).
+ Msg("Could not update player")
+ a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "PLAYER ERROR", err.Error())
+ return "", err
+ }
+ }
// Send data back to Arma
return `["Saved player data!"]`, nil
@@ -245,9 +396,17 @@ func onAddKillArgs(
// 3: distance
// 4: time
+ thisLogger := logger.With().
+ Interface("context", ctx).
+ Str("call_type", "RvExtensionArgs").
+ Str("command", ":KILL:").
+ Logger()
+
+ thisLogger.Debug().Send()
+
// Check size
if len(args) != 5 {
- log.Println("Error: Kill array size is not 5")
+ thisLogger.Error().Err(errors.New("kill array size is not 5")).Strs("args", args).Send()
a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "KILL ERROR", "WRONG KILL PARAMS COUNT")
return "", errors.New("kill array size is not 5")
}
@@ -264,16 +423,15 @@ func onAddKillArgs(
Distance: args[3],
Time: args[4],
}
- // Add kill to mission
- mission.AddKill(&kill)
-
- // Find the killer
- for i, p := range mission.Players {
- if p.UID == args[0] {
- // Increment kills
- mission.Players[i].AddKill()
- return `["Saved kill data!"]`, nil
- }
+
+ // Add kill to mission associations
+ err := db.Client().Model(&mission).Association("Kills").Append(&kill)
+ if err != nil {
+ thisLogger.Error().Err(err).
+ Interface("kill", &kill).
+ Msg("Could not add kill to mission")
+ a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "KILL ERROR", err.Error())
+ return "", err
}
// Send data back to Arma
@@ -289,9 +447,17 @@ func onAddShotArgs(
// args are:
// 0: playerUID
+ thisLogger := logger.With().
+ Interface("context", ctx).
+ Str("call_type", "RvExtensionArgs").
+ Str("command", ":SHOT:").
+ Logger()
+
+ thisLogger.Debug().Send()
+
// Check size
if len(args) != 1 {
- log.Println("Error: Shot array size is not 1")
+ thisLogger.Error().Err(errors.New("shot array size is not 1")).Strs("args", args).Send()
a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "SHOT ERROR", "WRONG SHOT PARAMS COUNT")
return "", errors.New("shot array size is not 1")
}
@@ -300,18 +466,26 @@ func onAddShotArgs(
args[i] = a3interface.RemoveEscapeQuotes(v)
}
- // Find the player
- for i, p := range mission.Players {
- if p.UID == args[0] {
- // Increment shots
- // do so using a method which uses the mutex
- mission.Players[i].AddShot()
+ // Find the player and update it
+ for i, v := range mission.Players {
+ if v.PlayerUID == args[0] {
+ err := db.Client().Model(&(mission.Players[i])).Updates(&Player{
+ Shots: (mission.Players[i].Shots + 1),
+ }).Error
+
+ if err != nil {
+ thisLogger.Error().Err(db.Client().Error).
+ Str("player_uid", args[0]).
+ Msg("DB Error")
+ a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "SHOT ERROR", "DB ERROR")
+ return "", errors.New("db error")
+ }
return `["Saved shot data!"]`, nil
}
}
// Send data back to Arma
- return `["Saved shot data!"]`, nil
+ return "", errors.New("no player found error")
}
// addHit :HIT: adds a hit to the mission
@@ -323,9 +497,17 @@ func onAddHitArgs(
// args are:
// 0: playerUID
+ thisLogger := logger.With().
+ Interface("context", ctx).
+ Str("call_type", "RvExtensionArgs").
+ Str("command", ":HIT:").
+ Logger()
+
+ thisLogger.Debug().Send()
+
// Check size
if len(args) != 1 {
- log.Println("Error: Hit array size is not 1")
+ thisLogger.Error().Err(errors.New("hit array size is not 1")).Strs("args", args).Send()
a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "HIT ERROR", "WRONG HIT PARAMS COUNT")
return "", errors.New("hit array size is not 1")
}
@@ -334,17 +516,27 @@ func onAddHitArgs(
args[i] = a3interface.RemoveEscapeQuotes(v)
}
- // Find the player
- for i, p := range mission.Players {
- if p.UID == args[0] {
- // Increment hits
- mission.Players[i].Hits++
+ // Find the shooter and update it
+ for i, v := range mission.Players {
+ if v.PlayerUID == args[0] {
+ err := db.Client().Model(&(mission.Players[i])).Updates(&Player{
+ Hits: (mission.Players[i].Hits + 1),
+ }).Error
+
+ if err != nil {
+ thisLogger.Error().Err(db.Client().Error).
+ Str("player_uid", args[0]).
+ Msg("DB Error")
+ a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "SHOT ERROR", "DB ERROR")
+ return "", errors.New("db error")
+ }
+
return `["Saved hit data!"]`, nil
}
}
// Send data back to Arma
- return `["Saved hit data!"]`, nil
+ return "", errors.New("no player found error")
}
// onAddFPSArgs :FPS: adds FPS data to the mission
@@ -356,9 +548,17 @@ func onAddFPSArgs(
// args are:
// 0: fps
+ thisLogger := logger.With().
+ Interface("context", ctx).
+ Str("call_type", "RvExtensionArgs").
+ Str("command", ":FPS:").
+ Logger()
+
+ thisLogger.Debug().Send()
+
// Check size
if len(args) != 1 {
- log.Println("Error: FPS array size is not 1")
+ thisLogger.Error().Err(errors.New("fps array size is not 1")).Strs("args", args).Send()
a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "FPS ERROR", "WRONG FPS PARAMS COUNT")
return "", errors.New("fps array size is not 1")
}
@@ -370,16 +570,28 @@ func onAddFPSArgs(
// Convert to float64
f, err := strconv.ParseFloat(args[0], 64)
if err != nil {
- log.Println("Error: FPS is not a float64")
+ thisLogger.Error().Err(err).
+ Str("fps", args[0]).
+ Msg("FPS is not a float64")
a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "FPS ERROR", "FPS IS NOT A FLOAT64")
return "", errors.New("fps is not a float64")
}
// Add FPS to mission
- mission.AddFPS(&FPSRecord{
+ fps := &FPSRecord{
FPS: f,
TimeUTC: time.Now().UTC(),
- })
+ }
+
+ // Add FPS to mission associations
+ err = db.Client().Model(&mission).Association("FPSRecords").Append(fps)
+ if err != nil {
+ thisLogger.Error().Err(err).
+ Interface("fps", fps).
+ Msg("Could not add FPS to mission")
+ a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "FPS ERROR", err.Error())
+ return "", err
+ }
// Send data back to Arma
return `["Saved FPS data!"]`, nil
@@ -391,12 +603,23 @@ func onExport(
data string,
) (string, error) {
+ thisLogger := logger.With().
+ Interface("context", ctx).
+ Str("call_type", "RvExtension").
+ Str("command", ":EXPORT:").
+ Str("data", data).
+ Logger()
+
+ thisLogger.Debug().Send()
+
// Export mission
// Get executablepath/stats
p := filepath.Join(modulePathDir, "stats-output")
err := os.MkdirAll(p, os.ModePerm)
if err != nil {
- log.Println("Error: Could not create stats folder")
+ thisLogger.Error().Err(err).
+ Str("path", p).
+ Msg("Could not create stats-output folder")
a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "EXPORT ERROR", err.Error())
return "", err
}
@@ -418,7 +641,9 @@ func onExport(
missionBytes, err := json.MarshalIndent(&mission, "", " ")
// catch error
if err != nil {
- log.Println("Error: Could not marshal mission")
+ thisLogger.Error().Err(err).
+ Interface("mission", &mission).
+ Msg("Could not marshal mission to JSON")
a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "EXPORT ERROR", err.Error())
return "", err
}
@@ -428,11 +653,14 @@ func onExport(
// Check for errors
if err != nil {
- log.Println("Error: Could not write mission to file")
+ thisLogger.Error().Err(err).
+ Str("path", fileDestinationAbsolute).
+ Msg("Could not write mission to file")
a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "EXPORT ERROR", err.Error())
return "", err
}
+ thisLogger.Info().Str("path", fileDestinationAbsolute).Msg("Exported mission to file")
a3interface.WriteArmaCallback(EXTENSION_NAME, "DEBUG", "EXPORT DONE", "EXPORT FINISHED")
return fmt.Sprintf(
`["Successfully exported data to %s"]`,
@@ -442,5 +670,9 @@ func onExport(
}
func main() {
+ db.Client().AutoMigrate(&Mission{})
+ db.Client().AutoMigrate(&Player{})
+ db.Client().AutoMigrate(&Kill{})
+ db.Client().AutoMigrate(&FPSRecord{})
fmt.Scanln()
}
diff --git a/mod.cpp b/mod.cpp
index 48c596c..a4ad1bb 100644
--- a/mod.cpp
+++ b/mod.cpp
@@ -1,11 +1,7 @@
-name = "Stats Logger"; // Name of your mod
-//picture = "\Samples_F\Data_01\Logos\logo.paa"; // Picture displayed from the expansions menu. Optimal size is 2048x1024
-//logoSmall = "\Samples_F\Data_01\Logos\logo_small.paa"; // Display next to the item added by the mod
-//logo = "\Samples_F\Data_01\Logos\logo.paa"; // Logo displayed in the main menu
-//logoOver = "\Samples_F\Data_01\Logos\logoOver.paa"; // When the mouse is over, in the main menu
-actionName = "Author"; // Text displayed on the "website" button
-action = "https://github.com/ilbinek"; // Website URL, that can accessed from the expansions menu
-tooltipOwned = "Stats logger v0.1"; // Tool tip displayed when the mouse is left over, in the main menu
+name = "Stats Logger";
+actionName = "Author";
+action = "https://github.com/ilbinek/statsLogger";
+tooltipOwned = "Stats logger v0.4";
// Color used for DLC stripes and backgrounds (RGBA)
dlcColor[] =
@@ -18,5 +14,5 @@ dlcColor[] =
// Overview text, displayed from the extension menu
overview = "A mod that logs various statistics from Arma 3 PvP missions.";
-hideName = 0; // Hide the extension name
-hidePicture = 0; // Hide the extension menu
\ No newline at end of file
+hideName = 0;
+hidePicture = 0;
\ No newline at end of file