235 lines
5.6 KiB
Go
235 lines
5.6 KiB
Go
package coordinator
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"sync"
|
|
"time"
|
|
|
|
"git.saintnet.tech/stryan/snengame/internal/game"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type Coordinator struct {
|
|
Matches map[uuid.UUID]*Session
|
|
MatchLock *sync.Mutex
|
|
PlayerQueueChan chan uuid.UUID
|
|
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),
|
|
CallbackChan: make(map[uuid.UUID]chan uuid.UUID),
|
|
}
|
|
}
|
|
|
|
func (c *Coordinator) Start() {
|
|
go func() {
|
|
for {
|
|
m := NewSession()
|
|
var p1, p2 uuid.UUID
|
|
p1 = <-c.PlayerQueueChan
|
|
fmt.Println("p1 join")
|
|
p2 = <-c.PlayerQueueChan
|
|
fmt.Println("p2 join")
|
|
m.Active = true
|
|
m.Game = game.NewGame()
|
|
c.MatchLock.Lock()
|
|
c.Matches[m.ID] = m
|
|
c.MatchLock.Unlock()
|
|
c.CallbackChan[p1] <- m.ID
|
|
c.CallbackChan[p2] <- m.ID
|
|
go MatchWatcher(m)
|
|
}
|
|
}()
|
|
go func() {
|
|
for {
|
|
time.Sleep(10 * time.Second)
|
|
c.MatchLock.Lock()
|
|
for _, v := range c.Matches {
|
|
if v.Game == nil && v.Active {
|
|
log.Println("clearing match with no game")
|
|
delete(c.Matches, v.ID)
|
|
v.Watcher <- true
|
|
}
|
|
}
|
|
c.MatchLock.Unlock()
|
|
}
|
|
}()
|
|
|
|
}
|
|
|
|
func (c *Coordinator) Coordinate(cmd *SessionCommand) *SessionCommandResult {
|
|
switch cmd.Command {
|
|
case SessionCmdQuery:
|
|
c.CallbackChan[cmd.ID] = make(chan uuid.UUID)
|
|
c.PlayerQueueChan <- cmd.ID
|
|
m := <-c.CallbackChan[cmd.ID]
|
|
return &SessionCommandResult{
|
|
ID: cmd.ID,
|
|
MatchID: m,
|
|
Result: SessionRespFound,
|
|
}
|
|
case SessionCmdJoin:
|
|
m, exists := c.Matches[cmd.MatchID]
|
|
if !exists {
|
|
return &SessionCommandResult{
|
|
ID: cmd.ID,
|
|
MatchID: uuid.Nil,
|
|
Result: SessionRespJoinError,
|
|
}
|
|
}
|
|
resp := m.Join(cmd.ID)
|
|
return &SessionCommandResult{
|
|
ID: cmd.ID,
|
|
MatchID: m.ID,
|
|
Result: resp,
|
|
}
|
|
case SessionCmdLeave:
|
|
m, exists := c.Matches[cmd.MatchID]
|
|
if !exists || !m.PlayerIn(cmd.ID) {
|
|
return &SessionCommandResult{
|
|
ID: cmd.ID,
|
|
MatchID: uuid.Nil,
|
|
Result: SessionRespLeft,
|
|
}
|
|
}
|
|
m.Leave(cmd.ID)
|
|
return &SessionCommandResult{
|
|
ID: cmd.ID,
|
|
MatchID: uuid.Nil,
|
|
Result: SessionRespLeft,
|
|
}
|
|
case SessionCmdPlay:
|
|
m, exists := c.Matches[cmd.MatchID]
|
|
if !exists || !m.PlayerIn(cmd.ID) {
|
|
return &SessionCommandResult{
|
|
ID: cmd.ID,
|
|
MatchID: uuid.Nil,
|
|
Result: SessionRespLeft,
|
|
}
|
|
}
|
|
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 {
|
|
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,
|
|
}
|
|
}
|
|
|
|
func MatchWatcher(m *Session) {
|
|
ticker := time.NewTicker(1 * time.Second)
|
|
old_turn := -1
|
|
var old_board *game.Board
|
|
old_sen_hand := -1
|
|
old_sco_hand := -1
|
|
old_sen_life := -1
|
|
old_sco_life := -1
|
|
sen_ready := false
|
|
sco_ready := false
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
if m.Active && m.Game != nil {
|
|
if m.Game.Status == game.StatusLobby && !sen_ready && m.Game.SentinalPlayer.Ready {
|
|
for _, v := range m.Broadcasts {
|
|
v <- SessionRespBroadcastSenReady
|
|
}
|
|
sen_ready = true
|
|
}
|
|
if m.Game.Status == game.StatusLobby && !sco_ready && m.Game.ScourgePlayer.Ready {
|
|
for _, v := range m.Broadcasts {
|
|
v <- SessionRespBroadcastScoReady
|
|
}
|
|
sco_ready = true
|
|
}
|
|
if m.Game.Status == game.StatusSentinalWin {
|
|
for _, v := range m.Broadcasts {
|
|
v <- SessionRespBroadcastSenWin
|
|
}
|
|
}
|
|
if m.Game.Status == game.StatusScourgeWin {
|
|
for _, v := range m.Broadcasts {
|
|
v <- SessionRespBroadcastScoWin
|
|
}
|
|
}
|
|
if m.p1 == uuid.Nil && sen_ready {
|
|
for _, v := range m.Broadcasts {
|
|
v <- SessionRespBroadcastSenLeft
|
|
}
|
|
sen_ready = false
|
|
}
|
|
if m.p2 == uuid.Nil && sco_ready {
|
|
for _, v := range m.Broadcasts {
|
|
v <- SessionRespBroadcastScoLeft
|
|
}
|
|
sco_ready = false
|
|
}
|
|
if old_board == nil || old_board.Sentinal != m.Game.GameBoard.Sentinal || old_board.Scourge != m.Game.GameBoard.Scourge || old_sen_hand != len(m.Game.SentinalPlayer.Hand) || old_sco_hand != len(m.Game.ScourgePlayer.Hand) || old_sen_life != m.Game.SentinalPlayer.Life || old_sco_life != m.Game.ScourgePlayer.Life {
|
|
if old_board == nil {
|
|
old_board = m.Game.GameBoard
|
|
} else {
|
|
old_board.Sentinal = m.Game.GameBoard.Sentinal
|
|
old_board.Scourge = m.Game.GameBoard.Scourge
|
|
}
|
|
old_sen_hand = len(m.Game.SentinalPlayer.Hand)
|
|
old_sco_hand = len(m.Game.ScourgePlayer.Hand)
|
|
old_sen_life = m.Game.SentinalPlayer.Life
|
|
old_sco_life = m.Game.ScourgePlayer.Life
|
|
for _, v := range m.Broadcasts {
|
|
v <- SessionRespBroadcastUpdate
|
|
}
|
|
}
|
|
if old_turn != m.Game.CurrentTurn {
|
|
old_turn = m.Game.CurrentTurn
|
|
if old_turn == game.SentinalID {
|
|
for k, v := range m.pMap {
|
|
if v == game.SentinalID {
|
|
m.Broadcasts[k] <- SessionRespBroadcastSenTurn
|
|
}
|
|
}
|
|
} else if old_turn == game.ScourgeID {
|
|
for k, v := range m.pMap {
|
|
if v == game.ScourgeID {
|
|
m.Broadcasts[k] <- SessionRespBroadcastScoTrun
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
case <-m.Watcher:
|
|
close(m.Watcher)
|
|
return
|
|
}
|
|
}
|
|
}
|