def writeServerEvent(self, logtype, *args, **kwargs): """ Write an event to the central Event Logger, if one is configured. The purpose of the Event Logger is to keep a game-wide record of all interesting in-game events that take place. Therefore, this function should be used whenever such an interesting in-game event occurs. """ if self.eventSocket is None: return # No event logger configured! log = collections.OrderedDict() log['type'] = logtype log['sender'] = self.eventLogId for i,v in enumerate(args): # +1 because the logtype was _0, so we start at _1 log['_%d' % (i+1)] = v log.update(kwargs) dg = PyDatagram() msgpack_encode(dg, log) self.eventSocket.Send(dg.getMessage())
def sendSetLocation(self, doId, parentId, zoneId): datagram = PyDatagram() datagram.addUint16(CLIENT_OBJECT_LOCATION) datagram.addUint32(doId) datagram.addUint32(parentId) datagram.addUint32(zoneId) self.send(datagram)
def writeServerEvent(self, logtype, *args, **kwargs): """ Write an event to the central Event Logger, if one is configured. The purpose of the Event Logger is to keep a game-wide record of all interesting in-game events that take place. Therefore, this function should be used whenever such an interesting in-game event occurs. """ if self.eventSocket is None: return # No event logger configured! log = collections.OrderedDict() log['type'] = logtype log['sender'] = self.eventLogId for i, v in enumerate(args): # +1 because the logtype was _0, so we start at _1 log['_%d' % (i + 1)] = v log.update(kwargs) dg = PyDatagram() msgpack_encode(dg, log) self.eventSocket.Send(dg.getMessage())
def sendDisconnect(self): if self.isConnected(): datagram = PyDatagram() datagram.addUint16(CLIENT_DISCONNECT_CMU) self.send(datagram) self.notify.info('Sent disconnect message to server') self.disconnect() self.stopHeartbeat()
def sendDeleteMsg(self, doId): # This method is only used in conjunction with the CMU LAN # server. datagram = PyDatagram() datagram.addUint16(CLIENT_OBJECT_DELETE) datagram.addUint32(doId) self.send(datagram)
def sendSetShardMsg(self, shardId): datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_SET_SHARD) # Add shard id datagram.addUint32(shardId) # send the message self.send(datagram)
def setAI(self, doId, aiChannel): """ Sets the AI of the specified DistributedObjectAI to be the specified channel. Generally, you should not call this method, and instead call DistributedObjectAI.setAI. """ dg = PyDatagram() dg.addServerHeader(doId, aiChannel, STATESERVER_OBJECT_SET_AI) dg.add_uint64(aiChannel) self.send(dg)
def setClientState(self, clientChannel, state): """ Sets the state of the client on the CA. Useful for logging in and logging out, and for little else. """ dg = PyDatagram() dg.addServerHeader(clientChannel, self.ourChannel, CLIENTAGENT_SET_STATE) dg.add_uint16(state) self.send(dg)
def setOwner(self, doId, newOwner): """ Sets the owner of a DistributedObject. This will enable the new owner to send "ownsend" fields, and will generate an OwnerView. """ dg = PyDatagram() dg.addServerHeader(doId, self.ourChannel, STATESERVER_OBJECT_SET_OWNER) dg.add_uint64(newOwner) self.send(dg)
def sendHeartbeat(self): datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_HEARTBEAT) # Send it! self.send(datagram) self.lastHeartbeat = globalClock.getRealTime() # This is important enough to consider flushing immediately # (particularly if we haven't run readerPollTask recently). self.considerFlush()
def sendDisconnect(self): if self.isConnected(): # Tell the game server that we're going: datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_DISCONNECT) # Send the message self.send(datagram) self.notify.info("Sent disconnect message to server") self.disconnect() self.stopHeartbeat()
def requestDelete(self, do): """ Request the deletion of an object that already exists on the State Server. You should probably use do.requestDelete() instead. """ dg = PyDatagram() dg.addServerHeader(do.doId, self.ourChannel, STATESERVER_OBJECT_DELETE_RAM) dg.addUint32(do.doId) self.send(dg)
def clientAddSessionObject(self, clientChannel, doId): """ Declares the specified DistributedObject to be a "session object", meaning that it is destroyed when the client disconnects. Generally used for avatars owned by the client. """ dg = PyDatagram() dg.addServerHeader(clientChannel, self.ourChannel, CLIENTAGENT_ADD_SESSION_OBJECT) dg.add_uint32(doId) self.send(dg)
def clearPostRemove(self): """ Clear all datagrams registered with addPostRemove. This is useful if the Panda3D process is performing a clean exit. It may clear the "emergency clean-up" post-remove messages and perform a normal exit-time clean-up instead, depending on the specific design of the game. """ dg = PyDatagram() dg.addServerControlHeader(CONTROL_CLEAR_POST_REMOVE) self.send(dg)
def requestDelete(self, do): """ Request the deletion of an object that already exists on the State Server. You should use do.requestDelete() instead. This is not meant to be called directly unless you really know what you are doing. """ dg = PyDatagram() dg.addServerHeader(do.doId, self.ourChannel, STATESERVER_OBJECT_DELETE_RAM) dg.addUint32(do.doId) self.send(dg)
def _sendRemoveInterest(self, contextId): """ contextId is a client-side created number that refers to a set of interests. The same contextId number doesn't necessarily have any relationship to the same contextId on another client. """ assert self.notify.debugCall() datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_REMOVE_INTEREST) datagram.addUint16(contextId) self.send(datagram)
def _sendRemoveInterest(self, handle, contextId): datagram = PyDatagram() datagram.addUint16(CLIENT_REMOVE_INTEREST) datagram.addUint16(handle) if contextId != 0: datagram.addUint32(contextId) self.send(datagram)
def setInterestZones(self, interestZoneIds): """ Changes the set of zones that this particular client is interested in hearing about. """ datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_SET_INTEREST_CMU) for zoneId in interestZoneIds: datagram.addUint32(zoneId) # send the message self.send(datagram) self.interestZones = interestZoneIds[:]
def sendSetLocation(self, do, parentId, zoneId): dg = PyDatagram() dg.addServerHeader(do.doId, self.ourChannel, STATESERVER_OBJECT_SET_LOCATION) dg.addUint32(parentId) dg.addUint32(zoneId) self.send(dg)
def unregisterForChannel(self, channel): """ Unregister a channel subscription on the Message Director. The Message Director will cease to relay messages to this AIR sent on the channel. """ if channel not in self._registeredChannels: return self._registeredChannels.remove(channel) dg = PyDatagram() dg.addServerControlHeader(CONTROL_REMOVE_CHANNEL) dg.addChannel(channel) self.send(dg)
def addPostRemove(self, dg): """ Register a datagram with the Message Director that gets sent out if the connection is ever lost. This is useful for registering cleanup messages: If the Panda3D process ever crashes unexpectedly, the Message Director will detect the socket close and automatically process any post-remove datagrams. """ dg2 = PyDatagram() dg2.addServerControlHeader(CONTROL_ADD_POST_REMOVE) dg2.addString(dg.getMessage()) self.send(dg2)
def clientAddInterest(self, clientChannel, interestId, parentId, zoneId): """ Opens an interest on the behalf of the client. This, used in conjunction with add_interest: visible (or preferably, disabled altogether), will mitigate possible security risks. """ dg = PyDatagram() dg.addServerHeader(clientChannel, self.ourChannel, CLIENTAGENT_ADD_INTEREST) dg.add_uint16(interestId) dg.add_uint32(parentId) dg.add_uint32(zoneId) self.send(dg)
def registerForChannel(self, channel): """ Register for messages on a specific Message Director channel. If the channel is already open by this AIR, nothing will happen. """ if channel in self._registeredChannels: return self._registeredChannels.add(channel) dg = PyDatagram() dg.addServerControlHeader(CONTROL_ADD_CHANNEL) dg.addChannel(channel) self.send(dg)
def _sendAddInterest(self, contextId, parentId, zoneId): """ Part of the new otp-server code. contextId is a client-side created number that refers to a set of interests. The same contextId number doesn't necessarily have any relationship to the same contextId on another client. """ datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_ADD_INTEREST) datagram.addUint16(contextId) datagram.addUint32(parentId) datagram.addUint32(zoneId) self.send(datagram)
def getNetworkAddress(self, clientId, callback): """ Get the endpoints of a client connection. You should already be sure the client actually exists, otherwise the callback will never be called. Callback is called as: callback(remoteIp, remotePort, localIp, localPort) """ ctx = self.getContext() self.__callbacks[ctx] = callback dg = PyDatagram() dg.addServerHeader(clientId, self.ourChannel, CLIENTAGENT_GET_NETWORK_ADDRESS) dg.addUint32(ctx) self.send(dg)
def getLocation(self, doId, callback): """ Ask a DistributedObject where it is. You should already be sure the object actually exists, otherwise the callback will never be called. Callback is called as: callback(doId, parentId, zoneId) """ ctx = self.getContext() self.__callbacks[ctx] = callback dg = PyDatagram() dg.addServerHeader(doId, self.ourChannel, STATESERVER_OBJECT_GET_LOCATION) dg.addUint32(ctx) self.send(dg)
def getActivated(self, doId, callback): ctx = self.getContext() self.__callbacks[ctx] = callback dg = PyDatagram() dg.addServerHeader(doId, self.ourChannel, DBSS_OBJECT_GET_ACTIVATED) dg.addUint32(ctx) dg.addUint32(doId) self.send(dg)
def __getBarrierData(self): # Returns the barrier data formatted as a blob for sending to # the clients. This lists all of the current outstanding # barriers and the avIds waiting for them. dg = PyDatagram() for context, barrier in self.__barriers.items(): toons = barrier.pendingToons if toons: dg.addUint16(context) dg.addUint16(len(toons)) for avId in toons: dg.addUint32(avId) return dg.getMessage()
def __connected(self): self.notify.info('Connected successfully.') # Listen to our channel... self.registerForChannel(self.ourChannel) # If we're configured with a State Server, register a post-remove to # clean up whatever objects we own on this server should we unexpectedly # fall over and die. if self.serverId: dg = PyDatagram() dg.addServerHeader(self.serverId, self.ourChannel, STATESERVER_DELETE_AI_OBJECTS) dg.addChannel(self.ourChannel) self.addPostRemove(dg) messenger.send('airConnected') self.handleConnected()
def eject(self, clientChannel, reasonCode, reason): """ Kicks the client residing at the specified clientChannel, using the specifed reasoning. """ dg = PyDatagram() dg.addServerHeader(clientChannel, self.ourChannel, CLIENTAGENT_EJECT) dg.add_uint16(reasonCode) dg.addString(reason) self.send(dg)
def setAllowClientSend(self, avId, dObj, fieldNameList=[]): """ Overrides the security of a field(s) specified, allows an owner of a DistributedObject to send the field(s) regardless if its marked ownsend/clsend. """ dg = PyDatagram() dg.addServerHeader(dObj.GetPuppetConnectionChannel(avId), self.ourChannel, CLIENTAGENT_SET_FIELDS_SENDABLE) fieldIds = [] for fieldName in fieldNameList: field = dObj.dclass.getFieldByName(fieldName) if field: fieldIds.append(field.getNumber()) dg.addUint32(dObj.getDoId()) dg.addUint16(len(fieldIds)) for fieldId in fieldIds: dg.addUint16(fieldId) self.send(dg)
def writeServerEvent(self, logtype, *args): """ Write an event to the central Event Logger, if one is configured. The purpose of the Event Logger is to keep a game-wide record of all interesting in-game events that take place. Therefore, this function should be used whenever such an interesting in-game event occurs. """ if self.eventSocket is None: return # No event logger configured! dg = PyDatagram() dg.addString(self.eventLogId) dg.addString(logtype) for arg in args: dg.addString(str(arg)) self.eventSocket.Send(dg.getMessage())
def addPostRemove(self, dg): """ Register a datagram with the Message Director that gets sent out if the connection is ever lost. This is useful for registering cleanup messages: If the Panda3D process ever crashes unexpectedly, the Message Director will detect the socket close and automatically process any post-remove datagrams. """ dg2 = PyDatagram() dg2.addServerControlHeader(CONTROL_ADD_POST_REMOVE) dg2.addUint64(self.ourChannel) dg2.addString(dg.getMessage()) self.send(dg2)
def setInterestZones(self, interestZoneIds): datagram = PyDatagram() datagram.addUint16(CLIENT_SET_INTEREST_CMU) for zoneId in interestZoneIds: datagram.addUint32(zoneId) self.send(datagram) self.interestZones = interestZoneIds[:]
def sendUpdateToChannel(self, distObj, channelId, fieldName, args): datagram = distObj.dclass.clientFormatUpdate(fieldName, distObj.doId, args) dgi = PyDatagramIterator(datagram) dgi.getUint16() dg = PyDatagram() dg.addUint16(CLIENT_OBJECT_UPDATE_FIELD_TARGETED_CMU) dg.addUint32(channelId & 4294967295L) dg.appendData(dgi.getRemainingBytes()) self.send(dg)
def sendSetZoneMsg(self, zoneId, visibleZoneList=None): datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_SET_ZONE) # Add zone id datagram.addUint32(zoneId) # if we have an explicit list of visible zones, add them if visibleZoneList is not None: vzl = list(visibleZoneList) vzl.sort() assert PythonUtil.uniqueElements(vzl) for zone in vzl: datagram.addUint32(zone) # send the message self.send(datagram)
def getObject(self, doId, callback): """ Get the entire state of an object. You should already be sure the object actually exists, otherwise the callback will never be called. Callback is called as: callback(doId, parentId, zoneId, dclass, fields) """ ctx = self.getContext() self.__callbacks[ctx] = callback dg = PyDatagram() dg.addServerHeader(doId, self.ourChannel, STATESERVER_OBJECT_GET_ALL) dg.addUint32(ctx) dg.addUint32(doId) self.send(dg)
def _sendRemoveAIInterest(self, handle): """ handle is a bare int, NOT an InterestHandle. Use this to close an AI opened interest. """ datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_REMOVE_INTEREST) datagram.addUint16((1<<15) + handle) self.send(datagram)
def sendRemoveZoneMsg(self, zoneId, visibleZoneList=None): # This method is only used in conjunction with the CMU LAN # server. datagram = PyDatagram() datagram.addUint16(CLIENT_REMOVE_ZONE) datagram.addUint32(zoneId) # if we have an explicit list of visible zones, add them if visibleZoneList is not None: vzl = list(visibleZoneList) vzl.sort() assert PythonUtil.uniqueElements(vzl) for zone in vzl: datagram.addUint32(zone) # send the message self.send(datagram)
def sendWorldPos(self, x, y): # The server will need to know the world # offset of our current render node path # and adjust the x, y accordingly. At one # point I considered adding the world offset # here, but that would just use extra bits. onScreenDebug.add("worldPos", "%-4d, %-4d" % (x, y)) return #*# datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_SET_WORLD_POS) # Add x datagram.addInt16(x) # Add y datagram.addSint16(y) # send the message self.send(datagram)