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)
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
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}.')
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)
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 ai_format_update(self, do_id, to_id, from_id, args): dg = Datagram() dg.add_uint8(1) dg.add_channel(to_id) dg.add_channel(from_id) dg.add_uint16(STATESERVER_OBJECT_UPDATE_FIELD) dg.add_uint32(do_id) dg.add_uint16(self.number) self.pack_value(dg, args) return dg
async def create_object(self, sender, context, dclass, fields): try: do_id = await self.backend.create_object(dclass, fields) 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 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)
def handle_owned_object_entrance(self, dgi, sender): do_id = dgi.get_uint32() parent_id = dgi.get_uint32() zone_id = dgi.get_uint32() dc_id = dgi.get_uint16() self.owned_objects[do_id] = ObjectInfo(do_id, dc_id, parent_id, zone_id) resp = Datagram() resp.add_uint16(CLIENT_GET_AVATAR_DETAILS_RESP) resp.add_uint32(self.avatar_id) resp.add_uint8(0) # Return code resp.add_bytes(dgi.remaining_bytes()) 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)
def database_generate_context(self, obj, context_id, parent_id, zone_id, owner_channel, database_server_id, from_channel_id): dg = Datagram() dg.add_uint8(1) dg.add_channel(database_server_id) dg.add_channel(from_channel_id) dg.add_uint16(STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT) dg.add_uint32(parent_id) dg.add_uint32(zone_id) dg.add_channel(owner_channel) dg.add_uint16(self.number) dg.add_uint32(context_id) for field in self.inherited_fields: if not isinstance(field, MolecularField) and field.is_required: self.pack_required_field(dg, obj, field) return dg
def receive_get_friend_list(self, dgi): self.service.log.debug( f'Friend list query received from {self.channel}') error = 0 count = 0 # Friend Structure # uint32 do_id # string name # string dna_string # uint32 pet_id resp = Datagram() resp.add_uint16(CLIENT_GET_FRIEND_LIST_RESP) resp.add_uint8(error) resp.add_uint16(count) self.send_datagram(resp)
async def do_login(self): f = DatagramFuture(self.service.loop, DBSERVER_ACCOUNT_QUERY_RESP) self.futures.append(f) sender, dgi = await f av_del_field = self.service.dc_file.namespace['Account'][ 'ACCOUNT_AV_SET_DEL'] self.service.log.debug('Begin unpack of deleted avatars.') try: self.avs_deleted = av_del_field.unpack_value(dgi) except Exception: import traceback traceback.print_exc() return self.service.log.debug( f'Avatars deleted list for {self.account.username}: {self.avs_deleted}' ) pos = dgi.tell() avatar_info = [None] * 6 for i in range(dgi.get_uint16()): pot_av = PotentialAvatar(do_id=dgi.get_uint32(), name=dgi.get_string16(), wish_name=dgi.get_string16(), approved_name=dgi.get_string16(), rejected_name=dgi.get_string16(), dna_string=dgi.get_blob16(), index=dgi.get_uint8()) avatar_info[pot_av.index] = pot_av self.potential_avatars = avatar_info self.state = ClientState.AVATAR_CHOOSER resp = Datagram() resp.add_uint16(CLIENT_GET_AVATARS_RESP) dgi.seek(pos) resp.add_uint8(0) # Return code resp.add_bytes(dgi.remaining_bytes()) self.send_datagram(resp)
def handle_datagram(self, dg, dgi): sender = dgi.get_channel() msgtype = dgi.get_uint16() self.service.log.debug(f'State server directly received msgtype {MSG_TO_NAME_DICT[msgtype]} from {sender}.') if msgtype == STATESERVER_OBJECT_GENERATE_WITH_REQUIRED: self.handle_generate(dgi, sender, False) elif msgtype == STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER: self.handle_generate(dgi, sender, True) elif msgtype == STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT: # DBSS msg self.handle_db_generate(dgi, sender, False) elif msgtype == STATESERVER_OBJECT_CREATE_WITH_REQUIR_OTHER_CONTEXT: # DBSS msg self.handle_db_generate(dgi, sender, True) elif msgtype == STATESERVER_ADD_AI_RECV: self.handle_add_ai(dgi, sender) elif msgtype == STATESERVER_OBJECT_SET_OWNER_RECV: self.handle_set_owner(dgi, sender) elif msgtype == DBSERVER_GET_STORED_VALUES_RESP: self.activate_callback(dgi) elif msgtype == STATESERVER_SHARD_REST: self.handle_shard_rest(dgi) elif msgtype == STATESERVER_OBJECT_LOCATE: context = dgi.get_uint32() do_id = dgi.get_uint32() do = self.service.objects.get(do_id) resp = Datagram() resp.add_server_header([sender], do_id, STATESERVER_OBJECT_LOCATE_RESP) resp.add_uint32(context) resp.add_uint32(do_id) if do is None: resp.add_uint8(False) self.service.send_datagram(resp) else: resp.add_uint8(True) parent_id, zone_id = do.parent_id, do.zone_id resp.add_uint32(parent_id) resp.add_uint32(zone_id) ai_channel = do.ai_channel if do.ai_channel else 0 resp.add_uint32(ai_channel) self.service.send_datagram(resp)
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 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_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 test_add_uint8(self): dg = Datagram() other = b'' dg.add_uint8(12) other += pack_unsigned(12) self.assertEqual(dg.bytes(), other) dg.add_uint8(47) other += pack_unsigned(47) self.assertEqual(dg.bytes(), other) dg.add_uint8(255) other += pack_unsigned(255) self.assertEqual(dg.bytes(), other) dg.add_uint8(0) other += pack_unsigned(0) self.assertEqual(dg.bytes(), other) with self.assertRaises(OverflowError): dg.add_uint8(256) dg.add_uint8(-3)
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)