add state history
This commit is contained in:
@@ -25,11 +25,13 @@ func Init(di *dig.Container, app *fiber.App) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
serverIdGroup := groups.Group("/server/:id")
|
||||||
routeGroups := &common.RouteGroups{
|
routeGroups := &common.RouteGroups{
|
||||||
Api: groups.Group("/api"),
|
Api: groups.Group("/api"),
|
||||||
Server: groups.Group("/server"),
|
Server: groups.Group("/server"),
|
||||||
Config: groups.Group("/server/:id").Group("/config"),
|
Config: serverIdGroup.Group("/config"),
|
||||||
Lookup: groups.Group("/lookup"),
|
Lookup: groups.Group("/lookup"),
|
||||||
|
StateHistory: serverIdGroup.Group("/state-history"),
|
||||||
}
|
}
|
||||||
|
|
||||||
groups.Use(basicAuthConfig)
|
groups.Use(basicAuthConfig)
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ func InitializeControllers(c *dig.Container) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic("unable to initialize lookup controller")
|
log.Panic("unable to initialize lookup controller")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = c.Invoke(NewStateHistoryController)
|
||||||
|
if err != nil {
|
||||||
|
log.Panic("unable to initialize stateHistory controller")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilteredResponse
|
// FilteredResponse
|
||||||
|
|||||||
43
local/controller/stateHistory.go
Normal file
43
local/controller/stateHistory.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
@@ -31,11 +31,6 @@ type PlayerState struct {
|
|||||||
IsConnected bool
|
IsConnected bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccServerInstance struct {
|
|
||||||
Model *Server
|
|
||||||
State *ServerState
|
|
||||||
}
|
|
||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
Session string `json:"session"`
|
Session string `json:"session"`
|
||||||
SessionStart time.Time `json:"sessionStart"`
|
SessionStart time.Time `json:"sessionStart"`
|
||||||
@@ -53,4 +48,12 @@ type ServerState struct {
|
|||||||
MaxConnections int `json:"maxConnections"`
|
MaxConnections int `json:"maxConnections"`
|
||||||
// Players map[int]*PlayerState
|
// Players map[int]*PlayerState
|
||||||
// etc.
|
// 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"`
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
// *dig.Container: Dig Container
|
// *dig.Container: Dig Container
|
||||||
func InitializeRepositories(c *dig.Container) {
|
func InitializeRepositories(c *dig.Container) {
|
||||||
c.Provide(NewApiRepository)
|
c.Provide(NewApiRepository)
|
||||||
|
c.Provide(NewStateHistoryRepository)
|
||||||
c.Provide(NewServerRepository)
|
c.Provide(NewServerRepository)
|
||||||
c.Provide(NewConfigRepository)
|
c.Provide(NewConfigRepository)
|
||||||
c.Provide(NewLookupRepository)
|
c.Provide(NewLookupRepository)
|
||||||
|
|||||||
45
local/repository/stateHistory.go
Normal file
45
local/repository/stateHistory.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -8,22 +8,25 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerService struct {
|
type ServerService struct {
|
||||||
repository *repository.ServerRepository
|
repository *repository.ServerRepository
|
||||||
|
stateHistoryRepo *repository.StateHistoryRepository
|
||||||
apiService *ApiService
|
apiService *ApiService
|
||||||
instances sync.Map
|
instances sync.Map
|
||||||
configService *ConfigService
|
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{
|
service := &ServerService{
|
||||||
repository: repository,
|
repository: repository,
|
||||||
apiService: apiService,
|
apiService: apiService,
|
||||||
configService: configService,
|
configService: configService,
|
||||||
|
stateHistoryRepo: stateHistoryRepo,
|
||||||
}
|
}
|
||||||
servers := repository.GetAll(context.Background())
|
servers := repository.GetAll(context.Background())
|
||||||
for _, server := range *servers {
|
for _, server := range *servers {
|
||||||
@@ -40,10 +43,13 @@ func NewServerService(repository *repository.ServerRepository, apiService *ApiSe
|
|||||||
|
|
||||||
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 := tracking.NewAccServerInstance(server, func(states ...tracking.StateChange) {
|
instance := tracking.NewAccServerInstance(server, func(state *model.ServerState, states ...tracking.StateChange) {
|
||||||
for _, state := range states {
|
s.stateHistoryRepo.Insert(context.Background(), &model.StateHistory{
|
||||||
log.Println(tracking.StateChanges[state])
|
ServerID: server.ID,
|
||||||
}
|
Session: state.Session,
|
||||||
|
PlayerCount: state.PlayerCount,
|
||||||
|
DateCreated: time.Now().UTC(),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
config, _ := DecodeFileName(ConfigurationJson)(server.ConfigPath)
|
config, _ := DecodeFileName(ConfigurationJson)(server.ConfigPath)
|
||||||
cfg := config.(model.Configuration)
|
cfg := config.(model.Configuration)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ func InitializeServices(c *dig.Container) {
|
|||||||
repository.InitializeRepositories(c)
|
repository.InitializeRepositories(c)
|
||||||
|
|
||||||
c.Provide(NewServerService)
|
c.Provide(NewServerService)
|
||||||
|
c.Provide(NewStateHistoryService)
|
||||||
c.Provide(NewApiService)
|
c.Provide(NewApiService)
|
||||||
c.Provide(NewConfigService)
|
c.Provide(NewConfigService)
|
||||||
c.Provide(NewLookupService)
|
c.Provide(NewLookupService)
|
||||||
|
|||||||
29
local/service/stateHistory.go
Normal file
29
local/service/stateHistory.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ type RouteGroups struct {
|
|||||||
Server fiber.Router
|
Server fiber.Router
|
||||||
Config fiber.Router
|
Config fiber.Router
|
||||||
Lookup fiber.Router
|
Lookup fiber.Router
|
||||||
|
StateHistory fiber.Router
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckError(err error) {
|
func CheckError(err error) {
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ func Migrate(db *gorm.DB) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to migrate model.SessionType")
|
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"})
|
db.FirstOrCreate(&model.ApiModel{Api: "Works"})
|
||||||
|
|
||||||
Seed(db)
|
Seed(db)
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ var StateChanges = map[StateChange]string {
|
|||||||
type AccServerInstance struct {
|
type AccServerInstance struct {
|
||||||
Model *model.Server
|
Model *model.Server
|
||||||
State *model.ServerState
|
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{
|
return &AccServerInstance{
|
||||||
Model: server,
|
Model: server,
|
||||||
State: &model.ServerState{PlayerCount: 0},
|
State: &model.ServerState{PlayerCount: 0},
|
||||||
@@ -109,8 +109,8 @@ var logStateContain = map[LogStateType]string {
|
|||||||
|
|
||||||
var sessionChangeRegex = NewRegexHandler(`Session changed: (\w+) -> (\w+)`, logStateContain[SessionChange])
|
var sessionChangeRegex = NewRegexHandler(`Session changed: (\w+) -> (\w+)`, logStateContain[SessionChange])
|
||||||
var leaderboardUpdateRegex = NewRegexHandler(`Updated leaderboard for (\d+) clients`, logStateContain[LeaderboardUpdate])
|
var leaderboardUpdateRegex = NewRegexHandler(`Updated leaderboard for (\d+) clients`, logStateContain[LeaderboardUpdate])
|
||||||
var udpCountRegex = NewRegexHandler(`Udp message count ((\d+) client`, logStateContain[UDPCount])
|
var udpCountRegex = NewRegexHandler(`Udp message count (\d+) client`, logStateContain[UDPCount])
|
||||||
var clientsOnlineRegex = NewRegexHandler(`(\d+) client(s) online`, logStateContain[ClientsOnline])
|
var clientsOnlineRegex = NewRegexHandler(`(\d+) client\(s\) online`, logStateContain[ClientsOnline])
|
||||||
|
|
||||||
var logStateRegex = map[LogStateType]*StateRegexHandler {
|
var logStateRegex = map[LogStateType]*StateRegexHandler {
|
||||||
SessionChange: sessionChangeRegex,
|
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
|
state := instance.State
|
||||||
|
changes := []StateChange{}
|
||||||
state.Lock()
|
state.Lock()
|
||||||
defer state.Unlock()
|
defer state.Unlock()
|
||||||
callback(state)
|
callback(state, &changes)
|
||||||
|
if (len(changes) > 0) {
|
||||||
|
instance.OnStateChange(state, changes...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (instance *AccServerInstance) UpdatePlayerCount(count int) {
|
func (instance *AccServerInstance) UpdatePlayerCount(count int) {
|
||||||
changes := []StateChange{}
|
instance.UpdateState(func (state *model.ServerState, changes *[]StateChange) {
|
||||||
instance.UpdateState(func (state *model.ServerState) {
|
if (count == state.PlayerCount) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (count > 0 && state.PlayerCount == 0) {
|
if (count > 0 && state.PlayerCount == 0) {
|
||||||
state.SessionStart = time.Now()
|
state.SessionStart = time.Now()
|
||||||
changes = append(changes, Session)
|
*changes = append(*changes, Session)
|
||||||
} else if (count == 0) {
|
} else if (count == 0) {
|
||||||
state.SessionStart = time.Time{}
|
state.SessionStart = time.Time{}
|
||||||
changes = append(changes, Session)
|
*changes = append(*changes, Session)
|
||||||
}
|
}
|
||||||
state.PlayerCount = count
|
state.PlayerCount = count
|
||||||
changes = append(changes, PlayerCount)
|
*changes = append(*changes, PlayerCount)
|
||||||
})
|
})
|
||||||
instance.OnStateChange(changes...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (instance *AccServerInstance) UpdateSessionChange(session string) {
|
func (instance *AccServerInstance) UpdateSessionChange(session string) {
|
||||||
changes := []StateChange{}
|
instance.UpdateState(func (state *model.ServerState, changes *[]StateChange) {
|
||||||
instance.UpdateState(func (state *model.ServerState) {
|
if (session == state.Session) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (state.PlayerCount > 0) {
|
if (state.PlayerCount > 0) {
|
||||||
state.SessionStart = time.Now()
|
state.SessionStart = time.Now()
|
||||||
} else {
|
} else {
|
||||||
state.SessionStart = time.Time{}
|
state.SessionStart = time.Time{}
|
||||||
}
|
}
|
||||||
state.Session = session
|
state.Session = session
|
||||||
changes = append(changes, Session)
|
*changes = append(*changes, Session)
|
||||||
})
|
})
|
||||||
instance.OnStateChange(changes...)
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user