def testEnoughTurnsPlayed(self): txq = asyncio.Queue() wordset = SupportedWordSets["test"] allConns = Connections(txq) team1 = mockPlyrTeam(txq, allConns, 1, {"sb": [101]}, {"sb": 2}) team2 = mockPlyrTeam(txq, allConns, 2, {"jg": [102]}, {"jg": 2}) teams = {1: team1, 2: team2} hostParameters = HostParameters(numTeams=2, turnDurationSec=30, wordSets=["test"], numTurns=2) for team in teams.values(): for ws in team.conns._wss: allConns.addConn(ws) turnMgr = TurnManager("taboo:1", txq, wordset, teams, hostParameters, allConns, None) self.assertFalse(turnMgr.startNewTurn()) self.assertGiTxQueueMsgs(txq, [ ClientTxMsg(['PLAYER-STATUS', 'jg', {'numConns': 1, 'ready': False, 'turnsPlayed': 0}], {101}), ClientTxMsg(['PLAYER-STATUS', 'sb', {'numConns': 1, 'ready': False, 'turnsPlayed': 0}], {101}), ClientTxMsg(['PLAYER-STATUS', 'jg', {'numConns': 1, 'ready': False, 'turnsPlayed': 0}], {102}), ClientTxMsg(['PLAYER-STATUS', 'sb', {'numConns': 1, 'ready': False, 'turnsPlayed': 0}], {102}), ClientTxMsg(["SCORE", {1: 0, 2: 0}], {101, 102}), ], anyOrder=True)
def testAddConnectionsBeforeMsgSrc(self): conns1 = Connections(self.txq) conns2 = Connections(self.txq) conns2.addConn(clientWs2) self.assertGiTxQueueMsgs(self.txq, []) self.conns.addConnections(conns1) self.assertGiTxQueueMsgs(self.txq, []) self.conns.addConnections(conns2) self.assertGiTxQueueMsgs(self.txq, []) # Add message sources # message source with no messages msgSrcEmpty = MsgSrc(self.conns) # pylint: disable=unused-variable self.assertGiTxQueueMsgs(self.txq, []) msgSrc = MsgSrc(self.conns) msgSrc.setMsgs([ Jmai([1], initiatorWs=clientWs3), Jmai([2], initiatorWs=clientWs3) ]) self.assertGiTxQueueMsgs(self.txq, [ ClientTxMsg([1], {clientWs2}, initiatorWs=clientWs3), ClientTxMsg([2], {clientWs2}, initiatorWs=clientWs3) ])
class TabooPlayer: def __init__(self, txQueue, allConns, name, team, turnsPlayed=0): """ Creates a taboo player associated to a team. Self-registers to the team txQueue: tx queue allConns: Connections with all clients in the room name: player name team: team that this player should belong to """ self.name = name self.team = team self.turnsPlayed = turnsPlayed self.__ready = False self.playerConns = Connections(txQueue) team.addPlayer(self) self.playerStatusMsgSrc = MsgSrc(allConns) def __str__(self): return "TabooPlayer({}) teamId={}, turnsPlayed={}".format( self.name, self.team.teamNumber, self.turnsPlayed) def addConn(self, ws): self.playerConns.addConn(ws) self.team.conns.addConn(ws) self._updateMsgSrc() def delConn(self, ws): self.playerConns.delConn(ws) self.team.conns.delConn(ws) self._updateMsgSrc() @property def ready(self): return self.__ready @ready.setter def ready(self, flag): self.__ready = flag self._updateMsgSrc() def incTurnsPlayed(self): self.turnsPlayed += 1 def numConns(self): return self.playerConns.count() def _updateMsgSrc(self): self.playerStatusMsgSrc.setMsgs([ Jmai([ "PLAYER-STATUS", self.name, { "numConns": self.numConns(), "ready": self.ready, "turnsPlayed": self.turnsPlayed, } ], None), ])
def setRxTxQueues(self, rxQueue, txQueue): """Setup RX and TX queue for a plugin""" assert not self.rxQueue assert not self.txQueue assert rxQueue assert txQueue self.rxQueue = rxQueue self.txQueue = txQueue self.conns = Connections(self.txQueue) self.postQueueSetup()
def __init__(self, txQueue, allConns, name, team, turnsPlayed=0): """ Creates a taboo player associated to a team. Self-registers to the team txQueue: tx queue allConns: Connections with all clients in the room name: player name team: team that this player should belong to """ self.name = name self.team = team self.turnsPlayed = turnsPlayed self.__ready = False self.playerConns = Connections(txQueue) team.addPlayer(self) self.playerStatusMsgSrc = MsgSrc(allConns)
def testBasic(self): txq = asyncio.Queue() wordset = SupportedWordSets["test"] with stub(wordset, "nextWord", self.mockNextWord): allConns = Connections(txq) team1 = mockPlyrTeam(txq, allConns, 1, {"sb1": [101], "sb2": [102]}, {}) team2 = mockPlyrTeam(txq, allConns, 2, {"jg1": [201], "jg2": [202]}, {}) teams = {1: team1, 2: team2} hostParameters = HostParameters(numTeams=2, turnDurationSec=30, wordSets=["test"], numTurns=2) for team in teams.values(): for ws in team.conns._wss: allConns.addConn(ws) self.drainGiTxQueue(txq) turnMgr = TurnManager("taboo:1", txq, wordset, teams, hostParameters, allConns, None) def mockFindNextPlayer(): return team2.members["jg1"] stub1 = stub(turnMgr, "_findNextPlayer", mockFindNextPlayer) stub1.__enter__() # pylint: disable=no-member self.assertGiTxQueueMsgs(txq, [ ClientTxMsg(["SCORE", {1: 0, 2: 0}], {101, 102, 201, 202}), ], anyOrder=True) self.assertTrue(turnMgr.startNewTurn()) self.assertGiTxQueueMsgs(txq, [ ClientTxMsg(["WAIT-FOR-KICKOFF", 1, "jg1"], {101, 102, 201, 202}, None), ]) self.assertTrue(turnMgr.startNextWord()) secretMsg = ['TURN', 1, 1, {'team': 2, 'player': 'jg1', 'state': 'IN_PLAY', 'utcTimeout': None, # We didn't send KICKOFF explicitly 'secret': 'c', 'disallowed': ['c1', 'c2']}] publicMsg = ['TURN', 1, 1, {'team': 2, 'player': 'jg1', 'state': 'IN_PLAY', 'utcTimeout': None}] self.assertGiTxQueueMsgs(txq, [ ClientTxMsg(publicMsg, {101, 102, 201, 202}), ClientTxMsg(secretMsg, {101, 102}), ClientTxMsg(secretMsg, {201}), ], anyOrder=True)
def __init__(self, txQueue, allConns, teamNumber): self.txQueue = txQueue self.teamNumber = teamNumber self.teamStatusMsgSrc = MsgSrc(allConns) self.members = {} self.conns = Connections(self.txQueue) self._updateMsgSrc()
def mockPlyrTeam(txq, allConns, teamId : int, connsByPlayerName, turnsPlayedByPlayerName=None): turnsPlayedByPlayerName = turnsPlayedByPlayerName or {} team = TabooTeam(txq, Connections(txq), teamId) for plyrName, conns in connsByPlayerName.items(): plyr = TabooPlayer(txq, allConns, name=plyrName, team=team) for ws in conns: plyr.addConn(ws) plyr.turnsPlayed = turnsPlayedByPlayerName.get(plyrName, 0) return team
def setUpWord(self, turnId=1, wordId=1, secret=None, disallowed=None, player=None, otherTeams=None, allConns=None, state=WordState.IN_PLAY, utcTimeout=30, score=None, playerName="sb", playerTeamNum=1, playerWss=None, otherTeamNum=2, otherTeamWss=None): # pylint: disable=too-many-locals secret = secret or "a" disallowed = disallowed or ["a1", "a2"] playerWss = playerWss or {101} # 1 player otherTeamWss = otherTeamWss or {102} # 1 other team member allConns = Connections(self.txq) for ws in playerWss | otherTeamWss | {103}: # 1 spectator added allConns.addConn(ws) playerTeam = TabooTeam(self.txq, allConns, playerTeamNum) player = TabooPlayer(self.txq, allConns, name=playerName, team=playerTeam) for ws in playerWss: player.addConn(ws) otherTeam = TabooTeam(self.txq, allConns, otherTeamNum) for ws in otherTeamWss: otherTeam.conns.addConn(ws) otherTeams = [otherTeam] score = score or [] return Word(turnId=turnId, wordId=wordId, secret=secret, disallowed=disallowed, player=player, otherTeams=otherTeams, allConns=allConns, utcTimeout=utcTimeout, state=state, score=score)
def testNoClients(self): txq = asyncio.Queue() wordset = SupportedWordSets["test"] allConns = Connections(txq) team1 = mockPlyrTeam(txq, allConns, 1, {"sb": []}) team2 = mockPlyrTeam(txq, allConns, 2, {"jg": []}) teams = {1: team1, 2: team2} hostParameters = HostParameters(numTeams=2, turnDurationSec=30, wordSets=["test"], numTurns=1) for team in teams.values(): for ws in team.conns._wss: allConns.addConn(ws) turnMgr = TurnManager("taboo:1", txq, wordset, teams, hostParameters, allConns, None) self.assertFalse(turnMgr.startNewTurn()) self.assertGiTxQueueMsgs(txq, [])
class Plugin: """ Base game instance that is registered with the main loop by the name "path". """ def __init__(self, path, name, storage=None): """ Arguments --------- path : str Path to reach this instance. For example, "lobby" "chat" -- chat room's lobby "chat:1" -- chat room id 1 "chat:2" -- chat room id 2 storage : Storage (optional) """ self.path = path self.name = name self.storage = storage self.rxQueue = None self.txQueue = None self.conns = None # --------------------------------- # Startup related def postQueueSetup(self): """Additional logic to run after RX and TX queues are setup for a plugin""" def setRxTxQueues(self, rxQueue, txQueue): """Setup RX and TX queue for a plugin""" assert not self.rxQueue assert not self.txQueue assert rxQueue assert txQueue self.rxQueue = rxQueue self.txQueue = txQueue self.conns = Connections(self.txQueue) self.postQueueSetup() # --------------------------------- # Message sending helpers def broadcast(self, jmsg, initiatorWs=None): """Send a message to all clients of a Plugin""" self.conns.send([Jmai(jmsg, initiatorWs)]) # --------------------------------- # Message handling def postProcessConnect(self, ws): """Additional logic to run after a ws connects""" def processConnect(self, ws): """Add client websocket as a member in this game instance""" self.conns.addConn(ws) self.postProcessConnect(ws) def postProcessDisconnect(self, ws): """Additional logic to run after a ws disconnects""" def processDisconnect(self, ws): """Remove client websocket as a member in this game instance""" self.conns.delConn(ws) self.postProcessDisconnect(ws) def processMsg(self, qmsg): """Act to on the received msg. Return value : bool True => msg was handled False => msg was handled """ if isinstance(qmsg, InternalConnectWsToGi): self.processConnect(qmsg.ws) return True if isinstance(qmsg, InternalDisconnectWsToGi): self.processDisconnect(qmsg.ws) return True return False async def worker(self): """The worker task for a plugin. All messages should be processed. Any unhandled message returns a bad message to the sender""" trace(Level.game, "{}.worker() ready".format(self.path)) while True: qmsg = await self.rxQueue.get() self.rxQueue.task_done() trace(Level.msg, self.path, "received", str(qmsg)) processed = self.processMsg(qmsg) if not processed: if not isinstance(qmsg, ClientRxMsg): trace(Level.error, "Unexpected message not handled:", str(qmsg)) continue self.txQueue.put_nowait( ClientTxMsg("Bad message", {qmsg.initiatorWs}, initiatorWs=qmsg.initiatorWs))
def setUp(self): self.txq = asyncio.Queue() self.conns = Connections(self.txq)
class MsgSrcConnectionsTest(unittest.TestCase, MsgTestLib): """Test MsgSrc and Connections MsgSrc1 --> +-------------+ | | --> Websocket1 MsgSrc2 --> | Connections | | | --> Websocket2 MsgSrc3 --> +-------------+ """ def setUp(self): self.txq = asyncio.Queue() self.conns = Connections(self.txq) def test1MsgSrc1Websocket(self): """ Try various triggers of adding/removing MsgSrcs and Websockets """ self.assertGiTxQueueMsgs(self.txq, []) # Adding connections doesn't create a message self.conns.addConn(clientWs1) self.assertGiTxQueueMsgs(self.txq, []) # Adding msgSrc with no messages msgSrc1 = MsgSrc(self.conns) self.assertGiTxQueueMsgs(self.txq, []) # Set messages in msgSrc msgSrc1.setMsgs([ Jmai([1], initiatorWs=clientWs3), Jmai([2], initiatorWs=clientWs3) ]) self.assertGiTxQueueMsgs(self.txq, [ ClientTxMsg([1], {clientWs1}, initiatorWs=clientWs3), ClientTxMsg([2], {clientWs1}, initiatorWs=clientWs3) ]) # Adding msgSrc with previous messages msgSrc2 = MsgSrc(self.conns) msgSrc2.setMsgs( [Jmai([True], initiatorWs=None), Jmai([False], initiatorWs=None)]) self.assertGiTxQueueMsgs(self.txq, [ ClientTxMsg([True], {clientWs1}), ClientTxMsg([False], {clientWs1}) ]) # Adding a second connection self.conns.addConn(clientWs2) self.assertGiTxQueueMsgs(self.txq, [ ClientTxMsg([1], {clientWs2}, initiatorWs=clientWs3), ClientTxMsg([2], {clientWs2}, initiatorWs=clientWs3), ClientTxMsg([True], {clientWs2}, initiatorWs=None), ClientTxMsg([False], {clientWs2}, initiatorWs=None) ], anyOrder=True) # Adding msgSrc with state preset msgSrc2.setMsgs([Jmai(["yes"], initiatorWs=None)]) self.assertGiTxQueueMsgs( self.txq, [ClientTxMsg(["yes"], {clientWs1, clientWs2})]) # Delete msgSrc self.conns.delMsgSrc(msgSrc2) # Add another client self.conns.addConn(clientWs3) self.assertSetEqual(self.conns.msgSrcs, {msgSrc1}) self.assertGiTxQueueMsgs(self.txq, [ ClientTxMsg([1], {clientWs3}, initiatorWs=clientWs3), ClientTxMsg([2], {clientWs3}, initiatorWs=clientWs3) ], anyOrder=True) # Remove a client self.conns.delConn(clientWs2) self.assertGiTxQueueMsgs(self.txq, []) # Add msgSrc2 again self.conns.addMsgSrc(msgSrc2) self.assertGiTxQueueMsgs( self.txq, [ClientTxMsg(["yes"], {clientWs1, clientWs3}, initiatorWs=None)], anyOrder=True)
def testAddDelConnections(self): conns1 = Connections(self.txq) conns1.addConn(clientWs1) conns2 = Connections(self.txq) conns2.addConn(clientWs2) self.conns.addConnections(conns1) self.conns.addConnections(conns2) msgSrc = MsgSrc(self.conns) msgSrc.setMsgs([ Jmai([1], initiatorWs=clientWs3), Jmai([2], initiatorWs=clientWs3) ]) self.assertGiTxQueueMsgs(self.txq, [ ClientTxMsg([1], {clientWs1}, initiatorWs=clientWs3), ClientTxMsg([1], {clientWs2}, initiatorWs=clientWs3), ClientTxMsg([2], {clientWs1}, initiatorWs=clientWs3), ClientTxMsg([2], {clientWs2}, initiatorWs=clientWs3) ], anyOrder=True) self.conns.delConnections(conns1) clientWs4 = 104 conns3 = Connections(self.txq) conns3.addConn(clientWs3) conns3.addConn(clientWs4) self.conns.addConnections(conns3) self.assertGiTxQueueMsgs(self.txq, [ ClientTxMsg([1], {clientWs3, clientWs4}, initiatorWs=clientWs3), ClientTxMsg([2], {clientWs3, clientWs4}, initiatorWs=clientWs3) ], anyOrder=True)
def __init__(self, txQueue, name, passwd): self.name = name self.passwd = passwd self.playerConns = Connections(txQueue)