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") } }