core/internal/domain/server.go

395 lines
8.2 KiB
Go

package domain
import (
"errors"
"fmt"
"net/url"
"slices"
"time"
)
type Server struct {
key string
versionCode string
url *url.URL
open bool
special bool
numPlayers int
numTribes int
numVillages int
numPlayerVillages int
numBarbarianVillages int
numBonusVillages int
config ServerConfig
buildingInfo BuildingInfo
unitInfo UnitInfo
createdAt time.Time
playerDataSyncedAt time.Time
playerSnapshotsCreatedAt time.Time
tribeDataSyncedAt time.Time
tribeSnapshotsCreatedAt time.Time
villageDataSyncedAt time.Time
ennoblementDataSyncedAt time.Time
}
// UnmarshalServerFromDatabase unmarshals Server from the database.
//
// It should be used only for unmarshalling from the database!
// You can't use UnmarshalServerFromDatabase as constructor - It may put domain into the invalid state!
func UnmarshalServerFromDatabase(
key string,
versionCode string,
rawURL string,
open bool,
special bool,
numPlayers int,
numTribes int,
numVillages int,
numPlayerVillages int,
numBarbarianVillages int,
numBonusVillages int,
config ServerConfig,
buildingInfo BuildingInfo,
unitInfo UnitInfo,
createdAt time.Time,
playerDataSyncedAt time.Time,
playerSnapshotsCreatedAt time.Time,
tribeDataSyncedAt time.Time,
tribeSnapshotsCreatedAt time.Time,
villageDataSyncedAt time.Time,
ennoblementDataSyncedAt time.Time,
) (Server, error) {
if key == "" {
return Server{}, errors.New("key can't be blank")
}
if versionCode == "" {
return Server{}, errors.New("version code can't be blank")
}
u, err := parseURL(rawURL)
if err != nil {
return Server{}, err
}
return Server{
key: key,
url: u,
open: open,
special: special,
numPlayers: numPlayers,
numTribes: numTribes,
numVillages: numVillages,
numPlayerVillages: numPlayerVillages,
numBarbarianVillages: numBarbarianVillages,
numBonusVillages: numBonusVillages,
config: config,
buildingInfo: buildingInfo,
unitInfo: unitInfo,
createdAt: createdAt,
playerDataSyncedAt: playerDataSyncedAt,
playerSnapshotsCreatedAt: playerSnapshotsCreatedAt,
tribeDataSyncedAt: tribeDataSyncedAt,
tribeSnapshotsCreatedAt: tribeSnapshotsCreatedAt,
villageDataSyncedAt: villageDataSyncedAt,
ennoblementDataSyncedAt: ennoblementDataSyncedAt,
versionCode: versionCode,
}, nil
}
func (s Server) Key() string {
return s.key
}
func (s Server) VersionCode() string {
return s.versionCode
}
func (s Server) URL() *url.URL {
return s.url
}
func (s Server) Open() bool {
return s.open
}
func (s Server) Special() bool {
return s.special
}
func (s Server) NumPlayers() int {
return s.numPlayers
}
func (s Server) NumTribes() int {
return s.numTribes
}
func (s Server) NumVillages() int {
return s.numVillages
}
func (s Server) NumPlayerVillages() int {
return s.numPlayerVillages
}
func (s Server) NumBarbarianVillages() int {
return s.numBarbarianVillages
}
func (s Server) NumBonusVillages() int {
return s.numBonusVillages
}
func (s Server) Config() ServerConfig {
return s.config
}
func (s Server) BuildingInfo() BuildingInfo {
return s.buildingInfo
}
func (s Server) UnitInfo() UnitInfo {
return s.unitInfo
}
func (s Server) CreatedAt() time.Time {
return s.createdAt
}
func (s Server) PlayerDataSyncedAt() time.Time {
return s.playerDataSyncedAt
}
func (s Server) PlayerSnapshotsCreatedAt() time.Time {
return s.playerSnapshotsCreatedAt
}
func (s Server) TribeDataSyncedAt() time.Time {
return s.tribeDataSyncedAt
}
func (s Server) TribeSnapshotsCreatedAt() time.Time {
return s.tribeSnapshotsCreatedAt
}
func (s Server) VillageDataSyncedAt() time.Time {
return s.villageDataSyncedAt
}
func (s Server) EnnoblementDataSyncedAt() time.Time {
return s.ennoblementDataSyncedAt
}
type Servers []Server
// Close finds all servers with Server.Open returning true that are not in the given slice with open servers
// and then converts them to BaseServers with the corrected open value.
func (ss Servers) Close(open BaseServers) (BaseServers, error) {
res := make(BaseServers, 0, len(ss))
for _, s := range ss {
if !s.Open() || slices.ContainsFunc(open, func(openServer BaseServer) bool {
return openServer.Key() == s.Key() && openServer.Open() == s.Open()
}) {
continue
}
base, err := NewBaseServer(s.Key(), s.URL().String(), false)
if err != nil {
return nil, fmt.Errorf("couldn't construct BaseServer for server with key '%s': %w", s.Key(), err)
}
res = append(res, base)
}
return res, nil
}
type CreateServerParams struct {
base BaseServer
versionCode string
}
func NewCreateServerParams(servers BaseServers, versionCode string) ([]CreateServerParams, error) {
if err := validateVersionCode(versionCode); err != nil {
return nil, ValidationError{
Err: err,
Field: "versionCode",
}
}
res := make([]CreateServerParams, 0, len(servers))
for i, s := range servers {
if s.IsZero() {
return nil, fmt.Errorf("servers[%d] is an empty struct", i)
}
res = append(res, CreateServerParams{
base: s,
versionCode: versionCode,
})
}
return res, nil
}
func (params CreateServerParams) Base() BaseServer {
return params.base
}
func (params CreateServerParams) VersionCode() string {
return params.versionCode
}
type ServerSort uint8
const (
ServerSortKeyASC ServerSort = iota + 1
ServerSortKeyDESC
ServerSortOpenASC
ServerSortOpenDESC
)
const ServerListMaxLimit = 500
type ListServersParams struct {
keys []string
keyGT NullString
versionCodes []string
open NullBool
special NullBool
sort []ServerSort
limit int
offset int
}
func NewListServersParams() ListServersParams {
return ListServersParams{
sort: []ServerSort{ServerSortKeyASC},
limit: ServerListMaxLimit,
special: NullBool{
Value: false,
Valid: true,
},
}
}
func (params *ListServersParams) Keys() []string {
return params.keys
}
func (params *ListServersParams) SetKeys(keys []string) error {
params.keys = keys
return nil
}
func (params *ListServersParams) KeyGT() NullString {
return params.keyGT
}
func (params *ListServersParams) SetKeyGT(keyGT NullString) error {
params.keyGT = keyGT
return nil
}
func (params *ListServersParams) VersionCodes() []string {
return params.versionCodes
}
func (params *ListServersParams) SetVersionCodes(versionCodes []string) error {
params.versionCodes = versionCodes
return nil
}
func (params *ListServersParams) Open() NullBool {
return params.open
}
func (params *ListServersParams) SetOpen(open NullBool) error {
params.open = open
return nil
}
func (params *ListServersParams) Special() NullBool {
return params.special
}
func (params *ListServersParams) SetSpecial(special NullBool) error {
params.special = special
return nil
}
func (params *ListServersParams) Sort() []ServerSort {
return params.sort
}
const (
serverSortMinLength = 1
serverSortMaxLength = 2
)
func (params *ListServersParams) SetSort(sort []ServerSort) error {
if err := validateSliceLen(sort, serverSortMinLength, serverSortMaxLength); err != nil {
return ValidationError{
Field: "sort",
Err: err,
}
}
params.sort = sort
return nil
}
func (params *ListServersParams) Limit() int {
return params.limit
}
func (params *ListServersParams) SetLimit(limit int) error {
if limit <= 0 {
return ValidationError{
Field: "limit",
Err: MinGreaterEqualError{
Min: 1,
Current: limit,
},
}
}
if limit > ServerListMaxLimit {
return ValidationError{
Field: "limit",
Err: MaxLessEqualError{
Max: ServerListMaxLimit,
Current: limit,
},
}
}
params.limit = limit
return nil
}
func (params *ListServersParams) Offset() int {
return params.offset
}
func (params *ListServersParams) SetOffset(offset int) error {
if offset < 0 {
return ValidationError{
Field: "offset",
Err: MinGreaterEqualError{
Min: 0,
Current: offset,
},
}
}
params.offset = offset
return nil
}