def handle_query_zone(self, dgi, sender): # STATESERVER_QUERY_ZONE_OBJECT_ALL_DONE handle = dgi.get_uint16() context_id = dgi.get_uint32() parent_id = dgi.get_uint32() if parent_id != self.do_id: return num_zones = dgi.remaining() // 4 zones = [] for i in range(num_zones): zones.append(dgi.get_uint32()) object_ids = [] for zone in zones: if zone not in self.zone_objects: continue object_ids.extend(self.zone_objects[zone]) resp = Datagram() resp.add_server_header([sender], self.do_id, STATESERVER_QUERY_ZONE_OBJECT_ALL_DONE) resp.add_uint16(handle) resp.add_uint32(context_id) if not len(object_ids): self.service.send_datagram(resp) return self.send_location_entry(sender) for do_id in object_ids: self.service.objects[do_id].send_location_entry(sender) self.service.send_datagram(resp)
def append_other_data(self, dg, client_only, also_owner): if client_only: fields_dg = Datagram() count = 0 for field_name, raw_data in self.ram.items(): field = self.dclass.fields_by_name[field_name] if field.is_broadcast or field.is_clrecv or (also_owner and field.is_ownrecv): fields_dg.add_uint16(field.number) fields_dg.add_bytes(raw_data) count += 1 dg.add_uint16(count) if count: dg.add_bytes(fields_dg.bytes()) else: dg.add_uint16(len(self.ram)) for field_name, raw_data in self.ram.items(): field = self.dclass.fields_by_name[field_name] dg.add_uint16(field.number) dg.add_bytes(raw_data)
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)
def send_datagram(self, data: Datagram): loop = self.service.loop loop.call_soon_threadsafe(self.outgoing_q.put_nowait, data.bytes())
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)
def send_datagram(self, data: Datagram): self.outgoing_q.put_nowait(data.bytes())
def fromBytes(data): return Experience.fromNetString(Datagram(data).iterator())
def handle_datagram(self, dg, dgi): pos = dgi.tell() sender = dgi.get_channel() if sender == self.channel: return msgtype = dgi.get_uint16() self.check_futures(dgi, msgtype, sender) if msgtype == STATESERVER_OBJECT_ENTERZONE_WITH_REQUIRED_OTHER: self.handle_object_entrance(dgi, sender) elif msgtype == STATESERVER_OBJECT_ENTER_OWNER_RECV: self.handle_owned_object_entrance(dgi, sender) elif msgtype == STATESERVER_OBJECT_CHANGE_ZONE: do_id = dgi.get_uint32() if self.queue_pending(do_id, dgi, pos): self.service.log.debug( f'Queued location change for pending object {do_id}.') return self.handle_location_change(dgi, sender, do_id) elif msgtype == STATESERVER_QUERY_ZONE_OBJECT_ALL_DONE: self.handle_interest_done(dgi) elif msgtype == STATESERVER_OBJECT_UPDATE_FIELD: do_id = dgi.get_uint32() if not self.object_exists(do_id): queued = self.queue_pending(do_id, dgi, pos) if queued: self.service.log.debug( f'Queued field update for pending object {do_id}.') else: self.service.log.debug( f'Got update for unknown object {do_id}.') return self.handle_update_field(dgi, sender, do_id) elif msgtype == STATESERVER_OBJECT_DELETE_RAM: do_id = dgi.get_uint32() if do_id == self.avatar_id: if sender == self.account.disl_id << 32: self.disconnect(ClientDisconnect.RELOGGED, 'redundant login') else: self.disconnect(ClientDisconnect.SHARD_DISCONNECT, 'district reset') elif not self.object_exists(do_id): self.service.log.debug( f'Queued deletion for pending object {do_id}.') self.queue_pending(do_id, dgi, pos) return else: self.send_remove_object(do_id) del self.visible_objects[do_id] elif msgtype == CLIENT_AGENT_SET_INTEREST: self.receive_add_interest(dgi, ai=True) elif msgtype == CLIENT_AGENT_REMOVE_INTEREST: self.receive_remove_interest(dgi, ai=True) elif msgtype in { CLIENT_FRIEND_ONLINE, CLIENT_FRIEND_OFFLINE, CLIENT_GET_FRIEND_LIST_RESP }: dg = Datagram() dg.add_uint16(msgtype) dg.add_bytes(dgi.remaining_bytes()) self.send_datagram(dg) else: self.service.log.debug( f'Client {self.channel} received unhandled upstream msg {msgtype}.' )
def send_go_get_lost(self, booted_index, booted_text): resp = Datagram() resp.add_uint16(CLIENT_GO_GET_LOST) resp.add_uint16(booted_index) resp.add_string16(booted_text.encode('utf-8')) self.send_datagram(resp)
def send_object_entrance(self, parent_id, zone_id, dc_id, do_id, dgi, has_other): resp = Datagram() resp.add_uint16(CLIENT_CREATE_OBJECT_REQUIRED_OTHER if has_other else CLIENT_CREATE_OBJECT_REQUIRED) resp.add_uint32(parent_id) resp.add_uint32(zone_id) resp.add_uint16(dc_id) resp.add_uint32(do_id) resp.add_bytes(dgi.remaining_bytes()) self.send_datagram(resp)
def send_remove_object(self, do_id): self.service.log.debug(f'Sending removal of {do_id}.') resp = Datagram() resp.add_uint16(CLIENT_OBJECT_DISABLE) resp.add_uint32(do_id) self.send_datagram(resp)
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)
async def create_toon(self, sender, context, dclass, disl_id, pos, fields): try: do_id = await self.backend.create_object(dclass, fields) account = await self.backend.query_object_fields(disl_id, ['ACCOUNT_AV_SET'], 'Account') temp = Datagram() temp.add_bytes(account['ACCOUNT_AV_SET']) av_set = self.dc.namespace['Account']['ACCOUNT_AV_SET'].unpack_value(temp.iterator()) print(do_id, disl_id, pos, av_set) av_set[pos] = do_id temp.seek(0) self.dc.namespace['Account']['ACCOUNT_AV_SET'].pack_value(temp, av_set) await self.backend.set_field(disl_id, 'ACCOUNT_AV_SET', temp.bytes(), 'Account') except OTPCreateFailed as e: print('creation failed', e) do_id = 0 dg = Datagram() dg.add_server_header([sender], DBSERVERS_CHANNEL, DBSERVER_CREATE_STORED_OBJECT_RESP) dg.add_uint32(context) dg.add_uint8(do_id == 0) dg.add_uint32(do_id) self.send_datagram(dg)
def test_add_data(self): s = 'hello_world'.encode('utf-8') dg = Datagram() dg.add_string16(s) other = struct.pack(f'<H{len(s)}b', len(s), *s) self.assertEqual(dg.bytes(), other) s = 'abcdefghijklmnop'.encode('utf-8') dg = Datagram() dg.add_string32(s) other = struct.pack(f'<I{len(s)}b', len(s), *s) self.assertEqual(dg.bytes(), other) dg.add_bytes(b'') self.assertEqual(dg.bytes(), other) dg = Datagram() dg.add_string16(b'') self.assertEqual(dg.bytes(), b'\x00\x00') dg = Datagram() random.seed('pydc') s = bytes(random.randint(0, 255) for _ in range((1 << 16))) with self.assertRaises(OverflowError): dg.add_string16(s) dg = Datagram() s = bytes(random.randint(0, 255) for _ in range((1 << 16))) dg.add_string32(s) s = b''.join((struct.pack('<I', len(s)), s)) self.assertEqual(dg.bytes(), s) dg = Datagram() dg.add_string32(b'') self.assertEqual(dg.bytes(), struct.pack('<I', 0)) dg = Datagram() c = chr(0x1F600).encode('utf-8') dg.add_bytes(c) self.assertEqual(dg.bytes(), c)
def test_add_int8(self): dg = Datagram() dg.add_int8(-127) dg.add_int8(127) other = struct.pack('<bb', -127, 127) self.assertEqual(dg.bytes(), other) with self.assertRaises(OverflowError): dg.add_int8(-128) dg.add_int8(128)
def receive_login(self, dgi): play_token = dgi.get_string16() server_version = dgi.get_string16() hash_val = dgi.get_uint32() want_magic_words = dgi.get_string16() self.service.log.debug( f'play_token:{play_token}, server_version:{server_version}, hash_val:{hash_val}, ' f'want_magic_words:{want_magic_words}') try: play_token = bytes.fromhex(play_token) nonce, tag, play_token = play_token[:16], play_token[ 16:32], play_token[32:] cipher = AES.new(CLIENTAGENT_SECRET, AES.MODE_EAX, nonce) data = cipher.decrypt_and_verify(play_token, tag) self.service.log.debug(f'Login token data:{data}') data = json.loads(data) for key in list(data.keys()): value = data[key] if type(value) == str: data[key] = value.encode('utf-8') self.account = DISLAccount(**data) except ValueError as e: self.disconnect(ClientDisconnect.LOGIN_ERROR, 'Invalid token') return self.channel = getClientSenderChannel(self.account.disl_id, 0) self.subscribe_channel(self.channel) self.subscribe_channel(getAccountChannel(self.account.disl_id)) resp = Datagram() resp.add_uint16(CLIENT_LOGIN_TOONTOWN_RESP) return_code = 0 # -13 == period expired resp.add_uint8(return_code) error_string = b'' # 'Bad DC Version Compare' resp.add_string16(error_string) resp.add_uint32(self.account.disl_id) resp.add_string16(self.account.username) account_name_approved = True resp.add_uint8(account_name_approved) resp.add_string16(self.account.whitelist_chat_enabled) resp.add_string16(self.account.create_friends_with_chat) resp.add_string16(self.account.chat_code_creation_rule) t = time.time() * 10e6 usecs = int(t % 10e6) secs = int(t / 10e6) resp.add_uint32(secs) resp.add_uint32(usecs) resp.add_string16(self.account.access) resp.add_string16(self.account.whitelist_chat_enabled) last_logged_in = time.strftime('%c') # time.strftime('%c') resp.add_string16(last_logged_in.encode('utf-8')) account_days = 0 resp.add_int32(account_days) resp.add_string16(self.account.account_type) resp.add_string16(self.account.username) self.send_datagram(resp)
def receive_add_interest(self, dgi, ai=False): handle = dgi.get_uint16() context_id = dgi.get_uint32() parent_id = dgi.get_uint32() num_zones = dgi.remaining() // 4 zones = [] for i in range(num_zones): zones.append(dgi.get_uint32()) self.service.log.debug( f'Client {self.channel} is requesting interest with handle {handle} and context {context_id} ' f'for location {parent_id} {zones}') if self.state <= ClientState.AUTHENTICATED and parent_id != 4618: self.service.log.debug( f'Client {self.channel} requested unexpected interest in state {self.state}. Ignoring.' ) return previous_interest = None for _interest in self.interests: if _interest.handle == handle: previous_interest = _interest break if previous_interest is None: interest = Interest(self.channel, handle, context_id, parent_id, zones) self.interests.append(interest) else: self.service.log.debug( f'Altering interest {handle} (done: {previous_interest.done}): {previous_interest.zones} -> {zones}' ) self.interests.remove(previous_interest) if previous_interest.parent_id != parent_id: killed_zones = previous_interest.zones else: killed_zones = set(previous_interest.zones).difference( set(zones)) for _interest in self.interests: killed_zones = killed_zones.difference(set(_interest.zones)) if not killed_zones: break self.service.log.debug( f'Zones killed by altering interest: {killed_zones}') if killed_zones: for do_id in list(self.visible_objects.keys()): obj = self.visible_objects[do_id] if obj.parent_id == parent_id and obj.zone_id in killed_zones: self.service.log.debug( f'Object {obj.do_id}, location ({obj.parent_id}, {obj.zone_id}), killed by altered interest: {zones}' ) self.send_remove_object(obj.do_id) del self.visible_objects[do_id] for zone in killed_zones: self.unsubscribe_channel( location_as_channel(previous_interest.parent_id, zone)) interest = Interest(self.channel, handle, context_id, parent_id, zones) self.interests.append(interest) for do_id in list(self.pending_objects.keys()): if not self.pending_object_needed(do_id): del self.pending_objects[do_id] interest.ai = ai if not zones: interest.done = True if not ai: resp = Datagram() resp.add_uint16(CLIENT_DONE_INTEREST_RESP) resp.add_uint16(handle) resp.add_uint32(context_id) self.send_datagram(resp) return query_request = Datagram() query_request.add_server_header([parent_id], self.channel, STATESERVER_QUERY_ZONE_OBJECT_ALL) query_request.add_uint16(handle) query_request.add_uint32(context_id) query_request.add_uint32(parent_id) for zone in zones: query_request.add_uint32(zone) self.subscribe_channel(location_as_channel(parent_id, zone)) self.service.send_datagram(query_request)
def delete_avatar_ram(self): dg = Datagram() dg.add_server_header([self.avatar_id], self.channel, STATESERVER_OBJECT_DELETE_RAM) dg.add_uint32(self.avatar_id) self.service.send_datagram(dg)
def fromBytes(data): return Inventory.fromNetString(Datagram(data).iterator())
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) if field.name == 'setTalk': # TODO: filtering resp = Datagram() resp.add_uint16(CLIENT_OBJECT_UPDATE_FIELD) resp.add_uint32(do_id) resp.add_uint16(field_number) resp.add_bytes(dgi.remaining_bytes()) self.send_datagram(resp)
def makeNetString(self): dg = Datagram() dg.add_bytes(self.type.encode('ascii')) if self.type == 't': dg.add_uint8(toonHeadTypes.index(self.head)) dg.add_uint8(toonTorsoTypes.index(self.torso)) dg.add_uint8(toonLegTypes.index(self.legs)) if self.gender == 'm': dg.add_uint8(1) else: dg.add_uint8(0) dg.add_uint8(self.topTex) dg.add_uint8(self.topTexColor) dg.add_uint8(self.sleeveTex) dg.add_uint8(self.sleeveTexColor) dg.add_uint8(self.botTex) dg.add_uint8(self.botTexColor) dg.add_uint8(self.armColor) dg.add_uint8(self.gloveColor) dg.add_uint8(self.legColor) dg.add_uint8(self.headColor) else: raise Exception(f'unknown avatar type: {self.type}') return dg.bytes()
def receive_set_avatar(self, dgi): av_id = dgi.get_uint32() self.service.log.debug( f'client {self.channel} is setting their avatar to {av_id}') if not av_id: if self.avatar_id: # Client is logging out of their avatar. self.delete_avatar_ram() self.owned_objects.clear() self.visible_objects.clear() self.unsubscribe_channel( getClientSenderChannel(self.account.disl_id, self.avatar_id)) self.unsubscribe_channel(getPuppetChannel(self.avatar_id)) self.channel = getClientSenderChannel(self.account.disl_id, 0) self.subscribe_channel(self.channel) self.state = ClientState.AUTHENTICATED self.avatar_id = 0 return else: # Do nothing. return elif self.state == ClientState.PLAY_GAME: self.service.log.debug( f'Client {self.channel} tried to set their avatar {av_id} while avatar is already set to {self.avatar_id}.' ) return pot_av = None for pa in self.potential_avatars: if pa and pa.do_id == av_id: pot_av = pa break if pot_av is None: self.disconnect(ClientDisconnect.INTERNAL_ERROR, 'Could not find avatar on account.') return self.avatar_id = av_id self.created_av_id = 0 self.state = ClientState.SETTING_AVATAR self.channel = getClientSenderChannel(self.account.disl_id, self.avatar_id) self.subscribe_channel(self.channel) self.subscribe_channel(getPuppetChannel(self.avatar_id)) dclass = self.service.dc_file.namespace['DistributedToon'] access = 2 if self.account.access == b'FULL' else 1 # These Fields are REQUIRED but not stored in db. other_fields = [(dclass['setAccess'], (access, )), (dclass['setPreviousAccess'], (access, )), (dclass['setAsGM'], (False, )), (dclass['setBattleId'], (0, ))] if pot_av.approved_name: other_fields.append((dclass['setName'], (pot_av.approved_name, ))) pot_av.approved_name = '' dg = Datagram() dg.add_server_header( [STATESERVERS_CHANNEL], self.channel, STATESERVER_OBJECT_CREATE_WITH_REQUIR_OTHER_CONTEXT) dg.add_uint32(av_id) dg.add_uint32(0) dg.add_uint32(0) dg.add_channel(self.channel) dg.add_uint16(dclass.number) dg.add_uint16(len(other_fields)) for f, arg in other_fields: dg.add_uint16(f.number) f.pack_value(dg, arg) self.service.send_datagram(dg)
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)
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()))
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)
def receive_set_wishname(self, dgi): av_id = dgi.get_uint32() name = dgi.get_string16() av = self.get_potential_avatar(av_id) self.service.log.debug( f'Received wishname request from {self.channel} for avatar {av_id} for name "{name}".' ) pending = name.encode('utf-8') approved = b'' rejected = b'' failed = False resp = Datagram() resp.add_uint16(CLIENT_SET_WISHNAME_RESP) resp.add_uint32(av_id) resp.add_uint16(failed) resp.add_string16(pending) resp.add_string16(approved) resp.add_string16(rejected) self.send_datagram(resp) if av_id and av: dclass = self.service.dc_file.namespace['DistributedToon'] wishname_field = dclass['WishName'] wishname_state_field = dclass['WishNameState'] resp = Datagram() resp.add_server_header([DBSERVERS_CHANNEL], self.channel, DBSERVER_SET_STORED_VALUES) resp.add_uint32(av_id) resp.add_uint16(2) resp.add_uint16(wishname_state_field.number) wishname_state_field.pack_value(resp, ('PENDING', )) resp.add_uint16(wishname_field.number) wishname_field.pack_value(resp, (name, )) self.service.send_datagram(resp)
def requestDelete(self, do): dg = Datagram() dg.add_server_header([do.do_id], self.ourChannel, STATESERVER_OBJECT_DELETE_RAM) dg.add_uint32(do.do_id) self.send(dg)
def receive_set_name_pattern(self, dgi): av_id = dgi.get_uint32() self.service.log.debug(f'Got name pattern request for av_id {av_id}.') title_index, title_flag = dgi.get_int16(), dgi.get_int16() first_index, first_flag = dgi.get_int16(), dgi.get_int16() last_prefix_index, last_prefix_flag = dgi.get_int16(), dgi.get_int16() last_suffix_index, last_suffix_flag = dgi.get_int16(), dgi.get_int16() resp = Datagram() resp.add_uint16(CLIENT_SET_NAME_PATTERN_ANSWER) resp.add_uint32(av_id) if av_id != self.created_av_id: resp.add_uint8(1) self.send_datagram(resp) return if first_index <= 0 and last_prefix_index <= 0 and last_suffix_index <= 0: self.service.log.debug( f'Received request for empty name for {av_id}.') resp.add_uint8(2) self.send_datagram(resp) return if (last_prefix_index <= 0 <= last_suffix_index) or ( last_suffix_index <= 0 <= last_prefix_index): self.service.log.debug( f'Received request for invalid last name for {av_id}.') resp.add_uint8(3) self.send_datagram(resp) return try: title = self.get_name_part(title_index, title_flag, { NamePart.BOY_TITLE, NamePart.GIRL_TITLE, NamePart.NEUTRAL_TITLE }) first = self.get_name_part(first_index, first_flag, { NamePart.BOY_FIRST, NamePart.GIRL_FIRST, NamePart.NEUTRAL_FIRST }) last_prefix = self.get_name_part( last_prefix_index, last_prefix_flag, {NamePart.CAP_PREFIX, NamePart.LAST_PREFIX}) last_suffix = self.get_name_part(last_suffix_index, last_suffix_flag, {NamePart.LAST_SUFFIX}) except KeyError as e: resp.add_uint8(4) self.send_datagram(resp) self.service.log.debug( f'Received invalid index for name part. {e.args}') return name = f'{title}{" " if title else ""}{first}{" " if first else ""}{last_prefix}{last_suffix}' for pot_av in self.potential_avatars: if pot_av and pot_av.do_id == av_id: pot_av.approved_name = name break resp.add_uint8(0) self.send_datagram(resp)
def test_legacy_arrays(self): dc = parse_dc(TEST1_DC) field = dc.fields[0]() # type: AtomicField field2 = dc.fields[1]() # type: AtomicField field3 = dc.fields[2]() # type: AtomicField arg1 = [2, 4, 8, 16, 32] dg = Datagram() field.pack_value(dg, [arg1]) self.assertEqual(field.unpack_value(dg.iterator())[0], [2, 4, 8, 16, 32]) dg2 = Datagram() field2.pack_value(dg2, [arg1]) self.assertEqual(field2.unpack_value(dg2.iterator())[0], [2, 4, 8, 16, 32]) self.assertEqual(dg.get_message().tobytes(), dg2.get_message().tobytes()) arg1 = [[1, 2], [3, 4], [5, 6]] dg3 = Datagram() field3.pack_value(dg3, [arg1]) self.assertEqual(dg3.get_message().tobytes(), b'\x0f\x00\x01\x00\x00\x00\x02\x03\x00\x00\x00\x04\x05\x00\x00\x00\x06') self.assertEqual(field3.unpack_value(dg3.iterator())[0], [[1, 2], [3, 4], [5, 6]])
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)