From 71b7471443718dd812b0e8faf84d3512e93958f2 Mon Sep 17 00:00:00 2001 From: thetestgame Date: Mon, 11 Nov 2024 00:11:58 -0600 Subject: [PATCH] Additional SS bindings and sorted by docs --- panda3d_astron/interfaces/state.py | 428 ++++++++++++++--------------- panda3d_astron/msgtypes.py | 1 - 2 files changed, 210 insertions(+), 219 deletions(-) diff --git a/panda3d_astron/interfaces/state.py b/panda3d_astron/interfaces/state.py index b138347..f5a025a 100644 --- a/panda3d_astron/interfaces/state.py +++ b/panda3d_astron/interfaces/state.py @@ -54,208 +54,61 @@ def handle_datagram(self, msg_type: int, di: object) -> None: else: self.notify.warning('Received unknown state server message: %d' % msg_type) - def handle_obj_location(self, di: object) -> None: - """ - Handles STATE_SERVER_OBJECT_CHANGING_LOCATION messages + def create_object_with_required(self, doId: int, parentId: int, zoneId: int, dclass: direct.DClass, fields: dict) -> None: """ - - doId = di.get_uint32() - parentId = di.get_uint32() - zoneId = di.get_uint32() - - do = self.air.doId2do.get(doId) - - if not do: - self.notify.warning('Received location for unknown doId=%d!' % (doId)) - return + Create an object on the State Server, specifying its initial location as (parent_id, zone_id), its class, + and initial field data. The object then broadcasts an ENTER_LOCATION message to its location channel, + and sends a CHANGING_ZONE with old location (0,0) to its parent (if it has one). - do.setLocation(parentId, zoneId) - - def handle_object_entry(self, di: object, other: bool) -> None: - """ - Handles STATE_SERVER_OBJECT_ENTER_AI_WITH_REQUIRED and - STATE_SERVER_OBJECT_ENTER_AI_WITH_REQUIRED_OTHER messages + Additionally, the object sends a GET_LOCATION to its children over the parent messages channel (1 << 32|parent_id) with context 1001 (STATESERVER_CONTEXT_WAKE_CHILDREN). """ - doId = di.get_uint32() - parentId = di.get_uint32() - zoneId = di.get_uint32() - classId = di.get_uint16() - - if classId not in self.air.dclassesByNumber: - self.notify.warning('Received entry for unknown dclass=%d! (Object %d)' % (classId, doId)) - return - - if doId in self.air.doId2do: - return # We already know about this object; ignore the entry. - - dclass = self.air.dclassesByNumber[classId] - - do = dclass.getClassDef()(self) - do.dclass = dclass - do.doId = doId - - # The DO came in off the server, so we do not unregister the channel when - # it dies: - do.doNotDeallocateChannel = True - self.addDOToTables(do, location=(parentId, zoneId)) - - # Now for generation: - do.generate() - if other: - do.updateAllRequiredOtherFields(dclass, di) - else: - do.updateAllRequiredFields(dclass, di) - - def handle_object_exit(self, di: object) -> None: - """ - Handles STATE_SERVER_OBJECT_CHANGING_AI and - STATE_SERVER_OBJECT_DELETE_RAM messages - """ - - doId = di.get_uint32() - if doId not in self.air.doId2do: - self.notify.warning('Received AI exit for unknown object %d' % (doId)) - return - - do = self.air.doId2do[doId] - self.removeDOFromTables(do) - do.delete() - do.sendDeleteEvent() - - def get_location(self, doId: int, callback: object) -> None: - """ - 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.air.get_context() - self.__callbacks[ctx] = callback - dg = PyDatagram() - dg.addServerHeader(doId, self.air.ourChannel, msgtypes.STATESERVER_OBJECT_GET_LOCATION) - dg.add_uint32(ctx) - - self.air.send(dg) - - def handle_get_location_resp(self, di: object) -> None: - """ - Handles STATESERVER_OBJECT_GET_LOCATION_RESP messages - """ - - ctx = di.get_uint32() - doId = di.get_uint32() - parentId = di.get_uint32() - zoneId = di.get_uint32() - - if ctx not in self.__callbacks: - self.notify.warning('Received unexpected STATESERVER_OBJECT_GET_LOCATION_RESP (ctx: %d)' % ctx) - return - - try: - self.__callbacks[ctx](doId, parentId, zoneId) - finally: - del self.__callbacks[ctx] + dg.addServerHeader(doId, self.air.ourChannel, msgtypes.STATESERVER_OBJECT_CREATE_WITH_REQUIRED) - def handle_get_activated_resp(self, di: object) -> None: - """ - Handles DBSS_OBJECT_GET_ACTIVATED_RESP messages - """ - - ctx = di.getUint32() - doId = di.getUint32() - activated = di.getUint8() - - if ctx not in self.__callbacks: - self.notify.warning('Received unexpected DBSS_OBJECT_GET_ACTIVATED_RESP (ctx: %d)' %ctx) - return - - try: - self.__callbacks[ctx](doId, activated) - finally: - del self.__callbacks[ctx] - - def get_activated(self, doId: int, callback: object) -> None: - """ - Ask the Database state server if a DistributedObject is activated. This will fire off - a callback with the result. - """ + dg.add_uint32(doId) + dg.add_uint32(parentId) + dg.add_uint32(zoneId) + dg.add_uint16(dclass.get_number()) - ctx = self.get_context() - self.__callbacks[ctx] = callback + packer = direct.DCPacker() + for field in dclass.getFields(): + if field.isRequired() and field.getName() in fields: + packer.beginPack(field) + field.packArgs(packer, fields[field.getName()]) + packer.endPack() - dg = PyDatagram() - dg.addServerHeader(doId, self.air.ourChannel, msgtypes.DBSS_OBJECT_GET_ACTIVATED) - dg.addUint32(ctx) - dg.addUint32(doId) + dg.appendData(packer.getDatagram()) self.air.send(dg) - def handle_get_object_resp(self, di: object) -> None: - """ - Handles STATESERVER_OBJECT_GET_ALL_RESP messages + def create_object_with_required_other(self, doId: int, parentId: int, zoneId: int, dclass: direct.DClass, fields: dict) -> None: """ + Create an object on the State Server, specifying its initial location as (parent_id, zone_id), its class, + and initial field data. The object then broadcasts an ENTER_LOCATION message to its location channel, + and sends a CHANGING_ZONE with old location (0,0) to its parent (if it has one). - ctx = di.get_uint32() - doId = di.get_uint32() - parentId = di.get_uint32() - zoneId = di.get_uint32() - classId = di.get_uint16() - - if ctx not in self.__callbacks: - self.notify.warning('Received unexpected STATESERVER_OBJECT_GET_ALL_RESP (ctx: %d)' % ctx) - return - - if classId not in self.air.dclassesByNumber: - self.notify.warning('Received STATESERVER_OBJECT_GET_ALL_RESP for unknown dclass=%d! (Object %d)' % (classId, doId)) - return - - dclass = self.air.dclassesByNumber[classId] - - fields = {} - unpacker = direct.DCPacker() - unpacker.setUnpackData(di.getRemainingBytes()) - - # Required: - for i in range(dclass.getNumInheritedFields()): - field = dclass.getInheritedField(i) - if not field.isRequired() or field.asMolecularField(): continue - unpacker.beginUnpack(field) - fields[field.getName()] = field.unpackArgs(unpacker) - unpacker.endUnpack() + Additionally, the object sends a GET_LOCATION to its children over the parent messages channel (1 << 32|parent_id) with context 1001 (STATESERVER_CONTEXT_WAKE_CHILDREN). + """ - # Other: - other = unpacker.rawUnpackUint16() - for i in range(other): - field = dclass.getFieldByIndex(unpacker.rawUnpackUint16()) - unpacker.beginUnpack(field) - fields[field.getName()] = field.unpackArgs(unpacker) - unpacker.endUnpack() - - try: - self.__callbacks[ctx](doId, parentId, zoneId, dclass, fields) - finally: - del self.__callbacks[ctx] + dg = PyDatagram() + dg.addServerHeader(doId, self.air.ourChannel, msgtypes.STATESERVER_OBJECT_CREATE_WITH_REQUIRED_OTHER) - def get_object(self, doId: int, callback: object) -> None: - """ - 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) - """ + dg.add_uint32(doId) + dg.add_uint32(parentId) + dg.add_uint32(zoneId) + dg.add_uint16(dclass.get_number()) - ctx = self.air.get_context() - self.__callbacks[ctx] = callback + packer = direct.DCPacker() + for field in dclass.getFields(): + if field.isRequired() and field.getName() in fields: + packer.beginPack(field) + field.packArgs(packer, fields[field.getName()]) + packer.endPack() - dg = PyDatagram() - dg.addServerHeader(doId, self.air.ourChannel, msgtypes.STATESERVER_OBJECT_GET_ALL) - dg.add_uint32(ctx) - dg.add_uint32(doId) + dg.appendData(packer.getDatagram()) - self.air.send(dg) + self.air.send(dg) def delete_ai_objects(self, channel: int) -> None: """ @@ -273,33 +126,6 @@ def delete_ai_objects(self, channel: int) -> None: deleteAIObjects = delete_ai_objects - def register_delete_ai_objects_post_remove(self, server_id :int) -> None: - """ - Registers the delete_ai_objects method to be called when the AI disconnects. - """ - - dg = PyDatagram() - dg.addServerHeader(server_id, self.air.ourChannel, msgtypes.STATESERVER_DELETE_AI_OBJECTS) - - dg.addChannel(self.air.ourChannel) - self.air.add_post_remove(dg) - - registerDeleteAIObjectsPostRemove = register_delete_ai_objects_post_remove - - def request_delete(self, do: object) -> None: - """ - 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, msgtypes.STATESERVER_OBJECT_DELETE_RAM) - dg.ad_uint32(do.doId) - self.air.send(dg) - - requestDelete = request_delete - def get_object_field(self, doId: int, field: str, callback: object) -> None: """ Get a single field from an object. @@ -390,12 +216,12 @@ def handle_get_fields_resp(self, di: object) -> None: finally: del self.__callbacks[ctx] - def get_object_all(self, doId: int, callback: object) -> None: + def get_object(self, doId: int, callback: object) -> None: """ - Get all fields from an object. + 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, fields) + Callback is called as: callback(doId, parentId, zoneId, dclass, fields) """ ctx = self.air.get_context() @@ -403,15 +229,12 @@ def get_object_all(self, doId: int, callback: object) -> None: dg = PyDatagram() dg.addServerHeader(doId, self.air.ourChannel, msgtypes.STATESERVER_OBJECT_GET_ALL) - dg.add_uint32(ctx) dg.add_uint32(doId) self.air.send(dg) - getObjectAll = get_object_all - - def handle_get_object_all_resp(self,di: object) -> None: + def handle_get_object_resp(self, di: object) -> None: """ Handles STATESERVER_OBJECT_GET_ALL_RESP messages """ @@ -554,6 +377,175 @@ def set_location(self, do: object, parentId: int, zoneId: int) -> None: setLocation = set_location sendSetLocation = set_location + def handle_obj_location(self, di: object) -> None: + """ + Handles STATE_SERVER_OBJECT_CHANGING_LOCATION messages + """ + + doId = di.get_uint32() + parentId = di.get_uint32() + zoneId = di.get_uint32() + + do = self.air.doId2do.get(doId) + + if not do: + self.notify.warning('Received location for unknown doId=%d!' % (doId)) + return + + do.setLocation(parentId, zoneId) + + def handle_object_entry(self, di: object, other: bool) -> None: + """ + Handles STATE_SERVER_OBJECT_ENTER_AI_WITH_REQUIRED and + STATE_SERVER_OBJECT_ENTER_AI_WITH_REQUIRED_OTHER messages + """ + + doId = di.get_uint32() + parentId = di.get_uint32() + zoneId = di.get_uint32() + classId = di.get_uint16() + + if classId not in self.air.dclassesByNumber: + self.notify.warning('Received entry for unknown dclass=%d! (Object %d)' % (classId, doId)) + return + + if doId in self.air.doId2do: + return # We already know about this object; ignore the entry. + + dclass = self.air.dclassesByNumber[classId] + + do = dclass.getClassDef()(self) + do.dclass = dclass + do.doId = doId + + # The DO came in off the server, so we do not unregister the channel when + # it dies: + do.doNotDeallocateChannel = True + self.addDOToTables(do, location=(parentId, zoneId)) + + # Now for generation: + do.generate() + if other: + do.updateAllRequiredOtherFields(dclass, di) + else: + do.updateAllRequiredFields(dclass, di) + + def handle_object_exit(self, di: object) -> None: + """ + Handles STATE_SERVER_OBJECT_CHANGING_AI and + STATE_SERVER_OBJECT_DELETE_RAM messages + """ + + doId = di.get_uint32() + if doId not in self.air.doId2do: + self.notify.warning('Received AI exit for unknown object %d' % (doId)) + return + + do = self.air.doId2do[doId] + self.removeDOFromTables(do) + do.delete() + do.sendDeleteEvent() + + def get_location(self, doId: int, callback: object) -> None: + """ + 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.air.get_context() + self.__callbacks[ctx] = callback + + dg = PyDatagram() + dg.addServerHeader(doId, self.air.ourChannel, msgtypes.STATESERVER_OBJECT_GET_LOCATION) + dg.add_uint32(ctx) + + self.air.send(dg) + + def handle_get_location_resp(self, di: object) -> None: + """ + Handles STATESERVER_OBJECT_GET_LOCATION_RESP messages + """ + + ctx = di.get_uint32() + doId = di.get_uint32() + parentId = di.get_uint32() + zoneId = di.get_uint32() + + if ctx not in self.__callbacks: + self.notify.warning('Received unexpected STATESERVER_OBJECT_GET_LOCATION_RESP (ctx: %d)' % ctx) + return + + try: + self.__callbacks[ctx](doId, parentId, zoneId) + finally: + del self.__callbacks[ctx] + + def handle_get_activated_resp(self, di: object) -> None: + """ + Handles DBSS_OBJECT_GET_ACTIVATED_RESP messages + """ + + ctx = di.getUint32() + doId = di.getUint32() + activated = di.getUint8() + + if ctx not in self.__callbacks: + self.notify.warning('Received unexpected DBSS_OBJECT_GET_ACTIVATED_RESP (ctx: %d)' %ctx) + return + + try: + self.__callbacks[ctx](doId, activated) + finally: + del self.__callbacks[ctx] + + def get_activated(self, doId: int, callback: object) -> None: + """ + Ask the Database state server if a DistributedObject is activated. This will fire off + a callback with the result. + """ + + ctx = self.get_context() + self.__callbacks[ctx] = callback + + dg = PyDatagram() + dg.addServerHeader(doId, self.air.ourChannel, msgtypes.DBSS_OBJECT_GET_ACTIVATED) + dg.addUint32(ctx) + dg.addUint32(doId) + + self.air.send(dg) + + + def register_delete_ai_objects_post_remove(self, server_id :int) -> None: + """ + Registers the delete_ai_objects method to be called when the AI disconnects. + """ + + dg = PyDatagram() + dg.addServerHeader(server_id, self.air.ourChannel, msgtypes.STATESERVER_DELETE_AI_OBJECTS) + + dg.addChannel(self.air.ourChannel) + self.air.add_post_remove(dg) + + registerDeleteAIObjectsPostRemove = register_delete_ai_objects_post_remove + + def request_delete(self, do: object) -> None: + """ + 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, msgtypes.STATESERVER_OBJECT_DELETE_RAM) + dg.ad_uint32(do.doId) + self.air.send(dg) + + requestDelete = request_delete + + + def set_owner(self, doId: int, newOwner: int) -> None: """ Sets the owner of a DistributedObject. This will enable the new owner to send "ownsend" fields, diff --git a/panda3d_astron/msgtypes.py b/panda3d_astron/msgtypes.py index 1f268e6..55f1784 100644 --- a/panda3d_astron/msgtypes.py +++ b/panda3d_astron/msgtypes.py @@ -154,7 +154,6 @@ "CLIENT_OBJECT_LOCATION": 140, # Message Director control messages - "CONTROL_CHANNEL": 1, "CONTROL_ADD_CHANNEL": 9000, "CONTROL_REMOVE_CHANNEL": 9001, "CONTROL_ADD_RANGE": 9002,