update tracking

This commit is contained in:
Fran Jurmanović
2025-05-26 20:55:22 +02:00
parent edf5a2c8c4
commit e52894c663
5 changed files with 186 additions and 58 deletions

View File

@@ -26,7 +26,7 @@ func NewApiService(repository *repository.ApiRepository,
} }
} }
func (as ApiService) SetServerService(serverService *ServerService) { func (as *ApiService) SetServerService(serverService *ServerService) {
as.serverService = serverService as.serverService = serverService
} }

View File

@@ -73,7 +73,7 @@ func NewConfigService(repository *repository.ConfigRepository, serverRepository
} }
} }
func (as ConfigService) SetServerService(serverService *ServerService) { func (as *ConfigService) SetServerService(serverService *ServerService) {
as.serverService = serverService as.serverService = serverService
} }

View File

@@ -7,11 +7,7 @@ import (
"context" "context"
"log" "log"
"path/filepath" "path/filepath"
"regexp"
"strconv"
"strings"
"sync" "sync"
"time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@@ -42,57 +38,13 @@ func NewServerService(repository *repository.ServerRepository, apiService *ApiSe
return service return service
} }
var leaderboardUpdateRegex = regexp.MustCompile(`Updated leaderboard for (\d+) clients`)
var sessionChangeRegex = regexp.MustCompile(`Session changed: (\w+) -> (\w+)`)
func handleLogLine(instance *model.AccServerInstance) func(string) {
return func (line string) {
state := (*instance).State
now := time.Now()
if strings.Contains(line, "client(s) online") {
parts := strings.Fields(line)
if len(parts) >= 2 {
countStr := parts[1]
if count, err := strconv.Atoi(countStr); err == nil {
state.Lock()
state.PlayerCount = count
state.Unlock()
}
}
}
if strings.Contains(line, "Session changed") {
match := sessionChangeRegex.FindStringSubmatch(line)
if len(match) == 3 {
newSession := match[2]
state.Lock()
state.Session = newSession
state.SessionStart = now
state.Unlock()
}
}
if strings.Contains(line, "Updated leaderboard for") {
match := leaderboardUpdateRegex.FindStringSubmatch(line)
if len(match) == 2 {
if count, err := strconv.Atoi(match[1]); err == nil {
state.Lock()
state.PlayerCount = count
state.Unlock()
}
}
}
}
}
func (s *ServerService) StartAccServerRuntime(server *model.Server) { func (s *ServerService) StartAccServerRuntime(server *model.Server) {
s.instances.Delete(server.ID) s.instances.Delete(server.ID)
instance := &model.AccServerInstance{ instance := tracking.NewAccServerInstance(server, func(states ...tracking.StateChange) {
Model: server, for _, state := range states {
State: &model.ServerState{PlayerCount: 0}, log.Println(tracking.StateChanges[state])
} }
})
config, _ := DecodeFileName(ConfigurationJson)(server.ConfigPath) config, _ := DecodeFileName(ConfigurationJson)(server.ConfigPath)
cfg := config.(model.Configuration) cfg := config.(model.Configuration)
event, _ := DecodeFileName(EventJson)(server.ConfigPath) event, _ := DecodeFileName(EventJson)(server.ConfigPath)
@@ -101,7 +53,7 @@ func (s *ServerService) StartAccServerRuntime(server *model.Server) {
instance.State.MaxConnections = cfg.MaxConnections.ToInt() instance.State.MaxConnections = cfg.MaxConnections.ToInt()
instance.State.Track = evt.Track instance.State.Track = evt.Track
go tracking.TailLogFile(filepath.Join(server.ConfigPath, "\\server\\log\\server.log"), handleLogLine(instance)) go tracking.TailLogFile(filepath.Join(server.ConfigPath, "\\server\\log\\server.log"), instance.HandleLogLine)
s.instances.Store(server.ID, instance) s.instances.Store(server.ID, instance)
} }
@@ -125,7 +77,7 @@ func (as ServerService) GetAll(ctx *fiber.Ctx) *[]model.Server {
if !ok { if !ok {
log.Print("Unable to retrieve instance for server of ID: ", server.ID) log.Print("Unable to retrieve instance for server of ID: ", server.ID)
} else { } else {
serverInstance := instance.(*model.AccServerInstance) serverInstance := instance.(*tracking.AccServerInstance)
if (serverInstance.State != nil) { if (serverInstance.State != nil) {
(*servers)[i].State = *serverInstance.State (*servers)[i].State = *serverInstance.State
} }
@@ -153,7 +105,7 @@ func (as ServerService) GetById(ctx *fiber.Ctx, serverID int) *model.Server {
if !ok { if !ok {
log.Print("Unable to retrieve instance for server of ID: ", server.ID) log.Print("Unable to retrieve instance for server of ID: ", server.ID)
} else { } else {
serverInstance := instance.(*model.AccServerInstance) serverInstance := instance.(*tracking.AccServerInstance)
if (serverInstance.State != nil) { if (serverInstance.State != nil) {
server.State = *serverInstance.State server.State = *serverInstance.State
} }

View File

@@ -0,0 +1,26 @@
package regexHandler
import (
"acc-server-manager/local/model"
"regexp"
)
type AccServerInstance struct {
Model *model.Server
State *model.ServerState
}
type RegexHandler struct {
regex *regexp.Regexp
}
func (rh *RegexHandler) Contains(line string, callback func(...string)) {
match := rh.regex.FindStringSubmatch(line)
callback(match...)
}
func New(str string) *RegexHandler {
return &RegexHandler{
regex: regexp.MustCompile(str),
}
}

View File

@@ -1,11 +1,80 @@
package tracking package tracking
import ( import (
"acc-server-manager/local/model"
"acc-server-manager/local/utl/regexHandler"
"bufio" "bufio"
"os" "os"
"strconv"
"strings"
"time" "time"
) )
type StateChange int
const (
PlayerCount StateChange = iota
Session
)
var StateChanges = map[StateChange]string {
PlayerCount: "player-count",
Session: "session",
}
type AccServerInstance struct {
Model *model.Server
State *model.ServerState
OnStateChange func(...StateChange)
}
func NewAccServerInstance(server *model.Server, onStateChange func(...StateChange)) *AccServerInstance {
return &AccServerInstance{
Model: server,
State: &model.ServerState{PlayerCount: 0},
OnStateChange: onStateChange,
}
}
type StateRegexHandler struct {
*regexHandler.RegexHandler
test string
}
func NewRegexHandler(str string, test string) *StateRegexHandler {
return &StateRegexHandler{
RegexHandler: regexHandler.New(str),
test: test,
}
}
func (rh *StateRegexHandler) Test(line string) bool{
return strings.Contains(line, rh.test)
}
func (rh *StateRegexHandler) Count(line string) int{
var count int = 0
rh.Contains(line, func (strs ...string) {
if len(strs) == 2 {
if ct, err := strconv.Atoi(strs[1]); err == nil {
count = ct
}
}
})
return count
}
func (rh *StateRegexHandler) Change(line string) (string, string){
var old string = ""
var new string = ""
rh.Contains(line, func (strs ...string) {
if len(strs) == 3 {
old = strs[1]
new = strs[2]
}
})
return old, new
}
func TailLogFile(path string, callback func(string)) { func TailLogFile(path string, callback func(string)) {
file, _ := os.Open(path) file, _ := os.Open(path)
defer file.Close() defer file.Close()
@@ -22,3 +91,84 @@ func TailLogFile(path string, callback func(string)) {
} }
} }
} }
type LogStateType int
const (
SessionChange LogStateType = iota
LeaderboardUpdate
UDPCount
ClientsOnline
)
var logStateContain = map[LogStateType]string {
SessionChange: "Session changed",
LeaderboardUpdate: "Updated leaderboard for",
UDPCount: "Udp message count",
ClientsOnline: "client(s) online",
}
var sessionChangeRegex = NewRegexHandler(`Session changed: (\w+) -> (\w+)`, logStateContain[SessionChange])
var leaderboardUpdateRegex = NewRegexHandler(`Updated leaderboard for (\d+) clients`, logStateContain[LeaderboardUpdate])
var udpCountRegex = NewRegexHandler(`Udp message count ((\d+) client`, logStateContain[UDPCount])
var clientsOnlineRegex = NewRegexHandler(`(\d+) client(s) online`, logStateContain[ClientsOnline])
var logStateRegex = map[LogStateType]*StateRegexHandler {
SessionChange: sessionChangeRegex,
LeaderboardUpdate: leaderboardUpdateRegex,
UDPCount: udpCountRegex,
ClientsOnline: clientsOnlineRegex,
}
func (instance *AccServerInstance) HandleLogLine(line string) {
for logState, regexHandler := range logStateRegex {
if (regexHandler.Test(line)) {
switch logState {
case LeaderboardUpdate:
case UDPCount:
case ClientsOnline:
count := regexHandler.Count(line)
instance.UpdatePlayerCount(count)
case SessionChange:
_, new := regexHandler.Change(line)
instance.UpdateSessionChange(new)
}
}
}
}
func (instance *AccServerInstance) UpdateState(callback func(state *model.ServerState)) {
state := instance.State
state.Lock()
defer state.Unlock()
callback(state)
}
func (instance *AccServerInstance) UpdatePlayerCount(count int) {
changes := []StateChange{}
instance.UpdateState(func (state *model.ServerState) {
if (count > 0 && state.PlayerCount == 0) {
state.SessionStart = time.Now()
changes = append(changes, Session)
} else if (count == 0) {
state.SessionStart = time.Time{}
changes = append(changes, Session)
}
state.PlayerCount = count
changes = append(changes, PlayerCount)
})
instance.OnStateChange(changes...)
}
func (instance *AccServerInstance) UpdateSessionChange(session string) {
changes := []StateChange{}
instance.UpdateState(func (state *model.ServerState) {
if (state.PlayerCount > 0) {
state.SessionStart = time.Now()
} else {
state.SessionStart = time.Time{}
}
state.Session = session
changes = append(changes, Session)
})
instance.OnStateChange(changes...)
}