class Network(): def __init__(self): self.c_manager = QueuedConnectionManager() self.c_listener = QueuedConnectionListener(self.c_manager, 0) self.c_reader = QueuedConnectionReader(self.c_manager, 0) self.c_writer = ConnectionWriter(self.c_manager,0) self.active_conns=[] self.port_address=9099 #No-other TCP/IP services are using this port self.backlog=1000 #If we ignore 1,000 connection attempts, something is wrong! #tcp_socket = c_manager.openTCPServerRendezvous(port_address,backlog) #self.tcp_socket = self.c_manager.openTCPServerRendezvous("0.0.0.0", 9099, 1000) #self.c_listener.addConnection(self.tcp_socket) def tsk_listener_pol(self, taskdata): if self.c_listener.newConnectionAvailable(): rendezvous = PointerToConnection() netAddress = NetAddress() newConnection = PointerToConnection() if self.c_listener.getNewConnection(rendezvous,netAddress,newConnection): print("Connected: "+str(netAddress)) newConnection = newConnection.p() self.active_conns.append(newConnection) # Remember connection self.c_reader.addConnection(newConnection) # Begin reading connection return Task.cont def tsk_reader_pol(self, taskdata): if self.c_reader.dataAvailable(): datagram=NetDatagram() # catch the incoming data in this instance # Check the return value; if we were threaded, someone else could have # snagged this data before we did if self.c_reader.getData(datagram): self.data_process(datagram) return Task.cont def data_process(self, datagram): it = PyDatagramIterator(datagram) message = it.getString() print("Processed: "+message+" from: "+str(datagram.getAddress())) def listen(self): print("Now listening on all addresses on port 9099") self.tcp_socket = self.c_manager.openTCPServerRendezvous("0.0.0.0", 9099, 1000) self.c_listener.addConnection(self.tcp_socket) taskMgr.add(self.tsk_listener_pol, "Poll connection listener", -39) taskMgr.add(self.tsk_reader_pol, "Pol the connection reader", -40) def unlisten(self): print("Stopping listen") self.c_manager.closeConnection(self.tcp_socket)
def SetupCommunication(self): cManager = QueuedConnectionManager() cListener = QueuedConnectionListener(cManager, 0) cReader = QueuedConnectionReader(cManager, 0) self.activeConnections = [] # We'll want to keep track of these later port_address = 9098 # No-other TCP/IP services are using this port backlog = 1000 # If we ignore 1,000 connection attempts, something is wrong! tcpSocket = cManager.openTCPServerRendezvous(port_address, backlog) cListener.addConnection(tcpSocket) def tskListenerPolling(taskdata): if cListener.newConnectionAvailable(): rendezvous = PointerToConnection() netAddress = NetAddress() newConnection = PointerToConnection() if cListener.getNewConnection(rendezvous, netAddress, newConnection): newConnection = newConnection.p() self.activeConnections.append( newConnection) # Remember connection cReader.addConnection( newConnection) # Begin reading connection return Task.cont def tskReaderPolling(taskdata): if cReader.dataAvailable(): datagram = NetDatagram( ) # catch the incoming data in this instance # Check the return value; if we were threaded, someone else could have # snagged this data before we did if cReader.getData(datagram): self.myProcessDataFunction(datagram) return Task.cont self.taskMgr.add(tskReaderPolling, "Poll the connection reader", -40) self.taskMgr.add(tskListenerPolling, "Poll the connection listener", -39)
class TCP(): def __init__(self, _core): print("TCP Protocol Startup...") self.core = _core self.config = self.core.server.config def start(self): self.setupTCP() self.startTCPTasks() def setupTCP(self): self.tcpManager = QueuedConnectionManager() self.tcpReader = QueuedConnectionReader(self.tcpManager, 0) self.tcpWriter = ConnectionWriter(self.tcpManager, 0) self.tcpListener = QueuedConnectionListener(self.tcpManager, 0) self.tcpSocket = self.tcpManager.openTCPServerRendezvous( self.config.TCPPORT, self.config.BACKLOG) self.tcpListener.addConnection(self.tcpSocket) print("Started Server on: ", self.config.HOSTNAME, self.config.TCPPORT) def startTCPTasks(self): taskMgr.add(self.tcpListenerTask, "tcpListenerTask", 0) print("TCP Listener Started") taskMgr.add(self.tcpReaderTask, "tcpReaderTask", -10) print("TCP Reader Started") taskMgr.add(self.tcpDisconnectionHandler, "tcpDisconnectionHandler", 20) print("TCP Disconnection Handler Started") # TCP Listener Task def tcpListenerTask(self, task): """ Accept new incoming connection from clients, related to TCP """ # Handle new connection if self.tcpListener.newConnectionAvailable(): rendezvous = PointerToConnection() netAddress = NetAddress() newConnection = PointerToConnection() if self.tcpListener.getNewConnection(rendezvous, netAddress, newConnection): newConnection = newConnection.p() # Tell the reader about the new TCP connection self.tcpReader.addConnection(newConnection) # Handle the connection depending on persistent or not if self.core.server.isPersistent: self.core.handleConnection(generateUUID(), newConnection, netAddress) else: self.core.createPlayerObject(generateUUID(), newConnection, netAddress) print("Server: New Connection from -", str(netAddress.getIpString())) else: print("Server: Connection Failed from -", str(netAddress.getIpString())) return Task.cont def tcpReaderTask(self, task): """ Handle any data from clients by sending it to the Handlers. """ while 1: (datagram, data, opcode) = self.tcpNonBlockingRead(self.tcpReader) if opcode is MSG_NONE: # Do nothing or use it as some 'keep_alive' thing. break else: # Handle it self.core.packetManager.handlePacket(opcode, data, datagram.getAddress()) return Task.cont # TCP NonBlockingRead?? def tcpNonBlockingRead(self, qcr): """ Return a datagram collection and type if data is available on the queued connection udpReader """ if self.tcpReader.dataAvailable(): datagram = NetDatagram() if self.tcpReader.getData(datagram): data = DatagramIterator(datagram) opcode = data.getUint8() else: data = None opcode = MSG_NONE else: datagram = None data = None opcode = MSG_NONE # Return the datagram to keep a handle on the data return (datagram, data, opcode) # TCP Disconnection Handler def tcpDisconnectionHandler(self, task): # Check for resets if self.tcpManager.resetConnectionAvailable(): resetConnection = PointerToConnection() self.tcpManager.getResetConnection(resetConnection) for client in self.core.server.clients: if self.core.server.clients[ client].connection == resetConnection.p(): del self.core.server.clients[client] self.tcpReader.removeConnection(resetConnection.p()) print("Removed Connection:", resetConnection.p()) print('Current Clients:', self.core.server.clients) break return Task.cont def sendPacket(self, _pkt, _connection): self.tcpWriter.send(_pkt, _connection) def sendBroadcast(self, _pkt, _skipif=None): for client in self.serverManager.clients: if _skipif == client: pass else: conn = self.serverManager.clients[client].connection self.tcpWriter.send(_pkt, conn)
class TCP(): def __init__(self, _core): print ("TCP Protocol Startup...") self.core = _core self.config = self.core.server.config def start(self): self.setupTCP() self.startTCPTasks() def setupTCP(self): self.tcpManager = QueuedConnectionManager() self.tcpReader = QueuedConnectionReader(self.tcpManager, 0) self.tcpWriter = ConnectionWriter(self.tcpManager, 0) self.tcpListener = QueuedConnectionListener(self.tcpManager, 0) self.tcpSocket = self.tcpManager.openTCPServerRendezvous(self.config.TCPPORT, self.config.BACKLOG) self.tcpListener.addConnection(self.tcpSocket) print ("Started Server on: ", self.config.HOSTNAME, self.config.TCPPORT) def startTCPTasks(self): taskMgr.add(self.tcpListenerTask, "tcpListenerTask", 0) print ("TCP Listener Started") taskMgr.add(self.tcpReaderTask, "tcpReaderTask", -10) print ("TCP Reader Started") taskMgr.add(self.tcpDisconnectionHandler, "tcpDisconnectionHandler", 20) print ("TCP Disconnection Handler Started") # TCP Listener Task def tcpListenerTask(self, task): """ Accept new incoming connection from clients, related to TCP """ # Handle new connection if self.tcpListener.newConnectionAvailable(): rendezvous = PointerToConnection() netAddress = NetAddress() newConnection = PointerToConnection() if self.tcpListener.getNewConnection(rendezvous, netAddress, newConnection): newConnection = newConnection.p() # Tell the reader about the new TCP connection self.tcpReader.addConnection(newConnection) # Handle the connection depending on persistent or not if self.core.server.isPersistent: self.core.handleConnection(generateUUID(), newConnection, netAddress) else: self.core.createPlayerObject(generateUUID(),newConnection, netAddress) print ("Server: New Connection from -", str(netAddress.getIpString())) else: print ("Server: Connection Failed from -", str(netAddress.getIpString())) return Task.cont def tcpReaderTask(self, task): """ Handle any data from clients by sending it to the Handlers. """ while 1: (datagram, data, opcode) = self.tcpNonBlockingRead(self.tcpReader) if opcode is MSG_NONE: # Do nothing or use it as some 'keep_alive' thing. break else: # Handle it self.core.packetManager.handlePacket(opcode, data, datagram.getAddress()) return Task.cont # TCP NonBlockingRead?? def tcpNonBlockingRead(self, qcr): """ Return a datagram collection and type if data is available on the queued connection udpReader """ if self.tcpReader.dataAvailable(): datagram = NetDatagram() if self.tcpReader.getData(datagram): data = DatagramIterator(datagram) opcode = data.getUint8() else: data = None opcode = MSG_NONE else: datagram = None data = None opcode = MSG_NONE # Return the datagram to keep a handle on the data return (datagram, data, opcode) # TCP Disconnection Handler def tcpDisconnectionHandler(self, task): # Check for resets if self.tcpManager.resetConnectionAvailable(): resetConnection = PointerToConnection() self.tcpManager.getResetConnection(resetConnection) for client in self.core.server.clients: if self.core.server.clients[client].connection == resetConnection.p(): del self.core.server.clients[client] self.tcpReader.removeConnection(resetConnection.p()) print ("Removed Connection:", resetConnection.p()) print ('Current Clients:', self.core.server.clients) break return Task.cont def sendPacket(self, _pkt, _connection): self.tcpWriter.send(_pkt, _connection) def sendBroadcast(self, _pkt, _skipif=None): for client in self.serverManager.clients: if _skipif == client: pass else: conn = self.serverManager.clients[client].connection self.tcpWriter.send(_pkt, conn)
from panda3d.core import NetDatagram from panda3d.core import PointerToConnection from panda3d.core import NetAddress from direct.task.TaskManagerGlobal import taskMgr cManager = QueuedConnectionManager() cListener = QueuedConnectionListener(cManager, 0) cReader = QueuedConnectionReader(cManager, 0) cWriter = ConnectionWriter(cManager, 0) activeConnections = [] # We'll want to keep track of these later port_address = 9099 # No-other TCP/IP services are using this port backlog = 1000 # If we ignore 1,000 connection attempts, something is wrong! tcpSocket = cManager.openTCPServerRendezvous(port_address, backlog) cListener.addConnection(tcpSocket) def tskListenerPolling(taskdata): if cListener.newConnectionAvailable(): rendezvous = PointerToConnection() netAddress = NetAddress() newConnection = PointerToConnection() if cListener.getNewConnection(rendezvous, netAddress, newConnection): newConnection = newConnection.p() activeConnections.append(newConnection) # Remember connection cReader.addConnection(newConnection) # Begin reading connection
class Server: def __init__(self): self.port = 5555 self.addr = "127.0.0.1" self.backlog = 7 self.active_connections = dict() self.start = False self.connect() def connect(self) -> None: # Handle connections and terminations self.manager = QueuedConnectionManager() # Wait for clients connection requests self.listener = QueuedConnectionListener(self.manager, 0) # Buffers incoming data from active connection self.reader = QueuedConnectionReader(self.manager, 0) # Transmit PyDatagrams to active connection self.writer = ConnectionWriter(self.manager, 0) # Open TCP Rendezvous to accept client connections with a limit self.socket = self.manager.openTCPServerRendezvous( self.port, self.backlog) self.listener.addConnection(self.socket) print("Server listening on port %s...." % str(self.port)) # Listen for mew incoming connections taskMgr.add(self.handle_incoming_connections, "Poll the connection listener", -39) # Listen for new datagrams taskMgr.add(self.handle_connection_data, "Poll the connection reader", -40) # Listen for dropped connections taskMgr.add(self.handle_dropped_connections, "Poll the dropped connection listener", -41) # See if game can be started taskMgr.add(self.start_game, "Start Game", -42) def start_game(self, task_data: Task) -> Task: if len(self.active_connections) == self.backlog: self.start = True return Task.cont def handle_incoming_connections(self, task_data: Task) -> Task: if self.listener.newConnectionAvailable(): rendezvous = PointerToConnection() net_addr = NetAddress() new_connection = PointerToConnection() if self.listener.getNewConnection(rendezvous, net_addr, new_connection): new_connection = new_connection.p() # Keep track of our active connections self.active_connections[str( new_connection.this)] = new_connection # Start reading the new connection self.reader.addConnection(new_connection) print("%s just connected" % str(new_connection)) return Task.cont def handle_connection_data(self, task_data: Task) -> Task: if self.reader.dataAvailable(): # Catch the incoming data datagram = NetDatagram() if self.reader.getData(datagram): name = self.handle_client_message(datagram) broadcast = f"Everyone, welcome {name} to the game!" self.broadcast_message(broadcast) return Task.cont def handle_dropped_connections(self, task_data: Task) -> Task: if self.manager.resetConnectionAvailable(): connection_pointer = PointerToConnection() self.manager.getResetConnection(connection_pointer) lost_connection = connection_pointer.p() print("% s disconnected from server" % str(lost_connection)) del self.active_connections[str(lost_connection.this)] self.manager.closeConnection(lost_connection) return Task.cont def handle_client_message(self, message: str) -> str: iterator = PyDatagramIterator(message) return iterator.getString() def get_connections_count(self) -> int: return len(self.active_connections) def send_personal_message(self, message: str, client: PointerToConnection) -> None: datagram = self.create_new_datagram(message) self.writer.send(datagram, client) def broadcast_message(self, message: str) -> None: datagram = self.create_new_datagram(message) for client in self.active_connections: self.writer.send(datagram, self.active_connections[client]) def create_new_datagram(self, message: str) -> PyDatagram: new_datagram = PyDatagram() new_datagram.addString(message) return new_datagram def terminate_all_clients(self) -> None: for client in self.active_connections: self.reader.removeConnection(client) self.active_connections = list() def terminate_specific_client(self, client: PointerToConnection) -> None: self.reader.removeConnection(client) del self.active_connections[str(client)]
class ConnectionManager: notify = DirectNotifyGlobal.directNotify.newCategory("ConnectionManager") def __init__(self): self.socket = None self.hostName = None self.port = None self.ourChannel = 100001 self.cManager = QueuedConnectionManager() self.cListener = QueuedConnectionListener(self.cManager, 0) self.cReader = QueuedConnectionReader(self.cManager, 0) self.cWriter = ConnectionWriter(self.cManager, 0) def start(self): self.hostName = base.configManager.getString('host-name', '127.0.0.1') self.port = base.configManager.getInt('port-number', 6667) self.socket = self.cManager.openTCPServerRendezvous( self.hostName, self.port, 1000) if self.socket: self.cListener.addConnection(self.socket) self._serverStarted(self.hostName, self.port) taskMgr.add(self._socketListener, 'Connection Listener') taskMgr.add(self._socketReader, 'Connection Reader') else: self.notify.warning( "Unable to start server on %s:%d - is the port in use?" % (self.hostName, self.port)) base.logManager.writeServerEvent( 'ServerBase', 'Unable to start server on %s:%d - is the port in use?' % (self.hostName, self.port)) sys.exit() def _serverStarted(self, host, port): self.notify.warning("Server started on %s:%d" % (host, port)) base.logManager.writeServerEvent( 'ServerBase', 'Server started on %s:%d' % (host, port)) def _socketListener(self, task): if self.cListener.newConnectionAvailable(): rendezvous = PointerToConnection() netAddress = NetAddress() newConnection = PointerToConnection() if self.cListener.getNewConnection(rendezvous, netAddress, newConnection): newConnection = newConnection.p() base.activeConnections[newConnection] = NetworkedClient() self.cReader.addConnection(newConnection) self.notify.warning("New Unauthed Client Connected: %s" % (netAddress)) return Task.cont def _socketReader(self, task): if self.cReader.dataAvailable(): datagram = NetDatagram() if self.cReader.getData(datagram): base.messageManager.handleMessage(datagram) return Task.cont
class NetworkHost (): """ Handles networking with one or more clients. This class is essentially a server that handles the communication of GameManager's game logic. One player will have a NetworkHost and the rest of players will have NetworkClients. """ def __init__ (self, gameManager): self._connManager = QueuedConnectionManager() self._loadConfig() self._activeConns = [] # Active connections list. self._isActive = False self._backlog = HOST_MAX_BACKLOG self._gameManager = gameManager self._playerInfo = dict() # connections by connectionID (cID) self._creatures = dict() # Creatures by cID. self._localPlayerCID = None self._creatureIDCount = 0 def _initListener (self): """ Initializes this NetworkHost's connection listener. """ self._connListener = QueuedConnectionListener(self._connManager, 0) self._tcpSocket = connManager.openTCPServerRendezvous(self._portAddress, self._backlog) self._connListener.addConnection(self._tcpSocket) def _loadConfig (self): """ Loads network configuration defaults. """ self._portAddress = ConfigVariableInt("default-port", DEFAULT_PORT).getValue() def startHost (self): """ Finishes initialization and begins listening. """ # Initialize Reader and Writer: self._connReader = QueuedConnectionReader(self._connManager, 0) self._connWriter = ConnectionWriter(self._connManager, 0) # Initialize Listener: self._connListener = QueuedConnectionListener(self._connManager, 0) self._tcpSocket = self._connManager.openTCPServerRendezvous( self._portAddress, self._backlog) self._connListener.addConnection(self._tcpSocket) # Begin handling messages (start listening): taskMgr.add(self._onListenerPoll,"Poll the connection listener",-39) taskMgr.add(self._onReaderPoll,"Poll the connection reader",-40) self._isActive = True print ("[Host Started at %s]" % socket.gethostbyname( socket.gethostname())) self._gameManager.onHostInitialized() def _onListenerPoll(self, taskdata): """ Updates list of connections based on the listener's current findings. Does not read messages. See onReaderPoll(). (Assumes self._connListener has been initialized) """ # Check for new connections: if self._connListener.newConnectionAvailable(): rendezvous = PointerToConnection() netAddress = NetAddress() newConnection = PointerToConnection() # If we have a new connection, add it to our list: if self._connListener.getNewConnection(rendezvous,netAddress, newConnection): newConnection = newConnection.p() print ("[Host Received New Connection: %s]" % netAddress) self._activeConns.append(newConnection) # Begin reading messages from this new connection: self._connReader.addConnection(newConnection) # activate the onClientConnected functionalities: self.onClientConnected(newConnection) return Task.cont # Repeat this call on an interval def _onReaderPoll (self, taskdata): """ Called on an interval to interpret messages from the reader. """ if self._connReader.dataAvailable(): newDatagram = NetDatagram() # Double check to make sure (Multithreading safety): if self._connReader.getData(newDatagram): self._interpretDatagram(newDatagram) return Task.cont # Repeat this call on an interval def sendToClient (self, newMsg, conn, msgType): """ Sends a new message to a client at the other end of conn. """ print("[Server Sending %s message type %s]"%(str(conn), str(msgType))) self._connWriter.send(newMsg, conn) def sendToAll (self, newMsg, msgType): """ Writes and sends a new message to all connected clients. """ for conn in self._activeConns: self.sendToClient(newMsg, conn, msgType) def _interpretDatagram (self, datagram): """ Interprets a received datagram and performs actions based on its values. """ msg = PyDatagramIterator(datagram) msgType = msg.getUint8() if msgType == DEBUG_MESSAGE: print (msg.getString()) elif msgType == UPDATE_PLAYER_INFO: data = msg.getString() self._updatePlayerInfoHandler(datagram.getConnection().this, data) elif msgType == SPAWN_CHARACTER: data = msg.getString() dataDict = json.loads(data) self._onSpawnHandler(dataDict) elif msgType == SYNC_ACTION: data = msg.getString() dataDict = json.loads(data) self._onActionSyncHandler(dataDict, datagram.getConnection()) elif msgType == SYNC_RESPAWN: data = msg.getString() dataDict = json.loads(data) self._onRespawnRequestReceived(dataDict) elif msgType == WIN_STATE: data = msg.getString() self._onGameWon(data) def isHosting (self): """ Returns whether this NetworkHost is actively hosting. """ return self._isActive def getMyCID (self): return self._localPlayerCID def registerNewCID (self): newCID = "host" + str(self._creatureIDCount) self._creatureIDCount += 1 return newCID def registerLocalCID (self): newCID = self.registerNewCID() self._localPlayerCID = newCID # === [Local Client to Network] === def onCreatureDeath (self, creature): """ Sends a creature death message to all connected clients. """ msg = createSyncDeathMessage(creature.getCID()) self.sendToAll(msg, SYNC_DEATH) def onLocalPlayerRespawn (self, creature, newLocation): """ Respawns the local player and sends a sync message to all remotes. """ # Fulfill local player request to respawn: creature.respawn(newLocation) # Refill the creature's HP (Automatically syncs!): creature.takeDamage(-1*creature.getMaxHealth()) # Spawn on all connected clients: msg = createRespawnMessage(creature.getCID(), newLocation) self.sendToAll(msg, SYNC_RESPAWN) def localPlayerWins (self): """ End the game display the win screen. """ self._gameManager.onWinStateAchieved(self._playerInfo[self.getMyCID()]) msg = createWinMessage(self._playerInfo[self.getMyCID()]) self.sendToAll(msg, WIN_STATE) def updateLocalPlayerInfo (self, info=None): """ Updates info for this local player and sends it to all connected clients. """ if not info: self._playerInfo[self.getMyCID()] = PlayerInfo(cID=self.getMyCID()) else: # If info == None, we are just initializing. self._playerInfo[self.getMyCID()] = info infoMsg = createPlayerInfoMessage(info) self.sendToAll(infoMsg, UPDATE_PLAYER_INFO) # === === # === [Gameplay specific] === def _onGameWon (self, data): """ Boo. A remote client won. Oh well. Still have to sync that win across all other clients. """ # Show winner locally: winnerData = PlayerInfo(fromJson=data) self._gameManager.onWinStateAchieved(winnerData) msg = createWinMessage(winnerData) self.sendToAll(msg, WIN_STATE) def syncAction (self, cID, actionID, **kwargs): """ The local player has performed an action that must be synced across the network. Send a message to all clients telling them to perform a related action on that character. """ msg = createSyncActionMessage(cID, actionID, **kwargs) self.sendToAll(msg, SYNC_ACTION) def spawnGameObject (self, gameObject): """ Tracks the given gameObject and sends it to all clients. """ # First, track it locally: self._creatures[gameObject.getCID()] = gameObject # Send to all clients: msg = createSpawnCharacterMessage(gameObject, gameObject.getCID()) self.sendToAll(msg, SPAWN_CHARACTER) def dropItem (self, itemEnum, pos): """ Spawn a new item locally and sync! """ itemID = self.registerNewCID() # Create item locally: newItem = ITEM_ID_DICT[itemEnum](self._gameManager, itemID, coords=pos) self._gameManager.getTileMap().spawnItem(newItem, pos) # Track new item: self._creatures[itemID] = newItem msg = createSpawnItemMessage(newItem) self.sendToAll(msg, SPAWN_ITEM) def _onSpawnHandler (self, dataDict): """ Handles networking spawning characters """ # Spawn object locally if the object at cID doesn't already exist. if not dataDict['objID'] in self._creatures.keys(): # Spawn object of charType at pos objectType = getCharacterTypeAsClass(dataDict['charType']) newPos = Point2D(dataDict['pos'][0], dataDict['pos'][1]) newChar = objectType(parentCtrlr=None, cID=dataDict['objID'], gameManager=self._gameManager, coords=newPos) self._creatures[dataDict['objID']] = newChar self._gameManager.getTileMap().spawnObject(newChar, newPos) print("[Server Spawned %s]" % dataDict['objID']) # If we have a player info for this player, use their name for the # displayName: if dataDict['objID'] in self._playerInfo: newName = self._playerInfo[dataDict['objID']].cName newChar.setNameDisplay(newName) else: # Ignore Overwrite pass # Tell all other clients to spawn objects: newMsg = createSpawnCharacterMessage(self._creatures[dataDict['objID']], dataDict['objID']) self.sendToAll(newMsg, SPAWN_CHARACTER) def _onActionSyncHandler (self, dataDict, msgConn): """ Attempts to queue an action for execution on a target denoted by dataDict['objID'] """ copyMsg = createSyncActionMessage(**dataDict) syncedAction = ACTION_NETWORKING_DICT[dataDict['actionID']] # Add a few local variables to dataDict: targetObj = self._creatures[dataDict['objID']] # TODO Maybe make this part of dataDict! dataDict['tileMap'] = self._gameManager.getTileMap() if 'targetCID' in dataDict: # If there is another target: # Assign the target: dataDict['target'] = self._creatures[dataDict['targetCID']] dataDict['isServer'] = True # Let sync function know we are server # Create the newAction newAction = syncedAction(targetObj, **dataDict) targetObj.startAction(newAction) # queue or start the new action # Send action to all clients except the client that sent the sync msg: for client in self.getAllClientsExcept(msgConn): self.sendToClient(copyMsg, client, SYNC_ACTION) def _onRespawnRequestReceived (self, dataDict): """ Respawns the remote clients character in a new position and then syncs to all clients (including the one who requested). """ newPos = self._gameManager.getTileMap().getRandomEmptyFloor() targetObj = self._creatures[dataDict['objID']] # Set the target's HP to full and sync that: targetObj.takeDamage(-1*targetObj.getMaxHealth()) targetObj.respawn(newPos) # Sync the respawn to all clients: newMsg = createRespawnMessage(targetObj.getCID(), newPos) self.sendToAll(newMsg, SYNC_RESPAWN) def getAllClientsExcept (self, exceptConn): clientList = list() for conn in self._activeConns: if conn != exceptConn: clientList.append(conn) return clientList def onClientConnected (self, clientConn): """ If we have a map and/or any positional data, give it to this client. Also signal to the GameManager and all remote clients that a new player connected! """ connID = clientConn.this tileMap = self._gameManager.getTileMap() if tileMap != None: data = tileMap.getTileMapStr() msg = createMapMessage(data) self.sendToClient(msg, clientConn, MAP_MESSAGE) # Send player info to the new client: for player in self._playerInfo: # Don't send an info message about the player to the same player! if player != connID: newInfoMsg = createPlayerInfoMessage(self._playerInfo[player]) self.sendToClient(newInfoMsg, clientConn, UPDATE_PLAYER_INFO) # Send all creatures to the new client: for creatureID in self._creatures: newMsg = createSpawnCharacterMessage(self._creatures[creatureID], creatureID) self.sendToClient(newMsg, clientConn, SPAWN_CHARACTER) def _updatePlayerInfoHandler (self, connID, data=None): """ Adds data to self._playerInfo. If info doesn't exist, creates a new one for clientConn. """ if data != None: newPlayerData = PlayerInfo(fromJson=data) else: newPlayerData = PlayerInfo(cID=connID) # Update the playerInfo dict with the new data: self._playerInfo[newPlayerData.cID] = newPlayerData self._gameManager.updatePartyInfo(self._playerInfo, self.getMyCID()) # Send player info to every client: for player in self._playerInfo: newInfoMsg = createPlayerInfoMessage(self._playerInfo[player]) #Send every player to every client: self.sendToAll(newInfoMsg, UPDATE_PLAYER_INFO) # Update the creature's floating display name locally: if newPlayerData.cID in self._creatures: self._creatures[newPlayerData.cID]\ .setNameDisplay(newPlayerData.cName) def syncHealthChange (self, creatureID, newHealth): """ Called after the host runs a damage/healing action on a creature. Lets all clients know to update to a new value. """ data = createSyncHealthMessage(creatureID, newHealth) self.sendToAll(data, SYNC_HEALTH)
class NetworkManager: def __init__(self): print "Network Manager Started" def connection_open(self): self.cManager = QueuedConnectionManager() self.cReader = QueuedConnectionReader(self.cManager, 0) self.cWriter = ConnectionWriter(self.cManager,0) self.activeConnections=[] # We'll want to keep track of these later self.cListener = QueuedConnectionListener(self.cManager, 0) port_address=9099 #No-other TCP/IP services are using this port backlog=1000 #If we ignore 1,000 connection attempts, something is wrong! self.tcpSocket = self.cManager.openTCPServerRendezvous(port_address,backlog) self.cListener.addConnection(self.tcpSocket) print "Network Connection Opened" taskMgr.add(self.tskListenerPolling,"Poll the connection listener",-39) taskMgr.add(self.tskReaderPolling,"Poll the connection reader",-40) def connection_close(self): for aClient in self.activeConnections: self.cReader.removeConnection(aClient) self.activeConnections=[] # close down our listener self.cManager.closeConnection(self.tcpSocket) print "Network Connection Closed" def tskListenerPolling(self, taskdata): if self.cListener.newConnectionAvailable(): rendezvous = PointerToConnection() netAddress = NetAddress() newConnection = PointerToConnection() if self.cListener.getNewConnection(rendezvous,netAddress,newConnection): newConnection = newConnection.p() self.activeConnections.append(newConnection) # Remember connection self.cReader.addConnection(newConnection) # Begin reading connection return Task.cont def tskReaderPolling(self, taskdata): if self.cReader.dataAvailable(): datagram=NetDatagram() # catch the incoming data in this instance # Check the return value; if we were threaded, someone else could have # snagged this data before we did if self.cReader.getData(datagram): if base.client == True: self.client_processing(datagram) else: self.server_processing(datagram) return Task.cont def server_messager(self,msg,args=[]): if msg == "map_set": order = PyDatagram() order.addUint16(MAP_SET) order.addInt32(args[0]) self.send_package(order) elif msg == "client_update": order = PyDatagram() order.addUint16(CLIENT_INIT_UPDATE) order.addString(args[0]) order.addString(args[1]) order.addInt32(args[2]) order.addInt32(args[3]) self.send_package(order) elif msg == "chat_send": r = args[0][0] g = args[0][1] b = args[0][2] order = PyDatagram() order.addUint16(SERVER_CHAT) order.addInt32(r) order.addInt32(g) order.addInt32(b) order.addString(args[1]) self.send_package(order) base.menu_manager.menus["mp-game"].chat_add((r,g,b,1),args[1]) elif msg == "ready_button": order = PyDatagram() order.addUint16(SERVER_READY) order.addInt32(args[0]) order.addInt32(args[1]) self.send_package(order) base.menu_manager.menus["mp-game"].obj_list[args[0]]["indicatorValue"]=args[1] base.menu_manager.menus["mp-game"].start_game_check() elif msg == "server_loaded": order = PyDatagram() order.addUint16(SERVER_LOADED) self.send_package(order) elif msg == "all_loaded": order = PyDatagram() order.addUint16(ALL_LOADED) self.send_package(order) elif msg == "game_start": order = PyDatagram() order.addUint16(GAME_START) self.send_package(order) elif msg == "army_kill": order = PyDatagram() order.addUint16(ARMY_KILL) order.addInt32(args[0]) self.send_package(order) elif msg == "build_start": order = PyDatagram() order.addUint16(BUILD_START) order.addInt32(args[0]) order.addInt8(args[1]) order.addString(args[2]) self.send_package(order) elif msg == "tower_capture": order = PyDatagram() order.addUint16(TOWER_CAPTURE) order.addInt32(args[0]) order.addInt8(args[1]) self.send_package(order) elif msg == "build_complete": order = PyDatagram() order.addUint16(BUILD_COMPLETE) order.addInt32(args[0]) order.addInt8(args[1]) order.addString(args[2]) self.send_package(order) elif msg == "build_cancel": order = PyDatagram() order.addUint16(BUILD_CANCEL) order.addInt32(args[0]) self.send_package(order) elif msg == "battle_start": order = PyDatagram() order.addUint16(BATTLE_START) order.addInt32(args[0]) order.addFloat32(args[1]) order.addFloat32(args[2]) order.addInt32(args[3]) order.addFloat32(args[4]) order.addFloat32(args[5]) order.addInt32(args[6]) self.send_package(order) elif msg == "battle_clash": order = PyDatagram() order.addUint16(BATTLE_CLASH) order.addInt32(args[0]) order.addInt32(args[1]) order.addInt32(args[2]) order.addString(args[3]) order.addInt8(args[4]) self.send_package(order) elif msg == "battle_turn": order = PyDatagram() order.addUint16(BATTLE_TURN) order.addInt32(args[0]) order.addInt32(args[1]) self.send_package(order) elif msg == "battle_end": order = PyDatagram() order.addUint16(BATTLE_END) order.addInt32(args[0]) self.send_package(order) elif msg == "battle_armyadd": order = PyDatagram() order.addUint16(BATTLE_ARMYADD) order.addInt32(args[0]) order.addInt32(args[1]) order.addFloat32(args[2]) order.addFloat32(args[3]) self.send_package(order) def client_messager(self,msg,args=[]): if msg == "chat_send": order = PyDatagram() order.addUint16(CLIENT_CHAT) order.addInt32(args[0][0]) order.addInt32(args[0][1]) order.addInt32(args[0][2]) order.addString(args[1]) self.send_package(order) elif msg == "ready_button": order = PyDatagram() order.addUint16(CLIENT_READY) order.addInt32(args[0]) order.addInt32(args[1]) self.send_package(order) elif msg == "client_loaded": order = PyDatagram() order.addUint16(CLIENT_LOADED) self.send_package(order) elif msg == "game_init_request": order = PyDatagram() order.addUint16(CLIENT_INIT_REQUEST) order.addString(args[0]) order.addString(args[1]) self.send_package(order) elif msg == "build_start_request": order = PyDatagram() order.addUint16(BUILD_START_REQUEST) order.addInt32(args[0]) order.addInt32(args[1]) order.addString(args[2]) self.send_package(order) elif msg == "build_cancel_request": order = PyDatagram() order.addUint16(BUILD_CANCEL_REQUEST) order.addInt32(args[0]) self.send_package(order) def client_processing(self,datagram): data_iter = PyDatagramIterator(datagram) msgID = data_iter.getUint16() if msgID == PRINT_MESSAGE: messageToPrint = data_iter.getString() print messageToPrint if msgID == ARMY_MOVE: army_id = data_iter.getInt16() ax = data_iter.getFloat64() ay = data_iter.getFloat64() tx = data_iter.getFloat64() ty = data_iter.getFloat64() base.armies[army_id].node_path.setX(ax) base.armies[army_id].node_path.setY(ay) base.armies[army_id].move_to_point(tx,ty) if msgID == CLIENT_INIT_UPDATE: p1_name = data_iter.getString() p1_kingdom = data_iter.getString() p1_ready = data_iter.getInt32() game_map = data_iter.getInt32() base.menu_manager.menus["mp-game"].client_update(p1_name,p1_kingdom,p1_ready,game_map) if msgID == SERVER_CHAT: r = data_iter.getInt32() g = data_iter.getInt32() b = data_iter.getInt32() text = data_iter.getString() base.menu_manager.menus["mp-game"].chat_add((r,g,b),text) if msgID == SERVER_READY: but_id = data_iter.getInt32() state = data_iter.getInt32() base.menu_manager.menus["mp-game"].obj_list[but_id]["indicatorValue"]=state base.menu_manager.menus["mp-game"].start_game_check() if msgID == SERVER_LOADED: base.menu_manager.menus["mp-load"].load() if msgID == ALL_LOADED: base.menu_manager.menus["mp-load"].load_complete() if msgID == GAME_START: base.menu_manager.menu_goto("mp-load") if msgID == MAP_SET: map = data_iter.getInt32() base.menu_manager.menus["mp-game"].map_selected = map mapname = base.menu_manager.menus["mp-game"].maplist[map]["fullname"] mapimage = base.menu_manager.menus["mp-game"].maplist[map]["preview"] base.menu_manager.menus["mp-game"].obj_list[11]["text"]=mapname base.menu_manager.menus["mp-game"].obj_list[10].setImage(mapimage) if msgID == BATTLE_TURN: bat = data_iter.getInt32() turn = data_iter.getInt32() base.battles[bat].turn_change(turn) if msgID == BATTLE_START: a1 = data_iter.getInt32() a1_x = data_iter.getFloat32() a1_y = data_iter.getFloat32() a2 = data_iter.getInt32() a2_x = data_iter.getFloat32() a2_y = data_iter.getFloat32() army_start = data_iter.getInt32() base.armies[a1].stop() base.armies[a2].stop() base.armies[a1].node_path.setPos(a1_x,a1_y,0) base.armies[a2].node_path.setPos(a2_x,a2_y,0) base.battles.append(TimObjects.Battle([base.armies[a1],base.armies[a2]],army_start)) if msgID == BATTLE_CLASH: battle = data_iter.getInt32() a1 = data_iter.getInt32() a2 = data_iter.getInt32() result = data_iter.getString() buff = data_iter.getInt8() base.battles[battle].clash(base.armies[a1],base.armies[a2],result,buff) if msgID == BATTLE_ARMYADD: bat = data_iter.getInt32() army = data_iter.getInt32() a_x = data_iter.getFloat32() a_y = data_iter.getFloat32() base.battles[bat].add_army(base.armies[army]) base.armies[army].node_path.setPos(a_x,a_y,0) if msgID == BATTLE_END: bat = data_iter.getInt32() base.battles[bat].end() if msgID == BUILD_START: t_id = data_iter.getInt32() player = data_iter.getInt8() type = data_iter.getString() base.towers[t_id].build_start() if msgID == TOWER_CAPTURE: t_id = data_iter.getInt32() player = data_iter.getInt8() base.towers[t_id].change_owner(player) if msgID == BUILD_CANCEL: t_id = data_iter.getInt32() base.towers[t_id].build_cancel() if msgID == BUILD_COMPLETE: t_id = data_iter.getInt32() player = data_iter.getInt8() type = data_iter.getString() base.towers[t_id].create_counter() def server_processing(self,datagram): data_iter = PyDatagramIterator(datagram) msgID = data_iter.getUint16() if msgID == PRINT_MESSAGE: messageToPrint = data_iter.getString() print messageToPrint if msgID == ARMY_MOVE_REQUEST: army_id = data_iter.getInt16() ax = data_iter.getFloat64() ay = data_iter.getFloat64() tx = data_iter.getFloat64() ty = data_iter.getFloat64() base.armies[army_id].set_target(False,tx,ty) if msgID == CLIENT_CHAT: r = data_iter.getInt32() g = data_iter.getInt32() b = data_iter.getInt32() text = data_iter.getString() self.server_messager("chat_send",[(r,g,b),text]) #base.main_menu.chat_add((r,g,b,1),text) if msgID == CLIENT_READY: but_id = data_iter.getInt32() state = data_iter.getInt32() self.server_messager("ready_button",[but_id,state]) #base.main_menu.chat_add((r,g,b,1),text) if msgID == CLIENT_LOADED: self.server_messager("all_loaded",[]) base.menu_manager.menus["mp-load"].load_complete() if msgID == CLIENT_INIT_REQUEST: pn = data_iter.getString() pk = data_iter.getString() base.menu_manager.menus["mp-game"].obj_list[6]["text"] = pn base.menu_manager.menus["mp-game"].obj_list[7]["text"] = pk self.server_messager("client_update",[base.menu_manager.menus["mp-game"].obj_list[4]["text"], base.menu_manager.menus["mp-game"].obj_list[5]["text"], base.menu_manager.menus["mp-game"].obj_list[8]["indicatorValue"], base.menu_manager.menus["mp-game"].map_selected]) if msgID == BUILD_START_REQUEST: t_id = data_iter.getInt32() player = data_iter.getInt32() type = data_iter.getString() base.towers[t_id].build_start() if msgID == BUILD_CANCEL_REQUEST: t_id = data_iter.getInt32() player = data_iter.getInt32() type = data_iter.getString() base.towers[t_id].build_cancel() def msgAllClients(self): myPyDatagram=self.myNewPyDatagram() # build a datagram to send for aClient in self.activeConnections: self.cWriter.send(myPyDatagram,aClient) def send_package(self,package): # print "packaged" for aClient in self.activeConnections: print "Package",package,"sent" self.cWriter.send(package,aClient) def army_move(self,army_id,tx,ty): order = PyDatagram() if base.client == True: order.addUint16(ARMY_MOVE_REQUEST) else: order.addUint16(ARMY_MOVE) ax = base.armies[army_id].node_path.getX() ay = base.armies[army_id].node_path.getY() order.addInt16(army_id) order.addFloat64(ax) order.addFloat64(ay) order.addFloat64(tx) order.addFloat64(ty) if base.client == True: self.cWriter.send(order,base.server_connection) else: self.send_package(order) base.armies[army_id].move_to_point(tx,ty) def tower_train(self,tower_id,build_object): order = PyDatagram() if base.client == True: order.addUint16(REQUEST_TOWER_TRAIN) else: order.addUint16(TOWER_TRAIN) order.addInt16(army_id) order.addFloat64(tx) order.addFloat64(ty) if base.client == True: self.cWriter.send(order,base.server_connection) else: self.send_package(order) base.towers[tower_id].train_counter() # def request_army_move(self,army_id,tx,ty): # order = PyDatagram() # order.addUint16(REQUEST_MOVE_COUNTER) # order.addInt16(army_id) # order.addFloat64(tx) # order.addFloat64(ty) # self.cWriter.send(order,base.server_connection) def myNewPyDatagram(self): # Send a test message myPyDatagram = PyDatagram() myPyDatagram.addUint16(PRINT_MESSAGE) myPyDatagram.addString("You got ze message!") return myPyDatagram def client_connect(self,ip): port_address=9099 # same for client and server # a valid server URL. You can also use a DNS name # if the server has one, such as "localhost" or "panda3d.org" ip_address=ip # how long until we give up trying to reach the server? timeout_in_miliseconds=3000 # 3 seconds base.server_connection=self.cManager.openTCPClientConnection(ip_address,port_address,timeout_in_miliseconds) if base.server_connection: self.cReader.addConnection(base.server_connection) # receive messages from server self.activeConnections.append(base.server_connection) print "Connected to server",ip return True print "Connection failed" return False
class Server: def __init__(self): self.activeConnections = [] # lists all connections self.players = {} # keys are the players logins, values are the players datagram connections self.parties = {} # keys are the parties names, values are dicts representing parties data self.sessions = {} # keys are the datagram connections, values are dicts storing the characters of the player and its party self.playersinlobby = [] # lists players in the party screen self.charid = 0 # used for random team generation self.chars = [] # lists of dicts representing characters data self.cManager = QueuedConnectionManager() self.cListener = QueuedConnectionListener(self.cManager, 0) self.cReader = QueuedConnectionReader(self.cManager, 0) self.cReader.setTcpHeaderSize(4) self.send = Send(self.cManager) port = 3001 if len(sys.argv) > 1: port = sys.argv[1] self.tcpSocket = self.cManager.openTCPServerRendezvous(port, 10) self.cListener.addConnection(self.tcpSocket) print("Server listening on port", port) taskMgr.add(self.tskListenerPolling, "Poll the connection listener", -39) taskMgr.add(self.tskReaderPolling, "Poll the connection reader", -40) def processData(self, datagram): iterator = PyDatagramIterator(datagram) source = datagram.getConnection() callback = iterator.getString() getattr(globals()[callback], 'execute')(self, iterator, source) def updateAllPartyLists(self): parties = deepcopy(self.parties) for party in list(parties.values()): del party['map']['tiles'] for player in self.playersinlobby: self.send.UPDATE_PARTY_LIST(parties, player) def tskListenerPolling(self, taskdata): if self.cListener.newConnectionAvailable(): rendezvous = PointerToConnection() netAddress = NetAddress() newConnection = PointerToConnection() if self.cListener.getNewConnection(rendezvous, netAddress, newConnection): newConnection = newConnection.p() self.activeConnections.append(newConnection) self.cReader.addConnection(newConnection) print('A new client is connected', newConnection) return Task.cont def tskReaderPolling(self, taskdata): if self.cReader.dataAvailable(): datagram=NetDatagram() if self.cReader.getData(datagram): self.processData(datagram) return Task.cont
class Server(DirectObject): # TODO: Perhaps a better way to do this? handleNewConnection = None handleLostConnection = None def __init__(self, port, backlog=1000, compress=False): DirectObject.__init__(self) self.port = port self.compress = compress self.cManager = QueuedConnectionManager() self.cListener = QueuedConnectionListener(self.cManager, 0) self.cReader = QueuedConnectionReader(self.cManager, 0) self.cWriter = ConnectionWriter(self.cManager, 0) self.passedData = [] self.connect(port, backlog) self.startPolling() def connect(self, port, backlog): # Bind to our socket tcpSocket = self.cManager.openTCPServerRendezvous(port, backlog) self.cListener.addConnection(tcpSocket) def startPolling(self): self.addTask(self.tskListenerPolling, "serverListenTask", -40) self.addTask(self.tskDisconnectPolling, "serverDisconnectTask", -39) def tskListenerPolling(self, task): if self.cListener.newConnectionAvailable(): rendezvous = PointerToConnection() netAddress = NetAddress() newConnection = PointerToConnection() if self.cListener.getNewConnection(rendezvous, netAddress, newConnection): newConnection = newConnection.p() newConnection.setNoDelay(True) newConnection.setKeepAlive(True) if self.handleNewConnection: self.handleNewConnection(newConnection) self.cReader.addConnection( newConnection) # Begin reading connection return Task.cont def tskDisconnectPolling(self, task): while self.cManager.resetConnectionAvailable(): connPointer = PointerToConnection() self.cManager.getResetConnection(connPointer) connection = connPointer.p() # Remove the connection we just found to be "reset" or "disconnected" self.cReader.removeConnection(connection) if self.handleLostConnection: self.handleLostConnection(connection) return Task.cont def processData(self, netDatagram): myIterator = PyDatagramIterator(netDatagram) return self.decode(myIterator.getString()) def encode(self, data, compress=False): # encode(and possibly compress) the data with rencode return rencode.dumps(data, compress) def decode(self, data): # decode(and possibly decompress) the data with rencode return rencode.loads(data) def sendData(self, data, con): myPyDatagram = PyDatagram() myPyDatagram.addString(self.encode(data, self.compress)) self.cWriter.send(myPyDatagram, con) def passData(self, data, connection): self.passedData.append((data, connection)) def getData(self): data = self.passedData self.passedData = [] while self.cReader.dataAvailable(): datagram = NetDatagram() if self.cReader.getData(datagram): data.append( (datagram.getConnection(), self.processData(datagram))) return data
class SocketTCP(ShowBase): def __init__(self, _parent=None): ShowBase.__init__(self, windowType = 'none') print ("TCP Protocol Startup...") #self.parent = _parent #self.config = self.parent.server.config # tmp config self.tcpport = 6000 self.backlog = 10 self.hostname = '127.0.0.1' self.sendPacketQueue = None def startAll(self): self.setupTCP() self.startTCPTasks() def setupTCP(self): self.tcpManager = QueuedConnectionManager() self.tcpReader = QueuedConnectionReader(self.tcpManager, 0) self.tcpWriter = ConnectionWriter(self.tcpManager, 0) self.tcpListener = QueuedConnectionListener(self.tcpManager, 0) self.tcpSocket = self.tcpManager.openTCPServerRendezvous(self.tcpport, self.backlog) self.tcpListener.addConnection(self.tcpSocket) print ("Started Server on: ", self.hostname, self.tcpport) def startTCPTasks(self): taskMgr.add(self.tcpListenerTask, "tcpListenerTask", 0) print ("TCP Listener Started") taskMgr.add(self.tcpReaderTask, "tcpReaderTask", -10) print ("TCP Reader Started") taskMgr.add(self.tcpDisconnectionHandler, "tcpDisconnectionHandler", 20) print ("TCP Disconnection Handler Started") taskMgr.add(self.tcpQueuedSendHandlerTask, "tcpQueuedSendhandlerTask", -11) print ("TCP Queued Send Handler Started") # TCP Listener Task def tcpListenerTask(self, task): """ Accept new incoming connection from clients, related to TCP """ # Handle new connection if self.tcpListener.newConnectionAvailable(): rendezvous = PointerToConnection() netAddress = NetAddress() newConnection = PointerToConnection() if self.tcpListener.getNewConnection(rendezvous, netAddress, newConnection): newConnection = newConnection.p() # Tell the reader about the new TCP connection self.tcpReader.addConnection(newConnection) #self.core.createPlayerObject(generateUUID(),newConnection, netAddress) print ("Server: " + str(generateUUID), str(netAddress.getIpString())) else: print ("Server: Connection Failed from -", str(netAddress.getIpString())) return Task.cont def tcpReaderTask(self, task): """ Handle any data from clients by sending it to the Handlers. """ while 1: (datagram, data, opcode) = self.tcpNonBlockingRead(self.tcpReader) if opcode is MSG_NONE: # Do nothing or use it as some 'keep_alive' thing. break else: # Handle it #self.core.packetManager.handlePacket(opcode, data, datagram.getAddress()) print("Set Packet Handler to handle packets!") return Task.cont # TCP NonBlockingRead?? def tcpNonBlockingRead(self, qcr): """ Return a datagram collection and type if data is available on the queued connection udpReader """ if self.tcpReader.dataAvailable(): datagram = NetDatagram() if self.tcpReader.getData(datagram): data = DatagramIterator(datagram) opcode = data.getUint8() else: data = None opcode = MSG_NONE else: datagram = None data = None opcode = MSG_NONE # Return the datagram to keep a handle on the data return (datagram, data, opcode) # TCP Disconnection Handler def tcpDisconnectionHandler(self, task): # Check for resets if self.tcpManager.resetConnectionAvailable(): resetConnection = PointerToConnection() self.tcpManager.getResetConnection(resetConnection) print(str(resetConnection.p())) #for client in self.core.server.clients: # if self.core.server.clients[client].connection == resetConnection.p(): # del self.core.server.clients[client] # self.tcpReader.removeConnection(resetConnection.p()) # print ("Removed Connection:", resetConnection.p()) # print ('Current Clients:', self.core.server.clients) # break return Task.cont def sendPacket(self, _pkt, _connection): self.tcpWriter.send(_pkt, _connection) def sendBroadcast(self, _pkt, _skipif=None): for client in self.serverManager.clients: if _skipif == client: pass else: conn = self.serverManager.clients[client].connection self.tcpWriter.send(_pkt, conn) def addPacketToSendQueue(self, _data): # _data should contain packet and connection pass def addPacketToBroadcastQueue(self, _pkt, _connectionList): # _data should contain the pkt data and _connectionList should be the valid connections for that broadcast # Could have a grouping system that put clients together and broadcasts go per group pass def tcpQueuedSendHandlerTask(self, task): if not self.sendPacketQueue.isEmpty(): self.addPacketToSendQueue(self.sendPacketQueue.removeFromQue()) return Task.cont
class SocketHandler: logger = Logger("socket_handler") def __init__(self, port=None, host=None, ip_addr="127.0.0.1", backlog=10000, timeout=5000): self.port = port # our port self.host = host # host port self.ip_addr = ip_addr self.backlog = backlog self.timeout = timeout self.socket = None self.connection = None self.active_connections = [] self.handler = None self.cManager = QueuedConnectionManager() self.cListener = QueuedConnectionListener(self.cManager, 0) self.cReader = QueuedConnectionReader(self.cManager, 0) self.cWriter = ConnectionWriter(self.cManager, 0) def setup_socket(self): self.socket = self.cManager.openTCPServerRendezvous( self.ip_addr, self.port, self.backlog) if self.socket is None: raise Exception("unable to open new socket at %s:%d" % (self.ip_addr, self.port)) self.cListener.addConnection(self.socket) taskMgr.add(self.listen_suggestions, "poll the suggestion listener") taskMgr.add(self.read_data, "poll the connection reader") def connect_socket(self): if self.connection is None: self.connection = self.cManager.openTCPClientConnection( self.ip_addr, self.host, self.timeout) if self.connection is None: raise Exception("unable to connect to socket at %s:%d" % (self.ip_addr, self.host)) self.cReader.addConnection(self.connection) taskMgr.add(self.read_data, "poll the socket reader") def listen_suggestions(self, task): # to be overridden by inheritors pass def read_data(self, task): if self.cReader.dataAvailable(): dg = NetDatagram() if self.cReader.getData(dg): self.handle_data(dg) return task.cont def handle_data(self, dg): # to be overridden by inheritors pass
class Server(object): # https://www.panda3d.org/manual/index.php/Client-Server_Connection def __init__(self, host="localhost", port=5001): taskMgr = Task.TaskManager() self.cManager = QueuedConnectionManager() self.cListener = QueuedConnectionListener(self.cManager, 0) self.cReader = QueuedConnectionReader(self.cManager, 0) self.cWriter = ConnectionWriter(self.cManager, 0) self.activeConnections = [] # We'll want to keep track of these later self.readerCallbacks = [] backlog = 1000 #If we ignore 1,000 connection attempts, something is wrong! self.tcpSocket = self.cManager.openTCPServerRendezvous(port, backlog) self.cListener.addConnection(self.tcpSocket) taskMgr.add(self.tskListenerPolling, "Poll the connection listener", -39) taskMgr.add(self.tskReaderPolling, "Poll the connection reader", -40) print("started server! ({} at {})".format(port, host)) def Start(self): # derived servers can overwrite this function if needed pass def tskListenerPolling(self, taskdata): # listen for new connections # TODO(victor): what happens if a client shuts down? # print("server.tskListenerPolling()") if self.cListener.newConnectionAvailable(): rendezvous = PointerToConnection() netAddress = NetAddress() newConnection = PointerToConnection() if self.cListener.getNewConnection(rendezvous, netAddress, newConnection): newConnection = newConnection.p() self.activeConnections.append( newConnection) # Remember connection self.cReader.addConnection( newConnection) # Begin reading connection print("server: received new connection!") return Task.cont def tskReaderPolling(self, taskdata): # reader callback if not self.cReader.dataAvailable(): return Task.cont # catch the incoming data in this instance # Check the return value; if we were threaded, someone else could have # snagged this data before we did datagram = NetDatagram() if not self.cReader.getData(datagram): return Task.cont for callback in self.readerCallbacks: callback(datagram) return Task.cont def addReaderCallback(self, callbackFunction): self.readerCallbacks.append(callbackFunction) def BroadcastMessage(self, datagram): # send the same message to all clients for client in self.activeConnections: self.cWriter.send(datagram, client) def Close(self): # remove all clients for client in self.activeConnections: self.cReader.removeConnection(client) self.activeConnections = [] # close down our listener self.cManager.closeConnection(self.tcpSocket)