This repository has been archived on 2022-09-04. You can view files and clone it, but cannot push or open issues or pull requests.
api/servermap/usecase/map_usecase.go

233 lines
5.3 KiB
Go

package usecase
import (
"context"
"github.com/tribalwarshelp/shared/tw/twmodel"
"sort"
"strconv"
"strings"
"sync"
"github.com/pkg/errors"
"github.com/tribalwarshelp/map-generator/generator"
"github.com/tribalwarshelp/api/servermap"
"github.com/tribalwarshelp/api/village"
"golang.org/x/sync/errgroup"
)
const (
defaultBarbarianVillageColor = "#808080"
defaultPlayerVillageColor = "#FF0000"
)
type usecase struct {
villageRepo village.Repository
}
func New(villageRepo village.Repository) servermap.Usecase {
return &usecase{villageRepo}
}
func (ucase *usecase) GetMarkers(ctx context.Context, cfg servermap.GetMarkersConfig) ([]*generator.Marker, error) {
g := new(errgroup.Group)
tribes, tribeIDs, err := toMarkers(cfg.Tribes)
if err != nil {
return nil, err
}
players, playerIDs, err := toMarkers(cfg.Players)
if err != nil {
return nil, err
}
var otherMarkers []*generator.Marker
var otherMarkersMutex sync.Mutex
if cfg.ShowOtherPlayerVillages {
color := cfg.PlayerVillageColor
if color == "" {
color = defaultPlayerVillageColor
}
g.Go(func() error {
villages, _, err := ucase.villageRepo.Fetch(ctx, village.FetchConfig{
Server: cfg.Server,
Filter: &twmodel.VillageFilter{
PlayerFilter: &twmodel.PlayerFilter{
IDNEQ: append(playerIDs, 0),
TribeIDNEQ: tribeIDs,
},
},
Select: true,
Columns: []string{"x", "y"},
Count: false,
})
if err != nil {
return err
}
otherMarkersMutex.Lock()
otherMarkers = append(otherMarkers, &generator.Marker{
Villages: villages,
Color: color,
})
otherMarkersMutex.Unlock()
return nil
})
}
if cfg.ShowBarbarianVillages {
color := cfg.BarbarianVillageColor
if color == "" {
color = defaultBarbarianVillageColor
}
g.Go(func() error {
villages, _, err := ucase.villageRepo.Fetch(ctx, village.FetchConfig{
Server: cfg.Server,
Filter: &twmodel.VillageFilter{
PlayerID: []int{0},
},
Select: true,
Columns: []string{"x", "y"},
Count: false,
})
if err != nil {
return err
}
otherMarkersMutex.Lock()
otherMarkers = append(otherMarkers, &generator.Marker{
Villages: villages,
Color: color,
})
otherMarkersMutex.Unlock()
return nil
})
}
var tribeMarkers []*generator.Marker
var tribeMarkersMutex sync.Mutex
for color, tribeIDs := range tribes {
c := color
ids := tribeIDs
g.Go(func() error {
villages, _, err := ucase.villageRepo.Fetch(ctx, village.FetchConfig{
Server: cfg.Server,
Filter: &twmodel.VillageFilter{
PlayerFilter: &twmodel.PlayerFilter{
IDNEQ: playerIDs,
TribeID: ids,
},
},
Select: true,
Columns: []string{"x", "y"},
Count: false,
})
if err != nil {
return err
}
tribeMarkersMutex.Lock()
tribeMarkers = append(tribeMarkers, &generator.Marker{
Villages: villages,
Color: c,
Larger: cfg.LargerMarkers,
})
tribeMarkersMutex.Unlock()
return nil
})
}
var playerMarkers []*generator.Marker
var playerMarkersMutex sync.Mutex
for color, playerIDs := range players {
c := color
ids := playerIDs
g.Go(func() error {
villages, _, err := ucase.villageRepo.Fetch(ctx, village.FetchConfig{
Server: cfg.Server,
Filter: &twmodel.VillageFilter{
PlayerID: ids,
},
Select: true,
Columns: []string{"x", "y"},
Count: false,
})
if err != nil {
return err
}
playerMarkersMutex.Lock()
playerMarkers = append(playerMarkers, &generator.Marker{
Villages: villages,
Color: c,
Larger: cfg.LargerMarkers,
})
playerMarkersMutex.Unlock()
return nil
})
}
err = g.Wait()
if err != nil {
return nil, err
}
sort.SliceStable(playerMarkers, func(i, j int) bool {
return len(playerMarkers[i].Villages) < len(playerMarkers[j].Villages)
})
sort.SliceStable(tribeMarkers, func(i, j int) bool {
return len(tribeMarkers[i].Villages) < len(tribeMarkers[j].Villages)
})
return concatMarkers(otherMarkers, tribeMarkers, playerMarkers), nil
}
func concatMarkers(slices ...[]*generator.Marker) []*generator.Marker {
var totalLen int
for _, s := range slices {
totalLen += len(s)
}
tmp := make([]*generator.Marker, totalLen)
var i int
for _, s := range slices {
i += copy(tmp[i:], s)
}
return tmp
}
func toMarker(param string) (int, string, error) {
parts := strings.Split(param, ",")
if len(parts) != 2 {
return 0, "", errors.Errorf("%s: Invalid marker format (should be id,#hexcolor)", param)
}
id, err := strconv.Atoi(parts[0])
if err != nil {
return 0, "", errors.Wrapf(err, "%s: Invalid marker format (should be id,#hexcolor)", param)
}
if id <= 0 {
return 0, "", errors.New("ID should be greater than 0")
}
return id, parts[1], nil
}
func toMarkers(params []string) (map[string][]int, []int, error) {
idsByColor := make(map[string][]int)
var ids []int
cache := make(map[int]bool)
count := 0
for _, param := range params {
if count >= servermap.MaxMarkers {
break
}
//id,#color
id, color, err := toMarker(param)
if err != nil {
return nil, nil, errors.Wrapf(err, "invalid param %s", param)
}
if ok := cache[id]; ok || color == "" {
continue
}
ids = append(ids, id)
cache[id] = true
idsByColor[color] = append(idsByColor[color], id)
count++
}
return idsByColor, ids, nil
}