snengame/internal/coordinator/match.go

194 lines
4.8 KiB
Go

package coordinator
import (
"log"
"time"
"git.saintnet.tech/stryan/snengame/internal/game"
"github.com/google/uuid"
)
type MMRPlayer struct {
Id uuid.UUID
MMR int
QueueTime time.Time
}
func MatchMaker(c *Coordinator) {
//in the future the int will be MMR or something, but for now it's just a set
for {
select {
case p := <-c.PlayerQueueChan:
//add player to pool
c.PlayerPool[MMRPlayer{
Id: p,
MMR: 0,
QueueTime: time.Now()}] = true
log.Printf("Player %v has queued", p)
if len(c.PlayerPool) < 2 {
continue
} else {
//fancy matchmaking math to guarantee a fair game
var p1, p2 MMRPlayer
for key, _ := range c.PlayerPool {
p1 = key
break
}
delete(c.PlayerPool, p1)
for key, _ := range c.PlayerPool {
p2 = key
break
}
delete(c.PlayerPool, p2)
m := NewSession()
log.Printf("Creating match %v for %v and %v", m.ID, p1, p2)
m.Active = false
c.MatchLock.Lock()
c.Matches[m.ID] = m
c.MatchLock.Unlock()
c.CallbackChan[p1.Id] <- m.ID
c.CallbackChan[p2.Id] <- m.ID
go MatchWatcher(m)
}
}
}
}
func QueueCleaner(c *Coordinator) {
ticker := time.NewTicker(5 * time.Minute)
for {
select {
case <-ticker.C:
for v, _ := range c.PlayerPool {
if time.Now().After(v.QueueTime.Add(time.Minute * 5)) {
log.Printf("Removing player %v from pool", v.Id)
delete(c.PlayerPool, v) //probably should just flag
}
}
}
}
}
func MatchCleaner(c *Coordinator) {
ticker := time.NewTicker(10 * time.Second)
for {
select {
case <-ticker.C:
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
}
if time.Now().After(v.LastMove.Add(time.Minute * 10)) {
log.Println("clearing stale match")
v.Game = nil
v.Active = false
delete(c.Matches, v.ID)
v.Watcher <- true
}
}
c.MatchLock.Unlock()
}
}
}
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 && len(m.Broadcasts) > 0 {
if m.Game.Status == game.StatusLobby && !sen_ready && m.Game.SentinalPlayer.Ready {
for _, v := range m.Broadcasts {
addBroadcast(v, SessionRespBroadcastSenReady)
}
sen_ready = true
}
if m.Game.Status == game.StatusLobby && !sco_ready && m.Game.ScourgePlayer.Ready {
for _, v := range m.Broadcasts {
addBroadcast(v, SessionRespBroadcastScoReady)
}
sco_ready = true
}
if m.Game.Status == game.StatusSentinalWin {
for _, v := range m.Broadcasts {
addBroadcast(v, SessionRespBroadcastSenWin)
}
}
if m.Game.Status == game.StatusScourgeWin {
for _, v := range m.Broadcasts {
addBroadcast(v, SessionRespBroadcastScoWin)
}
}
if m.p1 == uuid.Nil && sen_ready {
for _, v := range m.Broadcasts {
addBroadcast(v, SessionRespBroadcastSenLeft)
}
sen_ready = false
}
if m.p2 == uuid.Nil && sco_ready {
for _, v := range m.Broadcasts {
addBroadcast(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 {
addBroadcast(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 {
addBroadcast(m.Broadcasts[k], SessionRespBroadcastSenTurn)
}
}
} else if old_turn == game.ScourgeID {
for k, v := range m.pMap {
if v == game.ScourgeID {
addBroadcast(m.Broadcasts[k], SessionRespBroadcastScoTrun)
}
}
}
}
}
case <-m.Watcher:
close(m.Watcher)
return
}
}
}
func addBroadcast(pipe chan SessionResp, brd SessionResp) {
select {
case pipe <- brd:
default:
log.Println("broadcasts buffer full, discarding")
}
}