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)
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 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)
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)
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 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)
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)
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)
def handle_query_all(self, dgi, sender): other = dgi.get_uint8() context = dgi.get_uint32() resp = Datagram() resp.add_server_header([sender], self.do_id, STATESERVER_QUERY_OBJECT_ALL_RESP) resp.add_uint32(self.do_id) resp.add_uint16(context) self.append_required_data(resp, False, True) self.service.send_datagram(resp)
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 test_get(self): dg = Datagram() dg.add_uint16(25) dg.add_int64(-354843598374) dg.add_string32('datagram iterator test'.encode('utf-8')) dgi = dg.iterator() self.assertEqual(dgi.get_uint16(), 25) self.assertEqual(dgi.get_int64(), -354843598374) self.assertEqual(dgi.get_string32(), 'datagram iterator test')
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
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 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'))
def receive_get_avatars(self, dgi): query = Datagram() query.add_server_header([ DBSERVERS_CHANNEL, ], self.channel, DBSERVER_ACCOUNT_QUERY) disl_id = self.account.disl_id query.add_uint32(disl_id) field_number = self.service.avatars_field.number query.add_uint16(field_number) self.service.send_datagram(query) self.tasks.append(self.service.loop.create_task(self.do_login()))
def disconnect(self, booted_index, booted_text): for task in self.tasks: task.cancel() del self.tasks[:] resp = Datagram() resp.add_uint16(CLIENT_GO_GET_LOST) resp.add_uint16(booted_index) resp.add_string16(booted_text.encode('utf-8')) self.transport.write(len(resp).to_bytes(2, byteorder='little')) self.transport.write(resp.bytes()) self.transport.close() self.service.log.debug( f'Booted client {self.channel} with index {booted_index} and text: "{booted_text}"' )
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 save_field(self, field: AtomicField, data): if field.is_required: self.required[field.name] = data elif field.is_ram: self.ram[field.name] = data if self.db and field.is_db: dg = Datagram() dg.add_server_header([DBSERVERS_CHANNEL], self.do_id, DBSERVER_SET_STORED_VALUES) dg.add_uint32(self.do_id) dg.add_uint16(1) dg.add_uint16(field.number) dg.add_bytes(data) self.service.send_datagram(dg) self.service.log.debug(f'Object {self.do_id} saved value {data} for field {field.name} to database.')
def test_copy_datagram(self): dg = Datagram() dg.add_string32('testing copy'.encode('utf-8')) dg2 = dg.copy() self.assertEqual(dg.bytes(), dg2.bytes()) dg.add_uint16(65200) self.assertNotEqual(dg.bytes(), dg2.bytes()) data = dg2.bytes() del dg self.assertEqual(dg2.bytes(), data)
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)
def activate_callback(self, dgi): context = dgi.get_uint32() do_id = dgi.get_uint32() state_server = self.service parent_id, zone_id, owner_channel, number, other_data = state_server.queries[do_id] dclass = state_server.dc_file.classes[number] del state_server.queries[do_id] required = {} ram = {} count = dgi.get_uint16() for i in range(count): field_number = dgi.get_uint16() field = state_server.dc_file.fields[field_number]() if field.is_required: required[field.name] = field.unpack_bytes(dgi) else: ram[field.name] = field.unpack_bytes(dgi) for field_number, data in other_data: field = state_server.dc_file.fields[field_number]() if field.is_required: required[field.name] = data else: ram[field.name] = data if field.is_db: dg = Datagram() dg.add_server_header([DBSERVERS_CHANNEL], do_id, DBSERVER_SET_STORED_VALUES) dg.add_uint32(do_id) dg.add_uint16(1) dg.add_uint16(field.number) dg.add_bytes(data) self.service.send_datagram(dg) self.service.log.debug(f'Activating {do_id} with required:{required}\nram:{ram}\n') obj = DistributedObject(state_server, STATESERVERS_CHANNEL, do_id, parent_id, zone_id, dclass, required, ram, owner_channel=owner_channel, db=True) state_server.database_objects.add(do_id) state_server.objects[do_id] = obj obj.send_owner_entry(owner_channel)
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()))
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 handle_interest_done(self, dgi): handle = dgi.get_uint16() context = dgi.get_uint32() self.service.log.debug( f'sending interest done for handle {handle} context {context}') interest = None for _interest in self.interests: if _interest.handle == handle and _interest.context == context: interest = _interest break if not interest: self.service.log.debug( f'Got interest done for unknown interest: {handle} {context}') return if interest.done: self.service.log.debug('Received duplicate interest done...') return interest.done = True pending = [ self.pending_objects.pop(do_id) for do_id in interest.pending_objects if do_id in self.pending_objects ] # Need this sorting. pending.sort(key=lambda p: p.dc_id) del interest.pending_objects[:] self.service.log.debug( f'Replaying datagrams for {[p.do_id for p in pending]}') for pending_object in pending: for datagram in pending_object.datagrams: self.handle_datagram(datagram, datagram.iterator()) if not interest.ai: resp = Datagram() resp.add_uint16(CLIENT_DONE_INTEREST_RESP) resp.add_uint16(handle) resp.add_uint32(context) self.send_datagram(resp)
def test_overwrite(self): dg = Datagram() dg.add_uint32(2828) pos = dg.tell() self.assertEqual(pos, 4) dg.add_uint16(24) dg.add_int64(-352793) dg.seek(pos) dg.add_uint16(5000) dg.seek(len(dg)) dg.add_string32(b'overwrite') dgi = dg.iterator() self.assertEqual(dgi.get_uint32(), 2828) self.assertEqual(dgi.get_uint16(), 5000) self.assertEqual(dgi.get_int64(), -352793) self.assertEqual(dgi.get_string32(), 'overwrite')
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 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)
def handle_update_field(self, dgi, sender, do_id): if sender == self.channel: return if not self.object_exists(do_id): self.service.log.debug( f'Got field update for unknown object {do_id}') pos = dgi.tell() field_number = dgi.get_uint16() field = self.service.dc_file.fields[field_number]() 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)