From e52894c66392719f78acc50f1466be085ff0dc53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Jurmanovi=C4=87?= Date: Mon, 26 May 2025 20:55:22 +0200 Subject: [PATCH] update tracking --- local/service/api.go | 2 +- local/service/config.go | 2 +- local/service/server.go | 64 ++--------- local/utl/regexHandler/regexHandler.go | 26 +++++ local/utl/tracking/tracking.go | 150 +++++++++++++++++++++++++ 5 files changed, 186 insertions(+), 58 deletions(-) create mode 100644 local/utl/regexHandler/regexHandler.go diff --git a/local/service/api.go b/local/service/api.go index 16da91a..22840b8 100644 --- a/local/service/api.go +++ b/local/service/api.go @@ -26,7 +26,7 @@ func NewApiService(repository *repository.ApiRepository, } } -func (as ApiService) SetServerService(serverService *ServerService) { +func (as *ApiService) SetServerService(serverService *ServerService) { as.serverService = serverService } diff --git a/local/service/config.go b/local/service/config.go index 613811c..d5b0d05 100644 --- a/local/service/config.go +++ b/local/service/config.go @@ -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 } diff --git a/local/service/server.go b/local/service/server.go index 8b1d497..7f38e47 100644 --- a/local/service/server.go +++ b/local/service/server.go @@ -7,11 +7,7 @@ import ( "context" "log" "path/filepath" - "regexp" - "strconv" - "strings" "sync" - "time" "github.com/gofiber/fiber/v2" ) @@ -42,57 +38,13 @@ func NewServerService(repository *repository.ServerRepository, apiService *ApiSe 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) { s.instances.Delete(server.ID) - instance := &model.AccServerInstance{ - Model: server, - State: &model.ServerState{PlayerCount: 0}, - } + instance := tracking.NewAccServerInstance(server, func(states ...tracking.StateChange) { + for _, state := range states { + log.Println(tracking.StateChanges[state]) + } + }) config, _ := DecodeFileName(ConfigurationJson)(server.ConfigPath) cfg := config.(model.Configuration) event, _ := DecodeFileName(EventJson)(server.ConfigPath) @@ -101,7 +53,7 @@ func (s *ServerService) StartAccServerRuntime(server *model.Server) { instance.State.MaxConnections = cfg.MaxConnections.ToInt() 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) } @@ -125,7 +77,7 @@ func (as ServerService) GetAll(ctx *fiber.Ctx) *[]model.Server { if !ok { log.Print("Unable to retrieve instance for server of ID: ", server.ID) } else { - serverInstance := instance.(*model.AccServerInstance) + serverInstance := instance.(*tracking.AccServerInstance) if (serverInstance.State != nil) { (*servers)[i].State = *serverInstance.State } @@ -153,7 +105,7 @@ func (as ServerService) GetById(ctx *fiber.Ctx, serverID int) *model.Server { if !ok { log.Print("Unable to retrieve instance for server of ID: ", server.ID) } else { - serverInstance := instance.(*model.AccServerInstance) + serverInstance := instance.(*tracking.AccServerInstance) if (serverInstance.State != nil) { server.State = *serverInstance.State } diff --git a/local/utl/regexHandler/regexHandler.go b/local/utl/regexHandler/regexHandler.go new file mode 100644 index 0000000..5112506 --- /dev/null +++ b/local/utl/regexHandler/regexHandler.go @@ -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), + } +} \ No newline at end of file diff --git a/local/utl/tracking/tracking.go b/local/utl/tracking/tracking.go index 77cee88..53c8d1f 100644 --- a/local/utl/tracking/tracking.go +++ b/local/utl/tracking/tracking.go @@ -1,11 +1,80 @@ package tracking import ( + "acc-server-manager/local/model" + "acc-server-manager/local/utl/regexHandler" "bufio" "os" + "strconv" + "strings" "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)) { file, _ := os.Open(path) defer file.Close() @@ -21,4 +90,85 @@ func TailLogFile(path string, callback func(string)) { time.Sleep(500 * time.Millisecond) // wait for new data } } +} + +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...) } \ No newline at end of file