package app import ( "context" "fmt" "time" "gitea.dwysokinski.me/twhelp/corev3/internal/domain" ) type ServerRepository interface { CreateOrUpdate(ctx context.Context, params ...domain.CreateServerParams) error List(ctx context.Context, params domain.ListServersParams) (domain.ListServersResult, error) Update(ctx context.Context, key string, params domain.UpdateServerParams) error } type ServerService struct { repo ServerRepository twSvc TWService pub ServerPublisher } func NewServerService(repo ServerRepository, twSvc TWService, pub ServerPublisher) *ServerService { return &ServerService{repo: repo, twSvc: twSvc, pub: pub} } func (svc *ServerService) Sync(ctx context.Context, payload domain.SyncServersCmdPayload) error { versionCode := payload.VersionCode() openServers, err := svc.twSvc.GetOpenServers(ctx, payload.URL()) if err != nil { return fmt.Errorf("%s: couldn't get open servers: %w", versionCode, err) } specialServers, err := svc.listAllSpecial(ctx, versionCode) if err != nil { return fmt.Errorf("%s: couldn't list special servers: %w", versionCode, err) } currentlyStoredOpenServers, err := svc.ListAllOpen(ctx, versionCode) if err != nil { return fmt.Errorf("%s: couldn't list open servers: %w", versionCode, err) } openServersWithoutSpecial := openServers.FilterOutSpecial(specialServers) serversToBeClosed, err := currentlyStoredOpenServers.Close(openServersWithoutSpecial) if err != nil { return fmt.Errorf("%s: couldn't close servers: %w", versionCode, err) } params, err := domain.NewCreateServerParams(append(openServersWithoutSpecial, serversToBeClosed...), versionCode) if err != nil { return fmt.Errorf("%s: %w", versionCode, err) } if err = svc.repo.CreateOrUpdate(ctx, params...); err != nil { return fmt.Errorf("%s: couldn't create/update servers: %w", versionCode, err) } payloads, err := domain.NewServerSyncedEventPayloads(openServersWithoutSpecial, versionCode) if err != nil { return fmt.Errorf("%s: couldn't construct domain.ServerSyncedEventPayload: %w", versionCode, err) } if err = svc.pub.EventSynced(ctx, payloads...); err != nil { return fmt.Errorf("%s: %w", versionCode, err) } return nil } func (svc *ServerService) listAllSpecial(ctx context.Context, versionCode string) (domain.Servers, error) { params := domain.NewListServersParams() if err := params.SetVersionCodes([]string{versionCode}); err != nil { return nil, err } if err := params.SetSpecial(domain.NullBool{ Value: true, Valid: true, }); err != nil { return nil, err } return svc.ListAll(ctx, params) } func (svc *ServerService) ListAllOpen(ctx context.Context, versionCode string) (domain.Servers, error) { params := domain.NewListServersParams() if err := params.SetVersionCodes([]string{versionCode}); err != nil { return nil, err } if err := params.SetOpen(domain.NullBool{ Value: true, Valid: true, }); err != nil { return nil, err } return svc.ListAll(ctx, params) } // ListAll retrieves all servers from the database based on the given params in an optimal way. // You can't specify a custom limit/cursor/sort for this operation. func (svc *ServerService) ListAll(ctx context.Context, params domain.ListServersParams) (domain.Servers, error) { if err := params.SetLimit(domain.ServerListMaxLimit); err != nil { return nil, err } if err := params.SetSort([]domain.ServerSort{domain.ServerSortKeyASC}); err != nil { return nil, err } if err := params.SetCursor(domain.ServerCursor{}); err != nil { return nil, err } var servers domain.Servers for { res, err := svc.repo.List(ctx, params) if err != nil { return nil, err } servers = append(servers, res.Servers()...) if res.Next().IsZero() { return servers, nil } if err = params.SetCursor(res.Next()); err != nil { return nil, err } } } func (svc *ServerService) List(ctx context.Context, params domain.ListServersParams) (domain.ListServersResult, error) { return svc.repo.List(ctx, params) } func (svc *ServerService) SyncConfigAndInfo(ctx context.Context, payload domain.ServerSyncedEventPayload) error { key := payload.Key() u := payload.URL() cfg, err := svc.twSvc.GetServerConfig(ctx, u) if err != nil { return fmt.Errorf("%s: couldn't get server config: %w", key, err) } buildingInfo, err := svc.twSvc.GetBuildingInfo(ctx, u) if err != nil { return fmt.Errorf("%s: couldn't get building info: %w", key, err) } unitInfo, err := svc.twSvc.GetUnitInfo(ctx, u) if err != nil { return fmt.Errorf("%s: couldn't get unit info: %w", key, err) } var updateParams domain.UpdateServerParams if err = updateParams.SetConfig(domain.NullServerConfig{ Value: cfg, Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } if err = updateParams.SetBuildingInfo(domain.NullBuildingInfo{ Value: buildingInfo, Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } if err = updateParams.SetUnitInfo(domain.NullUnitInfo{ Value: unitInfo, Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } return svc.repo.Update(ctx, key, updateParams) } func (svc *ServerService) UpdateNumTribes(ctx context.Context, payload domain.TribesSyncedEventPayload) error { key := payload.ServerKey() var updateParams domain.UpdateServerParams if err := updateParams.SetNumTribes(domain.NullInt{ Value: payload.NumTribes(), Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } if err := updateParams.SetTribeDataSyncedAt(domain.NullTime{ Value: time.Now(), Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } return svc.repo.Update(ctx, key, updateParams) } func (svc *ServerService) UpdateNumPlayers(ctx context.Context, payload domain.PlayersSyncedEventPayload) error { key := payload.ServerKey() var updateParams domain.UpdateServerParams if err := updateParams.SetNumPlayers(domain.NullInt{ Value: payload.NumPlayers(), Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } if err := updateParams.SetPlayerDataSyncedAt(domain.NullTime{ Value: time.Now(), Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } return svc.repo.Update(ctx, key, updateParams) } func (svc *ServerService) UpdateNumVillages(ctx context.Context, payload domain.VillagesSyncedEventPayload) error { key := payload.ServerKey() var updateParams domain.UpdateServerParams if err := updateParams.SetNumVillages(domain.NullInt{ Value: payload.NumVillages(), Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } if err := updateParams.SetNumPlayerVillages(domain.NullInt{ Value: payload.NumPlayerVillages(), Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } if err := updateParams.SetNumBarbarianVillages(domain.NullInt{ Value: payload.NumBarbarianVillages(), Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } if err := updateParams.SetNumBonusVillages(domain.NullInt{ Value: payload.NumBonusVillages(), Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } if err := updateParams.SetVillageDataSyncedAt(domain.NullTime{ Value: time.Now(), Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } return svc.repo.Update(ctx, key, updateParams) } func (svc *ServerService) UpdateEnnoblementDataSyncedAt( ctx context.Context, payload domain.EnnoblementsSyncedEventPayload, ) error { key := payload.ServerKey() var updateParams domain.UpdateServerParams if err := updateParams.SetEnnoblementDataSyncedAt(domain.NullTime{ Value: time.Now(), Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } return svc.repo.Update(ctx, key, updateParams) } func (svc *ServerService) UpdateTribeSnapshotsCreatedAt( ctx context.Context, payload domain.SnapshotsCreatedEventPayload, ) error { key := payload.ServerKey() var updateParams domain.UpdateServerParams if err := updateParams.SetTribeSnapshotsCreatedAt(domain.NullTime{ Value: time.Now(), Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } return svc.repo.Update(ctx, key, updateParams) } func (svc *ServerService) UpdatePlayerSnapshotsCreatedAt( ctx context.Context, payload domain.SnapshotsCreatedEventPayload, ) error { key := payload.ServerKey() var updateParams domain.UpdateServerParams if err := updateParams.SetPlayerSnapshotsCreatedAt(domain.NullTime{ Value: time.Now(), Valid: true, }); err != nil { return fmt.Errorf("%s: %w", key, err) } return svc.repo.Update(ctx, key, updateParams) } func (svc *ServerService) GetNormalByVersionCodeAndServerKey( ctx context.Context, versionCode, key string, ) (domain.Server, error) { params := domain.NewListServersParams() if err := params.SetVersionCodes([]string{versionCode}); err != nil { return domain.Server{}, err } if err := params.SetKeys([]string{key}); err != nil { return domain.Server{}, err } if err := params.SetSpecial(domain.NullBool{ Value: false, Valid: true, }); err != nil { return domain.Server{}, err } res, err := svc.repo.List(ctx, params) if err != nil { return domain.Server{}, err } servers := res.Servers() if len(servers) == 0 { return domain.Server{}, domain.ServerNotFoundError{ Key: key, } } return servers[0], nil }