194 lines
4.9 KiB
Go
194 lines
4.9 KiB
Go
|
package coordinator
|
||
|
|
||
|
import (
|
||
|
"log"
|
||
|
"time"
|
||
|
|
||
|
"git.saintnet.tech/tomecraft/tome_lib"
|
||
|
"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 *tome_lib.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 == tome_lib.StatusLobby && !sen_ready && m.Game.SentinalPlayer.Ready {
|
||
|
for _, v := range m.Broadcasts {
|
||
|
addBroadcast(v, tome_lib.SessionRespBroadcastSenReady)
|
||
|
}
|
||
|
sen_ready = true
|
||
|
}
|
||
|
if m.Game.Status == tome_lib.StatusLobby && !sco_ready && m.Game.ScourgePlayer.Ready {
|
||
|
for _, v := range m.Broadcasts {
|
||
|
addBroadcast(v, tome_lib.SessionRespBroadcastScoReady)
|
||
|
}
|
||
|
sco_ready = true
|
||
|
}
|
||
|
if m.Game.Status == tome_lib.StatusSentinalWin {
|
||
|
for _, v := range m.Broadcasts {
|
||
|
addBroadcast(v, tome_lib.SessionRespBroadcastSenWin)
|
||
|
}
|
||
|
}
|
||
|
if m.Game.Status == tome_lib.StatusScourgeWin {
|
||
|
for _, v := range m.Broadcasts {
|
||
|
addBroadcast(v, tome_lib.SessionRespBroadcastScoWin)
|
||
|
}
|
||
|
}
|
||
|
if m.p1 == uuid.Nil && sen_ready {
|
||
|
for _, v := range m.Broadcasts {
|
||
|
addBroadcast(v, tome_lib.SessionRespBroadcastSenLeft)
|
||
|
}
|
||
|
sen_ready = false
|
||
|
}
|
||
|
if m.p2 == uuid.Nil && sco_ready {
|
||
|
for _, v := range m.Broadcasts {
|
||
|
addBroadcast(v, tome_lib.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, tome_lib.SessionRespBroadcastUpdate)
|
||
|
}
|
||
|
}
|
||
|
if old_turn != m.Game.CurrentTurn {
|
||
|
old_turn = m.Game.CurrentTurn
|
||
|
if old_turn == tome_lib.SentinalID {
|
||
|
for k, v := range m.pMap {
|
||
|
if v == tome_lib.SentinalID {
|
||
|
addBroadcast(m.Broadcasts[k], tome_lib.SessionRespBroadcastSenTurn)
|
||
|
}
|
||
|
}
|
||
|
} else if old_turn == tome_lib.ScourgeID {
|
||
|
for k, v := range m.pMap {
|
||
|
if v == tome_lib.ScourgeID {
|
||
|
addBroadcast(m.Broadcasts[k], tome_lib.SessionRespBroadcastScoTrun)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
case <-m.Watcher:
|
||
|
close(m.Watcher)
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func addBroadcast(pipe chan tome_lib.SessionResp, brd tome_lib.SessionResp) {
|
||
|
select {
|
||
|
case pipe <- brd:
|
||
|
default:
|
||
|
log.Println("broadcasts buffer full, discarding")
|
||
|
}
|
||
|
}
|