2024-01-02 09:54:11 +00:00
|
|
|
package domain
|
|
|
|
|
|
|
|
import (
|
|
|
|
"cmp"
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
"net/url"
|
|
|
|
"slices"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
villageNameMinLength = 1
|
|
|
|
villageNameMaxLength = 150
|
|
|
|
villageContinentMinLength = 1
|
|
|
|
villageContinentMaxLength = 5
|
|
|
|
)
|
|
|
|
|
|
|
|
type Village struct {
|
|
|
|
id int
|
|
|
|
serverKey string
|
|
|
|
name string
|
|
|
|
points int
|
|
|
|
x int
|
|
|
|
y int
|
|
|
|
continent string
|
|
|
|
bonus int
|
|
|
|
playerID int
|
|
|
|
profileURL *url.URL
|
|
|
|
createdAt time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
const villageModelName = "Village"
|
|
|
|
|
|
|
|
// UnmarshalVillageFromDatabase unmarshals Village from the database.
|
|
|
|
//
|
|
|
|
// It should be used only for unmarshalling from the database!
|
|
|
|
// You can't use UnmarshalVillageFromDatabase as constructor - It may put domain into the invalid state!
|
|
|
|
func UnmarshalVillageFromDatabase(
|
|
|
|
id int,
|
|
|
|
serverKey string,
|
|
|
|
name string,
|
|
|
|
points int,
|
|
|
|
x int,
|
|
|
|
y int,
|
|
|
|
continent string,
|
|
|
|
bonus int,
|
|
|
|
playerID int,
|
|
|
|
rawProfileURL string,
|
|
|
|
createdAt time.Time,
|
|
|
|
) (Village, error) {
|
|
|
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
|
|
|
return Village{}, ValidationError{
|
|
|
|
Model: villageModelName,
|
|
|
|
Field: "id",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := validateServerKey(serverKey); err != nil {
|
|
|
|
return Village{}, ValidationError{
|
|
|
|
Model: villageModelName,
|
|
|
|
Field: "serverKey",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
profileURL, err := parseURL(rawProfileURL)
|
|
|
|
if err != nil {
|
|
|
|
return Village{}, ValidationError{
|
|
|
|
Model: villageModelName,
|
|
|
|
Field: "profileURL",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Village{
|
|
|
|
id: id,
|
|
|
|
serverKey: serverKey,
|
|
|
|
name: name,
|
|
|
|
points: points,
|
|
|
|
x: x,
|
|
|
|
y: y,
|
|
|
|
continent: continent,
|
|
|
|
bonus: bonus,
|
|
|
|
playerID: playerID,
|
|
|
|
profileURL: profileURL,
|
|
|
|
createdAt: createdAt,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v Village) ID() int {
|
|
|
|
return v.id
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v Village) ServerKey() string {
|
|
|
|
return v.serverKey
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v Village) Name() string {
|
|
|
|
return v.name
|
|
|
|
}
|
|
|
|
|
2024-03-05 06:42:37 +00:00
|
|
|
func (v Village) FullName() string {
|
2024-03-10 09:37:44 +00:00
|
|
|
return formatVillageFullName(v)
|
2024-03-05 06:42:37 +00:00
|
|
|
}
|
|
|
|
|
2024-01-02 09:54:11 +00:00
|
|
|
func (v Village) Points() int {
|
|
|
|
return v.points
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v Village) X() int {
|
|
|
|
return v.x
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v Village) Y() int {
|
|
|
|
return v.y
|
|
|
|
}
|
|
|
|
|
2024-03-06 07:14:31 +00:00
|
|
|
func (v Village) Coords() Coords {
|
|
|
|
return Coords{
|
|
|
|
x: v.x,
|
|
|
|
y: v.y,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-02 09:54:11 +00:00
|
|
|
func (v Village) Continent() string {
|
|
|
|
return v.continent
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v Village) Bonus() int {
|
|
|
|
return v.bonus
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v Village) PlayerID() int {
|
|
|
|
return v.playerID
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v Village) ProfileURL() *url.URL {
|
2024-03-11 07:37:04 +00:00
|
|
|
if v.profileURL == nil {
|
|
|
|
return &url.URL{}
|
|
|
|
}
|
2024-01-02 09:54:11 +00:00
|
|
|
return v.profileURL
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v Village) CreatedAt() time.Time {
|
|
|
|
return v.createdAt
|
|
|
|
}
|
|
|
|
|
2024-03-06 06:35:26 +00:00
|
|
|
func (v Village) WithRelations(player NullPlayerMetaWithRelations) VillageWithRelations {
|
|
|
|
return VillageWithRelations{
|
|
|
|
village: v,
|
|
|
|
player: player,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v Village) ToCursor() (VillageCursor, error) {
|
|
|
|
return NewVillageCursor(v.id, v.serverKey)
|
|
|
|
}
|
|
|
|
|
2024-03-10 09:37:44 +00:00
|
|
|
func (v Village) Meta() VillageMeta {
|
|
|
|
return VillageMeta{
|
|
|
|
id: v.id,
|
|
|
|
name: v.name,
|
|
|
|
x: v.x,
|
|
|
|
y: v.y,
|
|
|
|
continent: v.continent,
|
|
|
|
profileURL: v.profileURL,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-02 09:54:11 +00:00
|
|
|
func (v Village) Base() BaseVillage {
|
|
|
|
return BaseVillage{
|
|
|
|
id: v.id,
|
|
|
|
name: v.name,
|
|
|
|
points: v.points,
|
|
|
|
x: v.x,
|
|
|
|
y: v.y,
|
|
|
|
continent: v.continent,
|
|
|
|
bonus: v.bonus,
|
|
|
|
playerID: v.playerID,
|
|
|
|
profileURL: v.profileURL,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-05 06:14:41 +00:00
|
|
|
func (v Village) IsZero() bool {
|
|
|
|
return v == Village{}
|
|
|
|
}
|
|
|
|
|
2024-03-24 07:13:26 +00:00
|
|
|
// active villages must be sorted in ascending order by ID
|
|
|
|
func (v Village) canBeDeleted(serverKey string, active BaseVillages) bool {
|
|
|
|
if v.serverKey != serverKey {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
_, found := slices.BinarySearchFunc(active, v, func(a BaseVillage, b Village) int {
|
|
|
|
return cmp.Compare(a.ID(), b.id)
|
|
|
|
})
|
|
|
|
|
|
|
|
return !found
|
|
|
|
}
|
|
|
|
|
2024-01-02 09:54:11 +00:00
|
|
|
type Villages []Village
|
|
|
|
|
2024-01-06 09:44:14 +00:00
|
|
|
// Delete finds all villages with the given serverKey that are not in the given slice with active villages
|
|
|
|
// and returns their ids. Both slices must be sorted in ascending order by ID
|
|
|
|
// + if vs contains villages from different servers. they must be sorted in ascending order by server key.
|
|
|
|
func (vs Villages) Delete(serverKey string, active BaseVillages) []int {
|
2024-03-24 07:13:26 +00:00
|
|
|
// villages are deleted now and then, there is no point in prereallocating this slice
|
2024-01-02 09:54:11 +00:00
|
|
|
//nolint:prealloc
|
|
|
|
var toDelete []int
|
|
|
|
|
|
|
|
for _, v := range vs {
|
2024-03-24 07:13:26 +00:00
|
|
|
if !v.canBeDeleted(serverKey, active) {
|
2024-01-02 09:54:11 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
toDelete = append(toDelete, v.ID())
|
|
|
|
}
|
|
|
|
|
|
|
|
return toDelete
|
|
|
|
}
|
|
|
|
|
2024-03-06 06:35:26 +00:00
|
|
|
type VillageWithRelations struct {
|
|
|
|
village Village
|
|
|
|
player NullPlayerMetaWithRelations
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v VillageWithRelations) Village() Village {
|
|
|
|
return v.village
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v VillageWithRelations) Player() NullPlayerMetaWithRelations {
|
|
|
|
return v.player
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v VillageWithRelations) IsZero() bool {
|
|
|
|
return v.village.IsZero()
|
|
|
|
}
|
|
|
|
|
|
|
|
type VillagesWithRelations []VillageWithRelations
|
|
|
|
|
2024-03-10 09:37:44 +00:00
|
|
|
type VillageMeta struct {
|
|
|
|
id int
|
|
|
|
name string
|
|
|
|
x int
|
|
|
|
y int
|
|
|
|
continent string
|
|
|
|
profileURL *url.URL
|
|
|
|
}
|
|
|
|
|
|
|
|
const villageMetaModelName = "VillageMeta"
|
|
|
|
|
|
|
|
// UnmarshalVillageMetaFromDatabase unmarshals VillageMeta from the database.
|
|
|
|
//
|
|
|
|
// It should be used only for unmarshalling from the database!
|
|
|
|
// You can't use UnmarshalVillageMetaFromDatabase as constructor - It may put domain into the invalid state!
|
|
|
|
func UnmarshalVillageMetaFromDatabase(
|
|
|
|
id int,
|
|
|
|
name string,
|
|
|
|
x int,
|
|
|
|
y int,
|
|
|
|
continent string,
|
|
|
|
rawProfileURL string,
|
|
|
|
) (VillageMeta, error) {
|
|
|
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
|
|
|
return VillageMeta{}, ValidationError{
|
|
|
|
Model: villageMetaModelName,
|
|
|
|
Field: "id",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
profileURL, err := parseURL(rawProfileURL)
|
|
|
|
if err != nil {
|
|
|
|
return VillageMeta{}, ValidationError{
|
|
|
|
Model: villageMetaModelName,
|
|
|
|
Field: "profileURL",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return VillageMeta{
|
|
|
|
id: id,
|
|
|
|
name: name,
|
|
|
|
x: x,
|
|
|
|
y: y,
|
|
|
|
continent: continent,
|
|
|
|
profileURL: profileURL,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v VillageMeta) ID() int {
|
|
|
|
return v.id
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v VillageMeta) Name() string {
|
|
|
|
return v.name
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v VillageMeta) FullName() string {
|
|
|
|
return formatVillageFullName(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v VillageMeta) X() int {
|
|
|
|
return v.x
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v VillageMeta) Y() int {
|
|
|
|
return v.y
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v VillageMeta) Continent() string {
|
|
|
|
return v.continent
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v VillageMeta) ProfileURL() *url.URL {
|
2024-03-11 07:37:04 +00:00
|
|
|
if v.profileURL == nil {
|
|
|
|
return &url.URL{}
|
|
|
|
}
|
2024-03-10 09:37:44 +00:00
|
|
|
return v.profileURL
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v VillageMeta) IsZero() bool {
|
|
|
|
return v == VillageMeta{}
|
|
|
|
}
|
|
|
|
|
2024-03-11 06:41:32 +00:00
|
|
|
func (v VillageMeta) WithRelations(player NullPlayerMetaWithRelations) VillageMetaWithRelations {
|
|
|
|
return VillageMetaWithRelations{
|
|
|
|
village: v,
|
|
|
|
player: player,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type VillageMetaWithRelations struct {
|
|
|
|
village VillageMeta
|
|
|
|
player NullPlayerMetaWithRelations
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v VillageMetaWithRelations) Village() VillageMeta {
|
|
|
|
return v.village
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v VillageMetaWithRelations) Player() NullPlayerMetaWithRelations {
|
|
|
|
return v.player
|
|
|
|
}
|
|
|
|
|
2024-01-02 09:54:11 +00:00
|
|
|
type CreateVillageParams struct {
|
|
|
|
base BaseVillage
|
|
|
|
serverKey string
|
|
|
|
}
|
|
|
|
|
|
|
|
const createVillageParamsModelName = "CreateVillageParams"
|
|
|
|
|
|
|
|
func NewCreateVillageParams(serverKey string, villages BaseVillages) ([]CreateVillageParams, error) {
|
|
|
|
if err := validateServerKey(serverKey); err != nil {
|
|
|
|
return nil, ValidationError{
|
|
|
|
Model: createVillageParamsModelName,
|
|
|
|
Field: "serverKey",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
params := make([]CreateVillageParams, 0, len(villages))
|
|
|
|
|
|
|
|
for i, v := range villages {
|
|
|
|
if v.IsZero() {
|
|
|
|
return nil, fmt.Errorf("villages[%d] is an empty struct", i)
|
|
|
|
}
|
|
|
|
|
|
|
|
params = append(params, CreateVillageParams{
|
|
|
|
base: v,
|
|
|
|
serverKey: serverKey,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return params, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params CreateVillageParams) Base() BaseVillage {
|
|
|
|
return params.base
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params CreateVillageParams) ServerKey() string {
|
|
|
|
return params.serverKey
|
|
|
|
}
|
|
|
|
|
|
|
|
type VillageSort uint8
|
|
|
|
|
|
|
|
const (
|
|
|
|
VillageSortIDASC VillageSort = iota + 1
|
|
|
|
VillageSortIDDESC
|
|
|
|
VillageSortServerKeyASC
|
|
|
|
VillageSortServerKeyDESC
|
|
|
|
)
|
|
|
|
|
2024-03-02 08:49:42 +00:00
|
|
|
// IsInConflict returns true if two sorts can't be used together (e.g. VillageSortIDASC and VillageSortIDDESC).
|
|
|
|
func (s VillageSort) IsInConflict(s2 VillageSort) bool {
|
2024-03-15 06:57:21 +00:00
|
|
|
return isSortInConflict(s, s2)
|
2024-03-02 08:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//nolint:gocyclo
|
|
|
|
func (s VillageSort) String() string {
|
|
|
|
switch s {
|
|
|
|
case VillageSortIDASC:
|
|
|
|
return "id:ASC"
|
|
|
|
case VillageSortIDDESC:
|
|
|
|
return "id:DESC"
|
|
|
|
case VillageSortServerKeyASC:
|
|
|
|
return "serverKey:ASC"
|
|
|
|
case VillageSortServerKeyDESC:
|
|
|
|
return "serverKey:DESC"
|
|
|
|
default:
|
|
|
|
return "unknown village sort"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-05 06:14:41 +00:00
|
|
|
type VillageCursor struct {
|
|
|
|
id int
|
|
|
|
serverKey string
|
|
|
|
}
|
|
|
|
|
|
|
|
const villageCursorModelName = "VillageCursor"
|
|
|
|
|
|
|
|
func NewVillageCursor(id int, serverKey string) (VillageCursor, error) {
|
|
|
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
|
|
|
return VillageCursor{}, ValidationError{
|
|
|
|
Model: villageCursorModelName,
|
|
|
|
Field: "id",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := validateServerKey(serverKey); err != nil {
|
|
|
|
return VillageCursor{}, ValidationError{
|
|
|
|
Model: villageCursorModelName,
|
|
|
|
Field: "serverKey",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return VillageCursor{
|
|
|
|
id: id,
|
|
|
|
serverKey: serverKey,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//nolint:gocyclo
|
|
|
|
func decodeVillageCursor(encoded string) (VillageCursor, error) {
|
|
|
|
m, err := decodeCursor(encoded)
|
|
|
|
if err != nil {
|
|
|
|
return VillageCursor{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
id, err := m.int("id")
|
|
|
|
if err != nil {
|
|
|
|
return VillageCursor{}, ErrInvalidCursor
|
|
|
|
}
|
|
|
|
|
|
|
|
serverKey, err := m.string("serverKey")
|
|
|
|
if err != nil {
|
|
|
|
return VillageCursor{}, ErrInvalidCursor
|
|
|
|
}
|
|
|
|
|
|
|
|
vc, err := NewVillageCursor(
|
|
|
|
id,
|
|
|
|
serverKey,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return VillageCursor{}, ErrInvalidCursor
|
|
|
|
}
|
|
|
|
|
|
|
|
return vc, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (vc VillageCursor) ID() int {
|
|
|
|
return vc.id
|
|
|
|
}
|
|
|
|
|
|
|
|
func (vc VillageCursor) ServerKey() string {
|
|
|
|
return vc.serverKey
|
|
|
|
}
|
|
|
|
|
|
|
|
func (vc VillageCursor) IsZero() bool {
|
|
|
|
return vc == VillageCursor{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (vc VillageCursor) Encode() string {
|
|
|
|
if vc.IsZero() {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return encodeCursor([]keyValuePair{
|
|
|
|
{"id", vc.id},
|
|
|
|
{"serverKey", vc.serverKey},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-01-02 09:54:11 +00:00
|
|
|
type ListVillagesParams struct {
|
|
|
|
ids []int
|
|
|
|
serverKeys []string
|
2024-03-06 07:14:31 +00:00
|
|
|
coords []Coords
|
2024-03-09 07:38:23 +00:00
|
|
|
playerIDs []int
|
|
|
|
tribeIDs []int
|
2024-01-02 09:54:11 +00:00
|
|
|
sort []VillageSort
|
2024-03-05 06:14:41 +00:00
|
|
|
cursor VillageCursor
|
2024-01-02 09:54:11 +00:00
|
|
|
limit int
|
|
|
|
}
|
|
|
|
|
2024-01-27 08:37:12 +00:00
|
|
|
const (
|
|
|
|
VillageListMaxLimit = 500
|
|
|
|
listVillagesParamsModelName = "ListVillagesParams"
|
|
|
|
)
|
2024-01-02 09:54:11 +00:00
|
|
|
|
|
|
|
func NewListVillagesParams() ListVillagesParams {
|
|
|
|
return ListVillagesParams{
|
|
|
|
sort: []VillageSort{
|
|
|
|
VillageSortServerKeyASC,
|
|
|
|
VillageSortIDASC,
|
|
|
|
},
|
|
|
|
limit: VillageListMaxLimit,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params *ListVillagesParams) IDs() []int {
|
|
|
|
return params.ids
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params *ListVillagesParams) SetIDs(ids []int) error {
|
|
|
|
for i, id := range ids {
|
2024-02-28 06:59:10 +00:00
|
|
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
2024-01-02 09:54:11 +00:00
|
|
|
return SliceElementValidationError{
|
|
|
|
Model: listVillagesParamsModelName,
|
|
|
|
Field: "ids",
|
|
|
|
Index: i,
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
params.ids = ids
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params *ListVillagesParams) ServerKeys() []string {
|
|
|
|
return params.serverKeys
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params *ListVillagesParams) SetServerKeys(serverKeys []string) error {
|
2024-03-03 06:23:46 +00:00
|
|
|
for i, sk := range serverKeys {
|
|
|
|
if err := validateServerKey(sk); err != nil {
|
|
|
|
return SliceElementValidationError{
|
|
|
|
Model: listVillagesParamsModelName,
|
|
|
|
Field: "serverKeys",
|
|
|
|
Index: i,
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-02 09:54:11 +00:00
|
|
|
params.serverKeys = serverKeys
|
2024-03-03 06:23:46 +00:00
|
|
|
|
2024-01-02 09:54:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-03-06 07:14:31 +00:00
|
|
|
func (params *ListVillagesParams) Coords() []Coords {
|
|
|
|
return params.coords
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
villageCoordsMinLength = 0
|
|
|
|
villageCoordsMaxLength = 200
|
|
|
|
)
|
|
|
|
|
|
|
|
func (params *ListVillagesParams) SetCoords(coords []Coords) error {
|
|
|
|
if err := validateSliceLen(coords, villageCoordsMinLength, villageCoordsMaxLength); err != nil {
|
|
|
|
return ValidationError{
|
|
|
|
Model: listVillagesParamsModelName,
|
|
|
|
Field: "coords",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
params.coords = coords
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params *ListVillagesParams) SetCoordsString(rawCoords []string) error {
|
|
|
|
if err := validateSliceLen(rawCoords, villageCoordsMinLength, villageCoordsMaxLength); err != nil {
|
|
|
|
return ValidationError{
|
|
|
|
Model: listVillagesParamsModelName,
|
|
|
|
Field: "coords",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
coords := make([]Coords, 0, len(rawCoords))
|
|
|
|
|
|
|
|
for i, s := range rawCoords {
|
|
|
|
c, err := newCoords(s)
|
|
|
|
if err != nil {
|
|
|
|
return SliceElementValidationError{
|
|
|
|
Model: listVillagesParamsModelName,
|
|
|
|
Field: "coords",
|
|
|
|
Index: i,
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
coords = append(coords, c)
|
|
|
|
}
|
|
|
|
|
|
|
|
params.coords = coords
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-03-09 07:38:23 +00:00
|
|
|
func (params *ListVillagesParams) PlayerIDs() []int {
|
|
|
|
return params.playerIDs
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params *ListVillagesParams) SetPlayerIDs(playerIDs []int) error {
|
|
|
|
for i, id := range playerIDs {
|
|
|
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
|
|
|
return SliceElementValidationError{
|
|
|
|
Model: listVillagesParamsModelName,
|
|
|
|
Field: "playerIDs",
|
|
|
|
Index: i,
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
params.playerIDs = playerIDs
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params *ListVillagesParams) TribeIDs() []int {
|
|
|
|
return params.tribeIDs
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params *ListVillagesParams) SetTribeIDs(tribeIDs []int) error {
|
|
|
|
for i, id := range tribeIDs {
|
|
|
|
if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
|
|
|
|
return SliceElementValidationError{
|
|
|
|
Model: listVillagesParamsModelName,
|
|
|
|
Field: "tribeIDs",
|
|
|
|
Index: i,
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
params.tribeIDs = tribeIDs
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-01-02 09:54:11 +00:00
|
|
|
func (params *ListVillagesParams) Sort() []VillageSort {
|
|
|
|
return params.sort
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2024-03-25 07:29:37 +00:00
|
|
|
villageSortMinLength = 0
|
2024-01-02 09:54:11 +00:00
|
|
|
villageSortMaxLength = 2
|
|
|
|
)
|
|
|
|
|
|
|
|
func (params *ListVillagesParams) SetSort(sort []VillageSort) error {
|
2024-03-02 08:49:42 +00:00
|
|
|
if err := validateSort(sort, villageSortMinLength, villageSortMaxLength); err != nil {
|
2024-01-02 09:54:11 +00:00
|
|
|
return ValidationError{
|
|
|
|
Model: listVillagesParamsModelName,
|
|
|
|
Field: "sort",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
params.sort = sort
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-03-05 06:14:41 +00:00
|
|
|
func (params *ListVillagesParams) Cursor() VillageCursor {
|
|
|
|
return params.cursor
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params *ListVillagesParams) SetCursor(cursor VillageCursor) error {
|
|
|
|
params.cursor = cursor
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params *ListVillagesParams) SetEncodedCursor(encoded string) error {
|
|
|
|
decoded, err := decodeVillageCursor(encoded)
|
|
|
|
if err != nil {
|
|
|
|
return ValidationError{
|
|
|
|
Model: listVillagesParamsModelName,
|
|
|
|
Field: "cursor",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
params.cursor = decoded
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-01-02 09:54:11 +00:00
|
|
|
func (params *ListVillagesParams) Limit() int {
|
|
|
|
return params.limit
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params *ListVillagesParams) SetLimit(limit int) error {
|
|
|
|
if err := validateIntInRange(limit, 1, VillageListMaxLimit); err != nil {
|
|
|
|
return ValidationError{
|
|
|
|
Model: listVillagesParamsModelName,
|
|
|
|
Field: "limit",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
params.limit = limit
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-03-05 06:14:41 +00:00
|
|
|
type ListVillagesResult struct {
|
|
|
|
villages Villages
|
|
|
|
self VillageCursor
|
|
|
|
next VillageCursor
|
2024-01-02 09:54:11 +00:00
|
|
|
}
|
|
|
|
|
2024-03-05 06:14:41 +00:00
|
|
|
const listVillagesResultModelName = "ListVillagesResult"
|
|
|
|
|
|
|
|
func NewListVillagesResult(villages Villages, next Village) (ListVillagesResult, error) {
|
|
|
|
var err error
|
|
|
|
res := ListVillagesResult{
|
|
|
|
villages: villages,
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(villages) > 0 {
|
2024-03-06 06:35:26 +00:00
|
|
|
res.self, err = villages[0].ToCursor()
|
2024-03-05 06:14:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return ListVillagesResult{}, ValidationError{
|
|
|
|
Model: listVillagesResultModelName,
|
|
|
|
Field: "self",
|
|
|
|
Err: err,
|
|
|
|
}
|
2024-01-02 09:54:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-05 06:14:41 +00:00
|
|
|
if !next.IsZero() {
|
2024-03-06 06:35:26 +00:00
|
|
|
res.next, err = next.ToCursor()
|
2024-03-05 06:14:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return ListVillagesResult{}, ValidationError{
|
|
|
|
Model: listVillagesResultModelName,
|
|
|
|
Field: "next",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-01-02 09:54:11 +00:00
|
|
|
|
2024-03-05 06:14:41 +00:00
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (res ListVillagesResult) Villages() Villages {
|
|
|
|
return res.villages
|
|
|
|
}
|
|
|
|
|
|
|
|
func (res ListVillagesResult) Self() VillageCursor {
|
|
|
|
return res.self
|
|
|
|
}
|
|
|
|
|
|
|
|
func (res ListVillagesResult) Next() VillageCursor {
|
|
|
|
return res.next
|
2024-01-02 09:54:11 +00:00
|
|
|
}
|
2024-03-06 06:35:26 +00:00
|
|
|
|
|
|
|
type ListVillagesWithRelationsResult struct {
|
|
|
|
villages VillagesWithRelations
|
|
|
|
self VillageCursor
|
|
|
|
next VillageCursor
|
|
|
|
}
|
|
|
|
|
|
|
|
const listVillagesWithRelationsResultModelName = "ListVillagesWithRelationsResult"
|
|
|
|
|
|
|
|
func NewListVillagesWithRelationsResult(
|
|
|
|
villages VillagesWithRelations,
|
|
|
|
next VillageWithRelations,
|
|
|
|
) (ListVillagesWithRelationsResult, error) {
|
|
|
|
var err error
|
|
|
|
res := ListVillagesWithRelationsResult{
|
|
|
|
villages: villages,
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(villages) > 0 {
|
|
|
|
res.self, err = villages[0].Village().ToCursor()
|
|
|
|
if err != nil {
|
|
|
|
return ListVillagesWithRelationsResult{}, ValidationError{
|
|
|
|
Model: listVillagesWithRelationsResultModelName,
|
|
|
|
Field: "self",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !next.IsZero() {
|
|
|
|
res.next, err = next.Village().ToCursor()
|
|
|
|
if err != nil {
|
|
|
|
return ListVillagesWithRelationsResult{}, ValidationError{
|
|
|
|
Model: listVillagesWithRelationsResultModelName,
|
|
|
|
Field: "next",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (res ListVillagesWithRelationsResult) Villages() VillagesWithRelations {
|
|
|
|
return res.villages
|
|
|
|
}
|
|
|
|
|
|
|
|
func (res ListVillagesWithRelationsResult) Self() VillageCursor {
|
|
|
|
return res.self
|
|
|
|
}
|
|
|
|
|
|
|
|
func (res ListVillagesWithRelationsResult) Next() VillageCursor {
|
|
|
|
return res.next
|
|
|
|
}
|
2024-03-07 05:52:03 +00:00
|
|
|
|
|
|
|
type VillageNotFoundError struct {
|
|
|
|
ID int
|
|
|
|
ServerKey string
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ErrorWithParams = VillageNotFoundError{}
|
|
|
|
|
|
|
|
func (e VillageNotFoundError) Error() string {
|
|
|
|
return fmt.Sprintf("village with id %d and server key %s not found", e.ID, e.ServerKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e VillageNotFoundError) Type() ErrorType {
|
|
|
|
return ErrorTypeNotFound
|
|
|
|
}
|
|
|
|
|
2024-03-18 07:01:47 +00:00
|
|
|
const errorCodeVillageNotFound = "village-not-found"
|
|
|
|
|
2024-03-07 05:52:03 +00:00
|
|
|
func (e VillageNotFoundError) Code() string {
|
2024-03-18 07:01:47 +00:00
|
|
|
return errorCodeVillageNotFound
|
2024-03-07 05:52:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e VillageNotFoundError) Params() map[string]any {
|
|
|
|
return map[string]any{
|
|
|
|
"ID": e.ID,
|
|
|
|
"ServerKey": e.ServerKey,
|
|
|
|
}
|
|
|
|
}
|
2024-03-10 09:37:44 +00:00
|
|
|
|
|
|
|
func formatVillageFullName(v interface {
|
|
|
|
Name() string
|
|
|
|
X() int
|
|
|
|
Y() int
|
|
|
|
Continent() string
|
|
|
|
}) string {
|
|
|
|
return fmt.Sprintf("%s (%d|%d) %s", v.Name(), v.X(), v.Y(), v.Continent())
|
|
|
|
}
|