2024-01-01 07:49:47 +00:00
|
|
|
package app
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
|
|
|
|
)
|
|
|
|
|
2024-01-01 10:20:30 +00:00
|
|
|
type PlayerRepository interface {
|
|
|
|
CreateOrUpdate(ctx context.Context, params ...domain.CreatePlayerParams) error
|
2024-02-26 06:44:39 +00:00
|
|
|
List(ctx context.Context, params domain.ListPlayersParams) (domain.ListPlayersResult, error)
|
2024-02-28 06:59:10 +00:00
|
|
|
ListWithRelations(ctx context.Context, params domain.ListPlayersParams) (domain.ListPlayersWithRelationsResult, error)
|
2024-01-01 10:20:30 +00:00
|
|
|
// Delete marks players with the given serverKey and ids as deleted (sets deleted at to now).
|
|
|
|
// In addition, Delete sets TribeID to null.
|
|
|
|
//
|
|
|
|
// https://en.wiktionary.org/wiki/soft_deletion
|
|
|
|
Delete(ctx context.Context, serverKey string, ids ...int) error
|
|
|
|
}
|
|
|
|
|
2024-01-01 07:49:47 +00:00
|
|
|
type PlayerService struct {
|
2024-01-06 09:26:56 +00:00
|
|
|
repo PlayerRepository
|
|
|
|
tribeChangeSvc *TribeChangeService
|
|
|
|
twSvc TWService
|
|
|
|
pub PlayerPublisher
|
2024-01-01 07:49:47 +00:00
|
|
|
}
|
|
|
|
|
2024-01-06 09:26:56 +00:00
|
|
|
func NewPlayerService(
|
|
|
|
repo PlayerRepository,
|
|
|
|
tribeChangeSvc *TribeChangeService,
|
|
|
|
twSvc TWService,
|
|
|
|
pub PlayerPublisher,
|
|
|
|
) *PlayerService {
|
|
|
|
return &PlayerService{repo: repo, tribeChangeSvc: tribeChangeSvc, twSvc: twSvc, pub: pub}
|
2024-01-01 07:49:47 +00:00
|
|
|
}
|
|
|
|
|
2024-01-01 10:20:30 +00:00
|
|
|
func (svc *PlayerService) Sync(ctx context.Context, serverSyncedPayload domain.ServerSyncedEventPayload) error {
|
|
|
|
serverKey := serverSyncedPayload.Key()
|
|
|
|
serverURL := serverSyncedPayload.URL()
|
|
|
|
|
|
|
|
players, err := svc.twSvc.GetPlayers(ctx, serverURL)
|
2024-01-01 07:49:47 +00:00
|
|
|
if err != nil {
|
2024-01-01 10:20:30 +00:00
|
|
|
return fmt.Errorf("%s: couldn't get players: %w", serverKey, err)
|
2024-01-01 07:49:47 +00:00
|
|
|
}
|
|
|
|
|
2024-01-01 10:20:30 +00:00
|
|
|
if err = svc.createOrUpdate(ctx, serverKey, players); err != nil {
|
|
|
|
return fmt.Errorf("%s: couldn't create/update players: %w", serverKey, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = svc.delete(ctx, serverKey, players); err != nil {
|
|
|
|
return fmt.Errorf("%s: couldn't delete players: %w", serverKey, err)
|
|
|
|
}
|
2024-01-01 07:49:47 +00:00
|
|
|
|
2024-01-15 07:00:08 +00:00
|
|
|
payload, err := domain.NewPlayersSyncedEventPayload(
|
2024-01-01 10:53:59 +00:00
|
|
|
serverKey,
|
|
|
|
serverURL,
|
|
|
|
serverSyncedPayload.VersionCode(),
|
|
|
|
len(players),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("%s: couldn't construct domain.PlayersSyncedEventPayload: %w", serverKey, err)
|
|
|
|
}
|
|
|
|
|
2024-01-15 07:00:08 +00:00
|
|
|
if err = svc.pub.EventSynced(ctx, payload); err != nil {
|
2024-01-01 10:53:59 +00:00
|
|
|
return fmt.Errorf("%s: %w", serverKey, err)
|
|
|
|
}
|
|
|
|
|
2024-01-01 07:49:47 +00:00
|
|
|
return nil
|
|
|
|
}
|
2024-01-01 10:20:30 +00:00
|
|
|
|
|
|
|
const playerCreateOrUpdateChunkSize = domain.PlayerListMaxLimit
|
|
|
|
|
|
|
|
func (svc *PlayerService) createOrUpdate(ctx context.Context, serverKey string, players domain.BasePlayers) error {
|
|
|
|
for i := 0; i < len(players); i += playerCreateOrUpdateChunkSize {
|
|
|
|
end := i + playerCreateOrUpdateChunkSize
|
|
|
|
if end > len(players) {
|
|
|
|
end = len(players)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := svc.createOrUpdateChunk(ctx, serverKey, players[i:end]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (svc *PlayerService) createOrUpdateChunk(ctx context.Context, serverKey string, players domain.BasePlayers) error {
|
|
|
|
ids := make([]int, 0, len(players))
|
|
|
|
for _, p := range players {
|
|
|
|
ids = append(ids, p.ID())
|
|
|
|
}
|
|
|
|
|
|
|
|
listParams := domain.NewListPlayersParams()
|
|
|
|
if err := listParams.SetServerKeys([]string{serverKey}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := listParams.SetIDs(ids); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := listParams.SetSort([]domain.PlayerSort{domain.PlayerSortIDASC}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := listParams.SetLimit(domain.PlayerListMaxLimit); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-02-26 06:44:39 +00:00
|
|
|
res, err := svc.repo.List(ctx, listParams)
|
2024-01-01 10:20:30 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2024-02-26 06:44:39 +00:00
|
|
|
storedPlayers := res.Players()
|
2024-01-01 10:20:30 +00:00
|
|
|
|
|
|
|
createParams, err := domain.NewCreatePlayerParams(serverKey, players, storedPlayers)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-01-06 09:26:56 +00:00
|
|
|
tribeChangesParams, err := domain.NewCreateTribeChangeParamsFromPlayers(serverKey, players, storedPlayers)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = svc.repo.CreateOrUpdate(ctx, createParams...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return svc.tribeChangeSvc.Create(ctx, tribeChangesParams...)
|
2024-01-01 10:20:30 +00:00
|
|
|
}
|
|
|
|
|
2024-01-06 09:26:56 +00:00
|
|
|
//nolint:gocyclo
|
2024-01-01 10:20:30 +00:00
|
|
|
func (svc *PlayerService) delete(ctx context.Context, serverKey string, players domain.BasePlayers) error {
|
|
|
|
listParams := domain.NewListPlayersParams()
|
|
|
|
if err := listParams.SetServerKeys([]string{serverKey}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := listParams.SetDeleted(domain.NullBool{
|
2024-02-28 06:59:10 +00:00
|
|
|
V: false,
|
2024-01-01 10:20:30 +00:00
|
|
|
Valid: true,
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := listParams.SetSort([]domain.PlayerSort{domain.PlayerSortIDASC}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := listParams.SetLimit(domain.PlayerListMaxLimit); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var toDelete []int
|
2024-01-06 09:26:56 +00:00
|
|
|
var tribeChangesParams []domain.CreateTribeChangeParams
|
2024-01-01 10:20:30 +00:00
|
|
|
|
|
|
|
for {
|
2024-02-26 06:44:39 +00:00
|
|
|
res, err := svc.repo.List(ctx, listParams)
|
2024-01-01 10:20:30 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-02-26 06:44:39 +00:00
|
|
|
ids, params, err := res.Players().Delete(serverKey, players)
|
2024-01-06 09:26:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
toDelete = append(toDelete, ids...)
|
|
|
|
tribeChangesParams = append(tribeChangesParams, params...)
|
2024-01-01 10:20:30 +00:00
|
|
|
|
2024-02-26 06:44:39 +00:00
|
|
|
if res.Next().IsZero() {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = listParams.SetCursor(res.Next()); err != nil {
|
2024-01-01 10:20:30 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-06 09:26:56 +00:00
|
|
|
if err := svc.repo.Delete(ctx, serverKey, toDelete...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return svc.tribeChangeSvc.Create(ctx, tribeChangesParams...)
|
2024-01-01 10:20:30 +00:00
|
|
|
}
|
2024-01-16 06:28:03 +00:00
|
|
|
|
2024-02-26 06:44:39 +00:00
|
|
|
func (svc *PlayerService) List(ctx context.Context, params domain.ListPlayersParams) (domain.ListPlayersResult, error) {
|
2024-01-16 06:28:03 +00:00
|
|
|
return svc.repo.List(ctx, params)
|
|
|
|
}
|
2024-02-28 06:59:10 +00:00
|
|
|
|
|
|
|
func (svc *PlayerService) ListWithRelations(
|
|
|
|
ctx context.Context,
|
|
|
|
params domain.ListPlayersParams,
|
|
|
|
) (domain.ListPlayersWithRelationsResult, error) {
|
|
|
|
return svc.repo.ListWithRelations(ctx, params)
|
|
|
|
}
|
2024-02-29 06:25:01 +00:00
|
|
|
|
|
|
|
func (svc *PlayerService) GetWithRelations(
|
|
|
|
ctx context.Context,
|
|
|
|
id int,
|
|
|
|
serverKey string,
|
|
|
|
) (domain.PlayerWithRelations, error) {
|
|
|
|
params := domain.NewListPlayersParams()
|
|
|
|
if err := params.SetIDs([]int{id}); err != nil {
|
|
|
|
return domain.PlayerWithRelations{}, err
|
|
|
|
}
|
|
|
|
if err := params.SetServerKeys([]string{serverKey}); err != nil {
|
|
|
|
return domain.PlayerWithRelations{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
res, err := svc.repo.ListWithRelations(ctx, params)
|
|
|
|
if err != nil {
|
|
|
|
return domain.PlayerWithRelations{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
players := res.Players()
|
|
|
|
if len(players) == 0 {
|
|
|
|
return domain.PlayerWithRelations{}, domain.PlayerNotFoundError{
|
|
|
|
ID: id,
|
|
|
|
ServerKey: serverKey,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return players[0], nil
|
|
|
|
}
|