From 56ef5e1484dcd902d097e773827224e8eb7cd09d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Jurmanovi=C4=87?= Date: Tue, 27 May 2025 20:18:58 +0200 Subject: [PATCH] add state history --- local/api/api.go | 4 ++- local/controller/controller.go | 5 ++++ local/controller/stateHistory.go | 43 ++++++++++++++++++++++++++++++ local/model/server.go | 13 +++++---- local/repository/repository.go | 1 + local/repository/stateHistory.go | 45 ++++++++++++++++++++++++++++++++ local/service/server.go | 16 ++++++++---- local/service/service.go | 1 + local/service/stateHistory.go | 29 ++++++++++++++++++++ local/utl/common/common.go | 1 + local/utl/db/db.go | 4 +++ local/utl/tracking/tracking.go | 38 +++++++++++++++------------ 12 files changed, 173 insertions(+), 27 deletions(-) create mode 100644 local/controller/stateHistory.go create mode 100644 local/repository/stateHistory.go create mode 100644 local/service/stateHistory.go diff --git a/local/api/api.go b/local/api/api.go index 5c33980..0af6f11 100644 --- a/local/api/api.go +++ b/local/api/api.go @@ -25,11 +25,13 @@ func Init(di *dig.Container, app *fiber.App) { }, }) + serverIdGroup := groups.Group("/server/:id") routeGroups := &common.RouteGroups{ Api: groups.Group("/api"), Server: groups.Group("/server"), - Config: groups.Group("/server/:id").Group("/config"), + Config: serverIdGroup.Group("/config"), Lookup: groups.Group("/lookup"), + StateHistory: serverIdGroup.Group("/state-history"), } groups.Use(basicAuthConfig) diff --git a/local/controller/controller.go b/local/controller/controller.go index f31109c..3f8753e 100644 --- a/local/controller/controller.go +++ b/local/controller/controller.go @@ -40,6 +40,11 @@ func InitializeControllers(c *dig.Container) { if err != nil { log.Panic("unable to initialize lookup controller") } + + err = c.Invoke(NewStateHistoryController) + if err != nil { + log.Panic("unable to initialize stateHistory controller") + } } // FilteredResponse diff --git a/local/controller/stateHistory.go b/local/controller/stateHistory.go new file mode 100644 index 0000000..dd88240 --- /dev/null +++ b/local/controller/stateHistory.go @@ -0,0 +1,43 @@ +package controller + +import ( + "acc-server-manager/local/service" + "acc-server-manager/local/utl/common" + + "github.com/gofiber/fiber/v2" +) + +type StateHistoryController struct { + service *service.StateHistoryService +} + +// NewStateHistoryController +// Initializes StateHistoryController. +// +// Args: +// *services.StateHistoryService: StateHistory service +// *Fiber.RouterGroup: Fiber Router Group +// Returns: +// *StateHistoryController: Controller for "StateHistory" interactions +func NewStateHistoryController(as *service.StateHistoryService, routeGroups *common.RouteGroups) *StateHistoryController { + ac := &StateHistoryController{ + service: as, + } + + routeGroups.StateHistory.Get("/", ac.getAll) + + return ac +} + +// getAll returns StateHistorys +// +// @Summary Return StateHistorys +// @Description Return StateHistorys +// @Tags StateHistory +// @Success 200 {array} string +// @Router /v1/StateHistory [get] +func (ac *StateHistoryController) getAll(c *fiber.Ctx) error { + StateHistoryID, _ := c.ParamsInt("id") + StateHistoryModel := ac.service.GetAll(c, StateHistoryID) + return c.JSON(StateHistoryModel) +} \ No newline at end of file diff --git a/local/model/server.go b/local/model/server.go index 31153e6..f819153 100644 --- a/local/model/server.go +++ b/local/model/server.go @@ -31,11 +31,6 @@ type PlayerState struct { IsConnected bool } -type AccServerInstance struct { - Model *Server - State *ServerState -} - type State struct { Session string `json:"session"` SessionStart time.Time `json:"sessionStart"` @@ -53,4 +48,12 @@ type ServerState struct { MaxConnections int `json:"maxConnections"` // Players map[int]*PlayerState // etc. +} + +type StateHistory struct { + ID uint `gorm:"primaryKey" json:"id"` + ServerID uint `json:"serverId" gorm:"not null"` + Session string `json:"session"` + PlayerCount int `json:"playerCount"` + DateCreated time.Time `json:"dateCreated"` } \ No newline at end of file diff --git a/local/repository/repository.go b/local/repository/repository.go index d089f23..e6405e2 100644 --- a/local/repository/repository.go +++ b/local/repository/repository.go @@ -11,6 +11,7 @@ import ( // *dig.Container: Dig Container func InitializeRepositories(c *dig.Container) { c.Provide(NewApiRepository) + c.Provide(NewStateHistoryRepository) c.Provide(NewServerRepository) c.Provide(NewConfigRepository) c.Provide(NewLookupRepository) diff --git a/local/repository/stateHistory.go b/local/repository/stateHistory.go new file mode 100644 index 0000000..0e04b52 --- /dev/null +++ b/local/repository/stateHistory.go @@ -0,0 +1,45 @@ +package repository + +import ( + "acc-server-manager/local/model" + "context" + + "gorm.io/gorm" +) + +type StateHistoryRepository struct { + db *gorm.DB +} + +func NewStateHistoryRepository(db *gorm.DB) *StateHistoryRepository { + return &StateHistoryRepository{ + db: db, + } +} + +// GetAll +// Gets All rows from Server table. +// +// Args: +// context.Context: Application context +// Returns: +// model.ServerModel: Server object from database. +func (as StateHistoryRepository) GetAll(ctx context.Context, id int) *[]model.StateHistory { + db := as.db.WithContext(ctx) + ServerModel := new([]model.StateHistory) + db.Find(&ServerModel).Where("ID = ?", id) + return ServerModel +} + +// UpdateServer +// Updates Server row from Server table. +// +// Args: +// context.Context: Application context +// Returns: +// model.Server: Server object from database. +func (as StateHistoryRepository) Insert(ctx context.Context, body *model.StateHistory) *model.StateHistory { + db := as.db.WithContext(ctx) + db.Save(body) + return body +} diff --git a/local/service/server.go b/local/service/server.go index 7f38e47..4019a42 100644 --- a/local/service/server.go +++ b/local/service/server.go @@ -8,22 +8,25 @@ import ( "log" "path/filepath" "sync" + "time" "github.com/gofiber/fiber/v2" ) type ServerService struct { repository *repository.ServerRepository + stateHistoryRepo *repository.StateHistoryRepository apiService *ApiService instances sync.Map configService *ConfigService } -func NewServerService(repository *repository.ServerRepository, apiService *ApiService, configService *ConfigService) *ServerService { +func NewServerService(repository *repository.ServerRepository, stateHistoryRepo *repository.StateHistoryRepository, apiService *ApiService, configService *ConfigService) *ServerService { service := &ServerService{ repository: repository, apiService: apiService, configService: configService, + stateHistoryRepo: stateHistoryRepo, } servers := repository.GetAll(context.Background()) for _, server := range *servers { @@ -40,10 +43,13 @@ func NewServerService(repository *repository.ServerRepository, apiService *ApiSe func (s *ServerService) StartAccServerRuntime(server *model.Server) { s.instances.Delete(server.ID) - instance := tracking.NewAccServerInstance(server, func(states ...tracking.StateChange) { - for _, state := range states { - log.Println(tracking.StateChanges[state]) - } + instance := tracking.NewAccServerInstance(server, func(state *model.ServerState, states ...tracking.StateChange) { + s.stateHistoryRepo.Insert(context.Background(), &model.StateHistory{ + ServerID: server.ID, + Session: state.Session, + PlayerCount: state.PlayerCount, + DateCreated: time.Now().UTC(), + }) }) config, _ := DecodeFileName(ConfigurationJson)(server.ConfigPath) cfg := config.(model.Configuration) diff --git a/local/service/service.go b/local/service/service.go index 241337e..39dcb2e 100644 --- a/local/service/service.go +++ b/local/service/service.go @@ -16,6 +16,7 @@ func InitializeServices(c *dig.Container) { repository.InitializeRepositories(c) c.Provide(NewServerService) + c.Provide(NewStateHistoryService) c.Provide(NewApiService) c.Provide(NewConfigService) c.Provide(NewLookupService) diff --git a/local/service/stateHistory.go b/local/service/stateHistory.go new file mode 100644 index 0000000..26138b0 --- /dev/null +++ b/local/service/stateHistory.go @@ -0,0 +1,29 @@ +package service + +import ( + "acc-server-manager/local/model" + "acc-server-manager/local/repository" + + "github.com/gofiber/fiber/v2" +) + +type StateHistoryService struct { + repository *repository.StateHistoryRepository +} + +func NewStateHistoryService(repository *repository.StateHistoryRepository) *StateHistoryService { + return &StateHistoryService{ + repository: repository, + } +} + +// GetAll +// Gets All rows from StateHistory table. +// +// Args: +// context.Context: Application context +// Returns: +// string: Application version +func (as StateHistoryService) GetAll(ctx *fiber.Ctx, id int) *[]model.StateHistory { + return as.repository.GetAll(ctx.UserContext(), id) +} \ No newline at end of file diff --git a/local/utl/common/common.go b/local/utl/common/common.go index b8708e5..ce31788 100644 --- a/local/utl/common/common.go +++ b/local/utl/common/common.go @@ -19,6 +19,7 @@ type RouteGroups struct { Server fiber.Router Config fiber.Router Lookup fiber.Router + StateHistory fiber.Router } func CheckError(err error) { diff --git a/local/utl/db/db.go b/local/utl/db/db.go index 7250eb6..994b07b 100644 --- a/local/utl/db/db.go +++ b/local/utl/db/db.go @@ -55,6 +55,10 @@ func Migrate(db *gorm.DB) { if err != nil { panic("failed to migrate model.SessionType") } + err = db.AutoMigrate(&model.StateHistory{}) + if err != nil { + panic("failed to migrate model.StateHistory") + } db.FirstOrCreate(&model.ApiModel{Api: "Works"}) Seed(db) diff --git a/local/utl/tracking/tracking.go b/local/utl/tracking/tracking.go index 53c8d1f..1338156 100644 --- a/local/utl/tracking/tracking.go +++ b/local/utl/tracking/tracking.go @@ -24,10 +24,10 @@ var StateChanges = map[StateChange]string { type AccServerInstance struct { Model *model.Server State *model.ServerState - OnStateChange func(...StateChange) + OnStateChange func(*model.ServerState, ...StateChange) } -func NewAccServerInstance(server *model.Server, onStateChange func(...StateChange)) *AccServerInstance { +func NewAccServerInstance(server *model.Server, onStateChange func(*model.ServerState, ...StateChange)) *AccServerInstance { return &AccServerInstance{ Model: server, State: &model.ServerState{PlayerCount: 0}, @@ -109,8 +109,8 @@ var logStateContain = map[LogStateType]string { 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 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, @@ -136,39 +136,45 @@ func (instance *AccServerInstance) HandleLogLine(line string) { } } -func (instance *AccServerInstance) UpdateState(callback func(state *model.ServerState)) { +func (instance *AccServerInstance) UpdateState(callback func(state *model.ServerState, changes *[]StateChange)) { state := instance.State + changes := []StateChange{} state.Lock() defer state.Unlock() - callback(state) + callback(state, &changes) + if (len(changes) > 0) { + instance.OnStateChange(state, changes...) + } } func (instance *AccServerInstance) UpdatePlayerCount(count int) { - changes := []StateChange{} - instance.UpdateState(func (state *model.ServerState) { + instance.UpdateState(func (state *model.ServerState, changes *[]StateChange) { + if (count == state.PlayerCount) { + return + } if (count > 0 && state.PlayerCount == 0) { state.SessionStart = time.Now() - changes = append(changes, Session) + *changes = append(*changes, Session) } else if (count == 0) { state.SessionStart = time.Time{} - changes = append(changes, Session) + *changes = append(*changes, Session) } state.PlayerCount = count - changes = append(changes, PlayerCount) + *changes = append(*changes, PlayerCount) }) - instance.OnStateChange(changes...) } func (instance *AccServerInstance) UpdateSessionChange(session string) { - changes := []StateChange{} - instance.UpdateState(func (state *model.ServerState) { + instance.UpdateState(func (state *model.ServerState, changes *[]StateChange) { + if (session == state.Session) { + return + } if (state.PlayerCount > 0) { state.SessionStart = time.Now() } else { state.SessionStart = time.Time{} } state.Session = session - changes = append(changes, Session) + *changes = append(*changes, Session) }) - instance.OnStateChange(changes...) } \ No newline at end of file