示例#1
0
    def generate_hash(self, hash_gen):
        hash_gen.add_string(self.identifier)

        self.dtype.generate_hash(hash_gen)

        hash_gen.add_int(len(self.cases))
        for case in self.cases:
            try:
                dg = Datagram()
                pack_functions[DCTypes[self.dtype]](dg, case.value)
            except KeyError:
                dg = Datagram()
                self.dtype.pack_value(dg, case.value)

            hash_gen.add_bytes(dg.bytes())

            hash_gen.add_int(len(case.parameters) + 1)
            self.dtype.generate_hash(hash_gen)

            for parameter in case.parameters:
                parameter.generate_hash(hash_gen)

        if self.default_case is not None:
            hash_gen.add_int(len(self.default_case.parameters) + 1)
            self.dtype.generate_hash(hash_gen)

            for parameter in self.default_case.parameters:
                parameter.generate_hash(hash_gen)
示例#2
0
    def test_initialization(self):
        dg = Datagram(b'\x01\x02\x03')
        self.assertEqual(dg.bytes(), b'\x01\x02\x03')

        random.seed('test_initialization')
        data = os.urandom(32)
        dg = Datagram(data)
        self.assertEqual(dg.bytes(), data)
示例#3
0
    def createObjects(self):
        self.registerForChannel(self.ourChannel)

        from .Objects import ToontownDistrictAI, ToontownDistrictStatsAI, DistributedInGameNewsMgrAI, NewsManagerAI, FriendManagerAI
        from .TimeManagerAI import TimeManagerAI

        self.district = ToontownDistrictAI(self)
        self.district.name = 'Nutty River'
        self.generateWithRequired(self.district, OTP_DO_ID_TOONTOWN,
                                  OTP_ZONE_ID_DISTRICTS)

        post_remove = Datagram()
        post_remove.add_server_control_header(CONTROL_ADD_POST_REMOVE)
        post_remove.add_server_header([
            STATESERVERS_CHANNEL,
        ], self.ourChannel, STATESERVER_SHARD_REST)
        post_remove.add_channel(self.ourChannel)
        self.send(post_remove)

        dg = Datagram()
        dg.add_server_header([STATESERVERS_CHANNEL], self.ourChannel,
                             STATESERVER_ADD_AI_RECV)
        dg.add_uint32(self.district.do_id)
        dg.add_channel(self.ourChannel)
        self.send(dg)

        stats = ToontownDistrictStatsAI(self)
        stats.settoontownDistrictId(self.district.do_id)
        self.generateWithRequired(stats, OTP_DO_ID_TOONTOWN,
                                  OTP_ZONE_ID_DISTRICTS_STATS)

        dg = Datagram()
        dg.add_server_header([STATESERVERS_CHANNEL], self.ourChannel,
                             STATESERVER_ADD_AI_RECV)
        dg.add_uint32(stats.do_id)
        dg.add_channel(self.ourChannel)
        self.send(dg)

        self.timeManager = TimeManagerAI(self)
        self.timeManager.generateWithRequired(OTP_ZONE_ID_MANAGEMENT)

        self.ingameNewsMgr = DistributedInGameNewsMgrAI(self)
        self.ingameNewsMgr.generateWithRequired(OTP_ZONE_ID_MANAGEMENT)

        self.newsManager = NewsManagerAI(self)
        self.newsManager.generateWithRequired(OTP_ZONE_ID_MANAGEMENT)

        self.friendManager = FriendManagerAI(self)
        self.friendManager.generateGlobalObject(OTP_ZONE_ID_MANAGEMENT)

        self.loadZones()

        self.district.b_setAvailable(True)
示例#4
0
    def test_add_datagram(self):
        dg1 = Datagram()
        dg1.add_uint16(32)

        dg2 = Datagram()
        dg2.add_string16(b'hello')

        dg1.add_datagram(dg2)

        self.assertEqual(dg1.bytes(), struct.pack('<HH5B', 32, 5, *b'hello'))

        del dg2
        self.assertEqual(dg1.bytes(), struct.pack('<HH5B', 32, 5, *b'hello'))
示例#5
0
    def receive_delete_avatar(self, dgi):
        av_id = dgi.get_uint32()

        av = self.get_potential_avatar(av_id)

        if not av:
            return

        self.potential_avatars[av.index] = None
        avatars = [
            pot_av.do_id if pot_av else 0 for pot_av in self.potential_avatars
        ]
        self.avs_deleted.append((av_id, int(time.time())))

        field = self.service.dc_file.namespace['Account']['ACCOUNT_AV_SET']
        del_field = self.service.dc_file.namespace['Account'][
            'ACCOUNT_AV_SET_DEL']

        dg = Datagram()
        dg.add_server_header([DBSERVERS_CHANNEL], self.channel,
                             DBSERVER_SET_STORED_VALUES)
        dg.add_uint32(self.account.disl_id)
        dg.add_uint16(2)
        dg.add_uint16(field.number)
        field.pack_value(dg, avatars)
        dg.add_uint16(del_field.number)
        del_field.pack_value(dg, self.avs_deleted)
        self.service.send_datagram(dg)

        resp = Datagram()
        resp.add_uint16(CLIENT_DELETE_AVATAR_RESP)
        resp.add_uint8(0)  # Return code

        av_count = sum(
            (1 if pot_av else 0 for pot_av in self.potential_avatars))
        dg.add_uint16(av_count)

        for pot_av in self.potential_avatars:
            if not pot_av:
                continue
            dg.add_uint32(pot_av.do_id)
            dg.add_string16(pot_av.name.encode('utf-8'))
            dg.add_string16(pot_av.wish_name.encode('utf-8'))
            dg.add_string16(pot_av.approved_name.encode('utf-8'))
            dg.add_string16(pot_av.rejected_name.encode('utf-8'))
            dg.add_string16(pot_av.dna_string.encode('utf-8'))
            dg.add_uint8(pot_av.index)

        self.send_datagram(resp)
示例#6
0
    def ai_format_generate(self, obj, do_id, parent_id, zone_id, district_channel_id, from_channel_id, optional_fields):
        dg = Datagram()
        dg.add_uint8(1)
        dg.add_channel(district_channel_id)
        dg.add_channel(from_channel_id)
        if optional_fields:
            dg.add_uint16(STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER)
        else:
            dg.add_uint16(STATESERVER_OBJECT_GENERATE_WITH_REQUIRED)

        #if parent_id:
        dg.add_uint32(parent_id)

        dg.add_uint32(zone_id)
        dg.add_uint16(self.number)
        dg.add_uint32(do_id)

        for field in self.inherited_fields:
            if not isinstance(field, MolecularField) and field.is_required:
                self.pack_field(dg, obj, field)

        if optional_fields:
            dg.add_uint16(len(optional_fields))

            for field_name in optional_fields:
                field = self.fields_by_name[field_name]
                dg.add_uint16(field.number)
                self.pack_field(dg, obj, field)

        return dg
示例#7
0
 def unsubscribe_channel(self, channel):
     dg = Datagram()
     dg.add_uint8(1)
     dg.add_channel(CONTROL_MESSAGE)
     dg.add_uint16(CONTROL_REMOVE_CHANNEL)
     dg.add_channel(channel)
     self.send_datagram(dg)
示例#8
0
    async def created_avatar(self):
        f = DatagramFuture(self.service.loop,
                           DBSERVER_CREATE_STORED_OBJECT_RESP)
        self.futures.append(f)
        sender, dgi = await f
        context = dgi.get_uint32()
        return_code = dgi.get_uint8()
        av_id = dgi.get_uint32()

        av = self.potential_avatar
        av.do_id = av_id
        self.potential_avatars[av.index] = av
        self.potential_avatar = None

        resp = Datagram()
        resp.add_uint16(CLIENT_CREATE_AVATAR_RESP)
        resp.add_uint16(0)  # Context
        resp.add_uint8(return_code)  # Return Code
        resp.add_uint32(av_id)  # av_id
        self.send_datagram(resp)

        self.created_av_id = av_id

        self.service.log.debug(
            f'New avatar {av_id} created for client {self.channel}.')
示例#9
0
 def send_object_location(self, do_id, new_parent, new_zone):
     resp = Datagram()
     resp.add_uint16(CLIENT_OBJECT_LOCATION)
     resp.add_uint32(do_id)
     resp.add_uint32(new_parent)
     resp.add_uint32(new_zone)
     self.send_datagram(resp)
示例#10
0
    def test_remaining(self):
        dg = Datagram()
        dg.add_uint16(25)
        dg.add_int64(-354843598374)

        dgi = dg.iterator()
        self.assertEqual(dgi.remaining(), 10)

        s = 'remaining unit test'
        dg.add_string32(s.encode('utf-8'))
        self.assertEqual(dgi.remaining(), 10 + 4 + len(s))

        self.assertEqual(dgi.get_uint16(), 25)
        self.assertEqual(dgi.remaining(), 8 + 4 + len(s))

        dgi.seek(dg.get_length())
        self.assertEqual(dgi.remaining(), 0)

        dgi.seek(0)
        self.assertEqual(dgi.remaining(), 10 + 4 + len(s))

        dgi.seek(14)
        self.assertEqual(dgi.remaining(), len(s))

        dgi.seek(999)
        self.assertEqual(dgi.remaining(), 0)
示例#11
0
    async def get_stored_values(self, sender, context, do_id, fields):
        try:
            field_dict = await self.backend.query_object_fields(do_id, [field.name for field in fields])
        except OTPQueryNotFound:
            field_dict = None

        self.log.debug(f'Received query request from {sender} with context {context} for do_id: {do_id}.')

        dg = Datagram()
        dg.add_server_header([sender], DBSERVERS_CHANNEL, DBSERVER_GET_STORED_VALUES_RESP)
        dg.add_uint32(context)
        dg.add_uint32(do_id)
        pos = dg.tell()
        dg.add_uint16(0)

        if field_dict is None:
            print('object not found... %s' % do_id, sender, context)
            self.send_datagram(dg)
            return

        counter = 0
        for field in fields:
            if field.name not in field_dict:
                continue
            if field_dict[field.name] is None:
                continue
            dg.add_uint16(field.number)
            dg.add_bytes(field_dict[field.name])
            counter += 1

        dg.seek(pos)
        dg.add_uint16(counter)
        self.send_datagram(dg)
示例#12
0
    async def handle_datagrams(self):
        # TODO: run this tight loop in a seperate process, maybe proccess pool for CA and MD
        expected = 0

        while True:
            if expected:
                if len(self.buf) < expected:
                    await asyncio.sleep(0.01)
                    continue
                else:
                    try:
                        dg = Datagram()
                        dg.add_bytes(self.buf[:expected])
                        self.receive_datagram(dg)
                        del self.buf[:expected]
                        expected = 0
                        continue
                    except Exception as e:
                        import traceback
                        traceback.print_exc()
                        continue
            elif len(self.buf) > 2:
                expected = struct.unpack('H', self.buf[:2])[0]
                del self.buf[:2]
                continue
            else:
                await asyncio.sleep(0.01)
示例#13
0
    def receive_datagram(self, dg):
        dgi = dg.iterator()

        recipient_count = dgi.get_uint8()
        if recipient_count == 1 and dgi.get_channel() == CONTROL_MESSAGE:
            # Control message.
            msg_type = dgi.get_uint16()

            if msg_type == CONTROL_SET_CHANNEL:
                channel = dgi.get_channel()
                self.subscribe_channel(channel)
            elif msg_type == CONTROL_REMOVE_CHANNEL:
                channel = dgi.get_channel()
                self.unsubscribe_channel(channel)
            elif msg_type == CONTROL_ADD_RANGE:
                low = dgi.get_channel()
                high = dgi.get_channel()
                for channel in range(low, high, 1):
                    self.channels.add(channel)
            elif msg_type == CONTROL_REMOVE_RANGE:
                low = dgi.get_channel()
                high = dgi.get_channel()
                for channel in range(low, high, 1):
                    if channel in self.channels:
                        self.channels.remove(channel)
            elif msg_type == CONTROL_ADD_POST_REMOVE:
                post_dg = Datagram()
                post_dg.add_bytes(dgi.get_bytes(dgi.remaining()))
                self.service.log.debug(f'Received post remove:{post_dg.bytes()}')
                self.post_removes.append(post_dg)
            elif msg_type == CONTROL_CLEAR_POST_REMOVE:
                del self.post_removes[:]
        else:
            self.service.q.put_nowait((None, dg))
示例#14
0
    def receive_update_field(self, dgi, do_id=None):
        if do_id is None:
            do_id = dgi.get_uint32()

        field_number = dgi.get_uint16()

        field = self.service.dc_file.fields[field_number]()

        sendable = False

        if field.is_ownsend and do_id in self.owned_objects:
            sendable = True
        elif field.is_clsend:
            sendable = True

        if not sendable:
            self.disconnect(ClientDisconnect.INTERNAL_ERROR,
                            'Tried to send nonsendable field to object.')
            self.service.log.warn(
                f'Client {self.channel} tried to update {do_id} with nonsendable field {field.name}. '
                f'DCField keywords: {field.keywords}')
            return

        pos = dgi.tell()
        field.unpack_bytes(dgi)
        dgi.seek(pos)

        resp = Datagram()
        resp.add_server_header([do_id], self.channel,
                               STATESERVER_OBJECT_UPDATE_FIELD)
        resp.add_uint32(do_id)
        resp.add_uint16(field_number)
        resp.add_bytes(dgi.remaining_bytes())
        self.service.send_datagram(resp)
示例#15
0
 def removeInterest(self, client_channel, handle, context):
     dg = Datagram()
     dg.add_server_header([client_channel], self.ourChannel,
                          CLIENT_AGENT_REMOVE_INTEREST)
     dg.add_uint16(handle)
     dg.add_uint32(context)
     self.send(dg)
示例#16
0
    def handle_one_update(self, dgi, sender):
        field_id = dgi.get_uint16()
        field = self.dclass.dcfile().fields[field_id]()
        pos = dgi.tell()
        data = field.unpack_bytes(dgi)

        if isinstance(field, MolecularField):
            dgi.seek(pos)
            self.save_molecular(field, dgi)
        else:
            self.save_field(field, data)

        targets = []

        if field.is_broadcast:
            targets.append(location_as_channel(self.parent_id, self.zone_id))
        if field.is_airecv and self.ai_channel and self.ai_channel != sender:
            targets.append(self.ai_channel)
        if field.is_ownrecv and self.owner_channel and self.owner_channel != sender:
            targets.append(self.owner_channel)

        if targets:
            dg = Datagram()
            dg.add_server_header(targets, sender, STATESERVER_OBJECT_UPDATE_FIELD)
            dg.add_uint32(self.do_id)
            dg.add_uint16(field_id)
            dg.add_bytes(data)
            self.service.send_datagram(dg)
示例#17
0
    async def query_account(self, sender, do_id):
        dclass = self.dc.namespace['Account']
        toon_dclass = self.dc.namespace['DistributedToon']
        field_dict = await self.backend.query_object_all(do_id, dclass.name)

        temp = Datagram()
        temp.add_bytes(field_dict['ACCOUNT_AV_SET'])
        av_ids = dclass['ACCOUNT_AV_SET'].unpack_value(temp.iterator())

        dg = Datagram()
        dg.add_server_header([sender], DBSERVERS_CHANNEL, DBSERVER_ACCOUNT_QUERY_RESP)
        dg.add_bytes(field_dict['ACCOUNT_AV_SET_DEL'])
        av_count = sum((1 if av_id else 0 for av_id in av_ids))
        self.log.debug(f'Account query for {do_id} from {sender}: {field_dict}')
        dg.add_uint16(av_count)  # Av count
        for av_id in av_ids:
            if not av_id:
                continue
            toon_fields = await self.backend.query_object_fields(av_id, ['setName', 'WishNameState', 'WishName', 'setDNAString'], 'DistributedToon')

            wish_name = toon_fields['WishName']

            temp = Datagram()
            temp.add_bytes(toon_fields['WishNameState'])
            name_state = toon_dclass['WishNameState'].unpack_value(temp.iterator())

            dg.add_uint32(av_id)
            dg.add_bytes(toon_fields['setName'])

            pending_name = b'\x00\x00'
            approved_name = b'\x00\x00'
            rejected_name = b'\x00\x00'

            if name_state == 'APPROVED':
                approved_name = wish_name
            elif name_state == 'REJECTED':
                rejected_name = wish_name
            else:
                pending_name = wish_name

            dg.add_bytes(pending_name)
            dg.add_bytes(approved_name)
            dg.add_bytes(rejected_name)
            dg.add_bytes(toon_fields['setDNAString'])
            dg.add_uint8(av_ids.index(av_id))

        self.send_datagram(dg)
示例#18
0
    def test_get_remaining(self):
        dg = Datagram()
        dg.add_string16(b'test string')
        dg.add_uint32(2525)

        dgi = dg.iterator()
        self.assertEqual(dgi.remaining(), 2 + len('test string') + 4)
        self.assertEqual(dgi.get_remaining(), dg.get_message().tobytes())
示例#19
0
 def send_location_entry(self, location):
     dg = Datagram()
     dg.add_server_header([location], self.do_id, STATESERVER_OBJECT_ENTERZONE_WITH_REQUIRED_OTHER)
     dg.add_uint8(bool(self.ram))
     self.append_required_data(dg, True, False)
     if self.ram:
         self.append_other_data(dg, True, False)
     self.service.send_datagram(dg)
示例#20
0
 def sendFriendOnline(self, avId, otherAvId):
     # Need this delay so that `setFriendsList` is set first to avoid
     # the online whisper message.
     dg = Datagram()
     dg.add_server_header([getPuppetChannel(avId)], self.air.ourChannel,
                          CLIENT_FRIEND_ONLINE)
     dg.add_uint32(otherAvId)
     self.air.send(dg)
示例#21
0
 def queue_pending(self, do_id, dgi, pos):
     if do_id in self.pending_objects:
         dgi.seek(pos)
         dg = Datagram()
         dg.add_bytes(dgi.remaining_bytes())
         self.pending_objects[do_id].datagrams.append(dg)
         return True
     return False
示例#22
0
    def receive_remove_interest(self, dgi, ai=False):
        handle = dgi.get_uint16()

        if dgi.remaining():
            context = dgi.get_uint32()
        else:
            context = None

        interest = None

        for _interest in self.interests:
            if _interest.handle == handle:
                interest = _interest
                break

        if not interest:
            self.service.log.debug(
                f'Got unexpected interest removal from client {self.channel} for interest handle '
                f'{handle} with context {context}')
            return

        self.service.log.debug(
            f'Got remove interest request from client {self.channel} for interest handle '
            f'{handle} with context {context}')

        parent_id = interest.parent_id

        uninterested_zones = []

        for zone in interest.zones:
            if len(self.lookup_interest(parent_id, zone)) == 1:
                uninterested_zones.append(zone)

        to_remove = []

        for do_id in self.visible_objects:
            do = self.visible_objects[do_id]
            if do.parent_id == parent_id and do.zone_id in uninterested_zones:
                self.service.log.debug(
                    f'Object {do_id} killed by interest remove.')
                self.send_remove_object(do_id)

                to_remove.append(do_id)

        for do_id in to_remove:
            del self.visible_objects[do_id]

        for zone in uninterested_zones:
            self.unsubscribe_channel(location_as_channel(parent_id, zone))

        self.interests.remove(interest)

        if not ai:
            resp = Datagram()
            resp.add_uint16(CLIENT_DONE_INTEREST_RESP)
            resp.add_uint16(handle)
            resp.add_uint32(context)
            self.send_datagram(resp)
示例#23
0
    def handle_location_change(self, new_parent, new_zone, sender):
        old_parent = self.parent_id
        old_zone = self.zone_id

        targets = list()

        if self.ai_channel is not None:
            targets.append(self.ai_channel)

        if self.owner_channel is not None:
            targets.append(self.owner_channel)

        if new_parent == self.do_id:
            raise Exception('Object cannot be parented to itself.\n')

        if new_parent != old_parent:
            if old_parent:
                self.unsubscribe_channel(parent_to_children(old_parent))
                targets.append(old_parent)
                targets.append(location_as_channel(old_parent, old_zone))

            self.parent_id = new_parent
            self.zone_id = new_zone

            if new_parent:
                self.subscribe_channel(parent_to_children(new_parent))

                if not self.ai_explicitly_set:
                    new_ai_channel = self.service.resolve_ai_channel(new_parent)
                    if new_ai_channel != self.ai_channel:
                        self.ai_channel = new_ai_channel
                        self.send_ai_entry(new_ai_channel)

                targets.append(new_parent)

        elif new_zone != old_zone:
            self.zone_id = new_zone

            targets.append(self.parent_id)
            targets.append(location_as_channel(self.parent_id, old_zone))
        else:
            # Not changing zones.
            return

        dg = Datagram()
        dg.add_server_header(targets, sender, STATESERVER_OBJECT_CHANGE_ZONE)
        dg.add_uint32(self.do_id)
        dg.add_uint32(new_parent)
        dg.add_uint32(new_zone)
        dg.add_uint32(old_parent)
        dg.add_uint32(old_zone)

        self.service.send_datagram(dg)

        self.parent_synced = False

        if new_parent:
            self.send_location_entry(location_as_channel(new_parent, new_zone))
示例#24
0
    def unregisterForChannel(self, channel):
        if channel not in self._registedChannels:
            return
        self._registedChannels.remove(channel)

        dg = Datagram()
        dg.add_server_control_header(CONTROL_REMOVE_CHANNEL)
        dg.add_channel(channel)
        self.send(dg)
示例#25
0
    def registerForChannel(self, channel):
        if channel in self._registedChannels:
            return
        self._registedChannels.add(channel)

        dg = Datagram()
        dg.add_server_control_header(CONTROL_SET_CHANNEL)
        dg.add_channel(channel)
        self.send(dg)
示例#26
0
    def send_ai_entry(self, location):
        dg = Datagram()
        dg.add_server_header([location], self.do_id, STATESERVER_OBJECT_ENTER_AI_RECV)
        self.append_required_data(dg, False, False)

        if self.ram:
            self.append_other_data(dg, False, False)

        self.service.send_datagram(dg)
示例#27
0
    def test_add_uint32(self):
        dg = Datagram()
        dg.add_uint32(1 << 31)

        other = struct.pack('<I', 1 << 31)
        self.assertEqual(dg.bytes(), other)

        with self.assertRaises(OverflowError):
            dg.add_uint32(1 << 32)
示例#28
0
    def receive_create_avatar(self, dgi):
        _ = dgi.get_uint16()
        dna = dgi.get_blob16()
        pos = dgi.get_uint8()
        self.service.log.debug(
            f'Client {self.channel} requesting avatar creation with dna {dna} and pos {pos}.'
        )

        if not 0 <= pos < 6 or self.potential_avatars[pos] is not None:
            self.service.log.debug(
                f'Client {self.channel} tried creating avatar in invalid position.'
            )
            return

        self.potential_avatar = PotentialAvatar(do_id=0,
                                                name='Toon',
                                                wish_name='',
                                                approved_name='',
                                                rejected_name='',
                                                dna_string=dna,
                                                index=pos)

        dclass = self.service.dc_file.namespace['DistributedToon']

        dg = Datagram()
        dg.add_server_header([DBSERVERS_CHANNEL], self.channel,
                             DBSERVER_CREATE_STORED_OBJECT)
        dg.add_uint32(0)
        dg.add_uint16(dclass.number)
        dg.add_uint32(self.account.disl_id)
        dg.add_uint8(pos)
        pos = dg.tell()
        dg.add_uint16(0)

        default_toon = dict(DEFAULT_TOON)
        default_toon['setDNAString'] = (dna, )
        default_toon['setDISLid'] = (self.account.disl_id, )
        default_toon['WishName'] = ('', )
        default_toon['WishNameState'] = ('CLOSED', )

        count = 0
        for field in dclass.inherited_fields:
            if not isinstance(field, MolecularField) and field.is_db:
                if field.name == 'DcObjectType':
                    continue
                dg.add_uint16(field.number)
                field.pack_value(dg, default_toon[field.name])
                count += 1

        dg.seek(pos)
        dg.add_uint16(count)

        self.state = ClientState.CREATING_AVATAR

        self.service.send_datagram(dg)

        self.tasks.append(self.service.loop.create_task(self.created_avatar()))
示例#29
0
 def setInterest(self, client_channel, handle, context, parent_id, zones):
     dg = Datagram()
     dg.add_server_header([client_channel], self.ourChannel,
                          CLIENT_AGENT_SET_INTEREST)
     dg.add_uint16(handle)
     dg.add_uint32(context)
     dg.add_uint32(parent_id)
     for zone in zones:
         dg.add_uint32(zone)
     self.send(dg)
示例#30
0
 def sendLocation(self, do_id, old_parent: int, old_zone: int,
                  new_parent: int, new_zone: int):
     dg = Datagram()
     dg.add_server_header([do_id], self.ourChannel,
                          STATESERVER_OBJECT_SET_ZONE)
     dg.add_uint32(new_parent)
     dg.add_uint32(new_zone)
     dg.add_uint32(old_parent)
     dg.add_uint32(old_zone)
     self.send(dg)