tome_server/coordinator.go

192 lines
4.3 KiB
Go

package coordinator
import (
"log"
"sync"
"time"
"git.saintnet.tech/tomecraft/tome_game"
. "git.saintnet.tech/tomecraft/tome_lib"
"github.com/google/uuid"
)
type Coordinator struct {
Matches map[uuid.UUID]*Session
MatchLock *sync.Mutex
PlayerQueueChan chan uuid.UUID
PlayerPool map[MMRPlayer]bool
CallbackChan map[uuid.UUID]chan uuid.UUID
}
func NewCoordinator() *Coordinator {
return &Coordinator{
Matches: make(map[uuid.UUID]*Session),
MatchLock: &sync.Mutex{},
PlayerQueueChan: make(chan uuid.UUID),
PlayerPool: make(map[MMRPlayer]bool),
CallbackChan: make(map[uuid.UUID]chan uuid.UUID),
}
}
func (c *Coordinator) Start() {
go MatchMaker(c)
go MatchCleaner(c)
go QueueCleaner(c)
}
func (c *Coordinator) Coordinate(cmd *SessionCommand) *SessionCommandResult {
switch cmd.Command {
case SessionCmdQuery:
c.CallbackChan[cmd.ID] = make(chan uuid.UUID)
c.PlayerQueueChan <- cmd.ID
ticker := time.NewTicker(5 * time.Minute)
select {
case m := <-c.CallbackChan[cmd.ID]:
return &SessionCommandResult{
ID: cmd.ID,
MatchID: m,
Result: SessionRespFound,
}
case <-ticker.C:
return &SessionCommandResult{
ID: cmd.ID,
MatchID: uuid.Nil,
Result: SessionRespError,
}
}
case SessionCmdJoin:
m, exists := c.Matches[cmd.MatchID]
if !exists {
log.Printf("player %v tried to join non-existent match %v", cmd.ID, cmd.MatchID)
return &SessionCommandResult{
ID: cmd.ID,
MatchID: uuid.Nil,
Result: SessionRespJoinError,
}
}
resp := m.Join(cmd.ID)
if m.p1 != uuid.Nil && m.p2 != uuid.Nil {
log.Printf("Starting game for %v and %v\n", m.p1, m.p2)
d1 := []int{}
d2 := []int{}
if m.p1Deck != nil {
d1 = m.p1Deck
}
if m.p2Deck != nil {
d2 = m.p2Deck
}
m.Game = tome_game.NewGame(d1, d2)
m.Active = true
}
return &SessionCommandResult{
ID: cmd.ID,
MatchID: m.ID,
Result: resp,
}
case SessionCmdReady:
m, exists := c.Matches[cmd.MatchID]
if !exists {
return &SessionCommandResult{
ID: cmd.ID,
MatchID: uuid.Nil,
Result: SessionRespError,
}
}
if m.p1 != uuid.Nil && m.p2 != uuid.Nil {
return &SessionCommandResult{
ID: cmd.ID,
MatchID: m.ID,
Result: SessionRespReady,
}
} else {
return &SessionCommandResult{
ID: cmd.ID,
MatchID: uuid.Nil,
Result: SessionRespError,
}
}
case SessionCmdLoadDeck:
m, exists := c.Matches[cmd.MatchID]
if !exists || !m.PlayerIn(cmd.ID) {
return &SessionCommandResult{
ID: cmd.ID,
MatchID: uuid.Nil,
Result: SessionRespError,
}
}
if m.Game != nil && m.Game.Status != StatusLobby {
return &SessionCommandResult{
ID: cmd.ID,
MatchID: m.ID,
Result: SessionRespLoadDeckError,
}
}
resp := m.LoadDeck(cmd.ID, cmd.Data)
return &SessionCommandResult{
ID: cmd.ID,
MatchID: m.ID,
Result: resp,
}
case SessionCmdLeave:
m, exists := c.Matches[cmd.MatchID]
if exists && m.PlayerIn(cmd.ID) {
m.Leave(cmd.ID)
} else {
for k, _ := range c.PlayerPool {
if k.Id == m.ID {
delete(c.PlayerPool, k)
}
}
}
return &SessionCommandResult{
ID: cmd.ID,
MatchID: uuid.Nil,
Result: SessionRespLeft,
}
case SessionCmdPlay:
m, exists := c.Matches[cmd.MatchID]
if !exists || !m.PlayerIn(cmd.ID) || m.Game == nil {
return &SessionCommandResult{
ID: cmd.ID,
MatchID: uuid.Nil,
Result: SessionRespError,
}
}
resp := m.Play(cmd.ID, cmd.GameCommand)
return &SessionCommandResult{
ID: cmd.ID,
MatchID: m.ID,
Result: SessionRespPlayed,
GameResult: resp,
}
case SessionCmdPoll:
m, exists := c.Matches[cmd.MatchID]
if exists {
_, exists = m.Broadcasts[cmd.ID]
if !exists {
log.Printf("%v has opted in to polling", cmd.ID)
m.Broadcasts[cmd.ID] = make(chan SessionResp, 10)
}
select {
case res := <-m.Broadcasts[cmd.ID]:
return &SessionCommandResult{
ID: cmd.ID,
MatchID: m.ID,
Result: res,
}
default:
return &SessionCommandResult{
ID: cmd.ID,
MatchID: m.ID,
Result: SessionRespBroadcastNone,
}
}
}
}
return &SessionCommandResult{
ID: cmd.ID,
MatchID: uuid.Nil,
Result: SessionRespError,
}
}