async def get_current_room(entity: Entity, populate=True): from core.src.world.builder import map_repository not entity.get_component(PositionComponent) and await load_components(entity, PositionComponent) room = await map_repository.get_room(entity.get_component(PositionComponent), populate=populate) populate and await room.populate_content() entity.set_room(room) return room
async def emit_room_sys_msg(entity: Entity, event_type: str, details: typing.Dict, room=None, include_origin=True): from core.src.world.builder import transport assert isinstance(details, dict) room = room or (entity.get_room() or await get_current_room(entity)) listeners = await get_eligible_listeners_for_room(room) if listeners: listeners = listeners if not include_origin else [e for e in listeners if e.entity_id != entity.entity_id] listeners and ( await batch_load_components((SystemComponent, 'connection'), PositionComponent, entities=listeners) ) futures = [] for entity in listeners: position = entity.get_component(PositionComponent) if position.coord == room.position.coord and entity.get_component(SystemComponent).connection: payload = { "event": event_type, "target": "entity", "details": details, "position": room.position.value } futures.append( transport.send_system_event( entity.get_component(SystemComponent).connection.value, payload ) ) await asyncio.gather(*futures)
def move_entity_from_container( entity: Entity, target: (PositionComponent, InventoryComponent), current_owner: Entity = None ): current_position = entity.get_component(PositionComponent) if current_position.parent_of: assert current_owner and current_owner.entity_id == current_position.parent_of, ( current_owner and current_owner.entity_id, current_position.parent_of ) current_owner.get_component(InventoryComponent).content.remove(entity.entity_id) current_owner.set_for_update(current_owner.get_component(InventoryComponent)) if isinstance(target, InventoryComponent): target_owner = target.owned_by() new_position = PositionComponent().parent_of.set(target_owner.entity_id).coord.null() new_position.add_previous_position(current_position) target.content.append(entity.entity_id) target_owner.set_for_update(target) entity.set_for_update(new_position) elif isinstance(target, PositionComponent): assert target.coord.value new_position = PositionComponent().parent_of.null().coord.set(target.coord.value) entity.set_for_update(new_position) else: raise ValueError('Target must be type PosComponent or ContainerComponent, is: %s' % target) return entity
async def _destroy_instance(entity: Entity, parent_alias: str, entity_id_to_delete: int, force=False): from core.src.world.builder import world_repository if not await world_repository.entity_exists(entity_id_to_delete): await emit_msg(entity, 'Entity does not exists, use cleanup (bug)') return entity_to_delete = Entity(entity_id_to_delete) await load_components(entity_to_delete, (SystemComponent, 'instance_of', 'character')) system_component = entity_to_delete.get_component(SystemComponent) if system_component.character: await emit_msg(entity, 'Cannot destroy characters with this command') return if force != '--force': if system_component.instance_of and (parent_alias != system_component.instance_of): await emit_msg( entity, 'Entity {} is not type <{}>, it\'s <{}> instead'.format( entity_id_to_delete, parent_alias, system_component.instance_of)) return res = await world_repository.delete_entity(entity_id_to_delete) return res
async def look_at_target(entity: Entity, *arguments: str): if len(arguments) > 1: await emit_msg(entity, 'Command error - Nested targets not implemented yet') return target_entity = await search_entity_in_sight_by_keyword( entity, arguments[0]) if not target_entity: await emit_msg(entity, messages.missing_target()) elif entity.entity_id == target_entity.entity_id: await emit_msg(entity, messages.self_look()) elif not await ensure_same_position(entity, target_entity): await emit_msg(entity, messages.missing_target()) else: if await check_entity_can_receive_messages(target_entity): # Avoid to send messages to... knives, for example :-) await emit_msg( target_entity, messages.entity_looks_at_you( entity.get_component(AttributesComponent).keyword)) await emit_room_msg( origin=entity, target=target_entity, message_template=messages.entity_looks_at_entity_template()) await emit_msg( entity, messages.look_at_entity( target_entity.get_component(AttributesComponent).keyword)), await emit_sys_msg(entity, "look", target_entity)
async def follow(entity: Entity, *arguments: str): from core.src.world.builder import follow_system_manager if not len(arguments): return await unfollow(entity) assert len(arguments) == 1 target_entity = await search_entity_in_sight_by_keyword( entity, arguments[0]) if not target_entity: await emit_msg(entity, messages.target_not_found()) elif entity.entity_id == target_entity.entity_id: if follow_system_manager.is_following_someone(entity.entity_id): await unfollow(entity) else: await emit_msg(entity, messages.not_following_anyone()) elif follow_system_manager.is_follow_repetition(entity.entity_id, target_entity.entity_id): await emit_msg(entity, messages.already_following_that_target()) elif follow_system_manager.is_follow_loop(entity, target_entity): await emit_msg(entity, messages.follow_is_loop()) elif not await ensure_same_position(entity, target_entity): await emit_msg(entity, messages.target_not_found()) else: previous_target = follow_system_manager.get_follow_target( entity.entity_id) if previous_target: await emit_msg( previous_target, messages.entity_stop_following_you( entity.get_component(AttributesComponent).keyword)) follow_system_manager.stop_following(entity.entity_id) follow_system_manager.follow_entity(entity.entity_id, target_entity.entity_id) await asyncio.gather( emit_msg( entity, messages.follow_entity( target_entity.get_component(AttributesComponent).keyword)), emit_msg( target_entity, messages.entity_is_following_you( entity.get_component(AttributesComponent).keyword))) await emit_room_msg( origin=entity, target=target_entity, message_template=messages.entity_follows_entity_template(), )
async def whoami(entity: Entity): await load_components(entity, AttributesComponent) await emit_msg( entity, json.dumps({ "event": "whoami", "id": entity.entity_id, "name": entity.get_component(AttributesComponent).name.value }))
async def get_room_at_direction(entity: Entity, direction_enum, populate=True): from core.src.world.builder import map_repository delta = direction_to_coords_delta(direction_enum) if not delta: return await load_components(entity, PositionComponent) look_cords = apply_delta_to_position(entity.get_component(PositionComponent), delta) room = await map_repository.get_room(look_cords, populate=populate) populate and await room.populate_content() return room
async def disconnect_entity(entity: Entity, msg=True): from core.src.world.builder import map_repository from core.src.world.builder import websocket_channels_service from core.src.world.builder import cmds_observer events_publisher = get_events_publisher() await load_components(entity, PositionComponent) listeners = await get_eligible_listeners_for_area( entity.get_component(PositionComponent)) await events_publisher.on_entity_disappear_position( entity, entity.get_component(PositionComponent), "disconnect", listeners) msg and await emit_sys_msg(entity, "event", {"event": "quit"}) current_channel_id = entity.get_component(SystemComponent).connection.value entity.set_for_update(SystemComponent().connection.set("")) if await update_entities(entity): await map_repository.remove_entity_from_map( entity.entity_id, entity.get_component(PositionComponent)) await websocket_channels_service.close_namespace_for_entity_id( entity.entity_id) cmds_observer.close_channel(current_channel_id)
async def on_event(self, entity_id: int, message: typing.Dict, room: typing.Tuple, transport_id: str): room = PositionComponent(coord='{},{},{}'.format(*room)) entity = Entity(entity_id).set_component( SystemComponent().connection.set(transport_id)) await load_components(entity, PositionComponent) curr_pos = entity.get_component(PositionComponent) interest_type = await self._get_message_interest_type( entity, room, curr_pos) if not interest_type.value: return await self.publish_event(entity, message, room, interest_type, curr_pos)
async def on_disconnect(self, entity: Entity): current_connection = ( await self.world_repository.read_struct_components_for_entity( entity.entity_id, (SystemComponent, 'connection')))[SystemComponent.enum] if current_connection.connection.value != entity.get_component( SystemComponent).connection.value: return await disconnect_entity(entity, msg=False) self.events_subscriber_service.remove_observer_for_entity_id( entity.entity_id) self.manager.remove_transport(entity.entity_id) await self.events_subscriber_service.unsubscribe_all(entity)
async def drop(entity: Entity, keyword: str): await load_components(entity, PositionComponent, InventoryComponent) inventory = entity.get_component(InventoryComponent) items = await search_entities_in_container_by_keyword(inventory, keyword) msgs_stack = get_stacker() items_to_drop = [] for item in items: items_to_drop.append( move_entity_from_container( item, target=entity.get_component(PositionComponent), current_owner=entity)) if not items_to_drop: await emit_msg(entity, messages.target_not_found()) return entity.set_for_update(inventory) msgs_stack.add( emit_sys_msg(entity, 'remove_items', messages.items_to_message(items_to_drop)), emit_room_sys_msg(entity, 'add_items', messages.items_to_message(items_to_drop))) if len(items_to_drop) == 1: msgs_stack.add( emit_msg(entity, messages.on_drop_item(items[0])), emit_room_msg(origin=entity, message_template=messages.on_entity_drop_item( items[0]))) else: msgs_stack.add( emit_msg(entity, messages.on_drop_multiple_items()), emit_room_msg( origin=entity, message_template=messages.on_entity_drops_multiple_items())) if not await update_entities(entity, *items_to_drop): await emit_msg(entity, messages.target_not_found()) msgs_stack.cancel() else: await msgs_stack.execute()
async def emit_room_msg(origin: Entity, message_template, target: Entity = None, room=None): """ Emit a room message in the same room of "origin". The room can be overridden with the room= keyword argument, accepting a Room type as input. The message template must have a mandatory {origin} and an optional {target} placeholders. origin and target parameters must be type Entity, with the AttributesComponent loaded. origin is mandatory, target is optional. The emitted message type is a string, the target is the client text field. """ from core.src.world.builder import transport room = room or (origin.get_room() or await get_current_room(origin)) listeners = await get_eligible_listeners_for_room(room) listeners = [ listener for listener in listeners if listener.entity_id not in ( origin and origin.entity_id, target and target.entity_id ) ] if not listeners: return await batch_load_components((SystemComponent, 'connection'), PositionComponent, entities=listeners) futures = [] for entity in listeners: position = entity.get_component(PositionComponent) if position.coord == room.position.coord and entity.get_component(SystemComponent).connection: # TODO - Evaluate VS entity memory futures.append( transport.send_message( entity.get_component(SystemComponent).connection.value, message_template.format( origin=origin.get_component(AttributesComponent).keyword, target=target and target.get_component(AttributesComponent).keyword ) ) ) await asyncio.gather(*futures)
async def on_connect(self, entity: Entity): connection_id = entity.get_component(SystemComponent).connection.value self.events_subscriber_service.add_observer_for_entity_id( entity.entity_id, self.pubsub_observer) await update_entities( entity.set_for_update( SystemComponent().connection.set(connection_id))) await load_components(entity, PositionComponent) if not entity.get_component(PositionComponent).coord: await cast_entity(entity, get_base_room_for_entity(entity), on_connect=True, reason="connect") self.loop.create_task(self.greet(entity)) else: await cast_entity(entity, entity.get_component(PositionComponent), update=False, on_connect=True, reason="connect") self.manager.set_transport(entity.entity_id, connection_id) self.commands_observer.enable_channel(connection_id) self.loop.create_task(look(entity)) self.loop.create_task(getmap(entity))
async def ensure_same_position(self_entity: Entity, *entities: Entity) -> bool: """ Ensures two or more entities are in the same position. Return a boolean. """ assert self_entity.itsme self_pos = self_entity.get_component(PositionComponent) await batch_load_components(PositionComponent, entities=entities) for e in entities: position = e.get_component(PositionComponent) if position.coord and position.coord != self_pos.coord: return False elif position.parent_of and position.parent_of != self_entity.entity_id: return False elif not position.parent_of and not position.coord: LOGGER.core.error('Cannot determinate if an item is in the same position as previous declared') return False return True
async def put(entity: Entity, keyword: str, target: str): await load_components(entity, PositionComponent, InventoryComponent) inventory = entity.get_component(InventoryComponent) items = await search_entities_in_container_by_keyword(inventory, keyword) target_entity = await search_entity_in_sight_by_keyword( entity, target, filter_by=InventoryComponent, include_self=False ) if not target_entity: await emit_msg(entity, messages.target_not_found()) return msgs_stack = get_stacker() items_to_drop = [] for item in items: items_to_drop.append( move_entity_from_container( item, target=target_entity.get_component(InventoryComponent), current_owner=entity ) ) if not items_to_drop: await emit_msg(entity, messages.target_not_found()) return msgs_stack.add( emit_sys_msg(entity, 'remove_items', messages.items_to_message(items_to_drop)), #emit_room_sys_msg(entity, 'add_items', messages.items_to_message(items_to_drop)) # todo event for container ) if len(items_to_drop) == 1: msgs_stack.add( emit_msg(entity, messages.on_put_item(items[0], target_entity)), emit_room_msg(origin=entity, message_template=messages.on_entity_put_item(items[0], target_entity)) ) else: msgs_stack.add( emit_msg(entity, messages.on_put_multiple_items(target_entity)), emit_room_msg(origin=entity, message_template=messages.on_entity_put_multiple_items(target_entity)) ) if not await update_entities(entity, target_entity, *items_to_drop): await emit_msg(entity, messages.target_not_found()) msgs_stack.cancel() else: await msgs_stack.execute()
async def _create_instance(entity: Entity, parent_alias: str, *args): from core.src.world.builder import world_repository, library_repository instanced = library_repository.get_instance_of(parent_alias, entity) if not instanced: await emit_msg(entity, 'Cannot obtain instance of {}'.format(parent_alias)) return if args: location = args[0] if location not in ('.', '@here'): await emit_msg( entity, 'Error, location {} invalid (allowed location: ".")'.format( location)) await load_components(entity, PositionComponent) instanced.set_for_update(entity.get_component(PositionComponent)) else: await emit_msg(entity, 'Inventory not implemented, please specify a location') await world_repository.save_entity(instanced) return instanced
async def pick(entity: Entity, *arguments): keyword = arguments[0] if len(arguments) == 1: room = await get_current_room(entity) items_to_pick = await search_entities_in_room_by_keyword( room, keyword, filter_by=(AttributesComponent, 'collectible', True)) container_entity = None elif len(arguments) == 2: container_keyword = arguments[1] container_entity = await search_entity_in_sight_by_keyword( entity, container_keyword, filter_by=InventoryComponent, include_self=False) await load_components(container_entity, InventoryComponent) if not container_entity: await emit_msg(entity, messages.target_not_in_room()) return container_inventory = container_entity.get_component( InventoryComponent) items_to_pick = await search_entities_in_container_by_keyword( container_inventory, keyword) else: raise ValueError('max 2 arguments') if not items_to_pick: await emit_msg(entity, messages.target_not_in_room()) return await load_components(entity, InventoryComponent) inventory = entity.get_component(InventoryComponent) for item in items_to_pick: move_entity_from_container(item, target=inventory, current_owner=container_entity) msgs_stack = get_stacker() if len(items_to_pick) == 1: if container_entity: # SINGLE ITEM FOUND IN CONTAINER msgs_stack.add( emit_msg( entity, messages.item_picked_from_container( items_to_pick[0], container_entity)), emit_room_msg(origin=entity, message_template=messages. entity_picked_item_from_container( items_to_pick[0], container_entity)), ) else: # SINGLE ITEM FOUND IN ROOM msgs_stack.add( emit_msg(entity, messages.item_picked(items_to_pick[0])), emit_room_msg(origin=entity, message_template=messages.entity_picked_item( items_to_pick[0])), ) else: if container_entity: # MULTIPLE ITEMS FOUND IN CONTAINER msgs_stack.add( emit_msg( entity, messages.picked_multiple_items_from_container( container_entity)), emit_room_msg(origin=entity, message_template=messages. entity_picked_multiple_items_from_container( container_entity)), ) else: # MULTIPLE ITEMS FOUND IN ROOM msgs_stack.add( emit_msg(entity, messages.picked_multiple_items()), emit_room_msg( origin=entity, message_template=messages.entity_picked_multiple_items()), ) if items_to_pick: msgs_stack.add( emit_sys_msg(entity, 'add_items', messages.items_to_message(items_to_pick)), ) if not container_entity: msgs_stack.add( emit_room_sys_msg(entity, 'remove_items', messages.items_to_message(items_to_pick))) if not await update_entities( entity, *items_to_pick, *(container_entity and (container_entity, ) or ())): await emit_msg(entity, messages.cannot_pick_item()) msgs_stack.cancel() else: await msgs_stack.execute()
async def subscribe_events(self, entity: Entity): connection = entity.get_component(SystemComponent).connection assert connection.value self._transports_by_entity_id[entity.entity_id] = connection.value await asyncio.gather(self._subscribe_entity(entity))
async def inventory(entity: Entity): await load_components(entity, PositionComponent, InventoryComponent) inventory = entity.get_component(InventoryComponent) items = await search_entities_in_container_by_keyword(inventory, '*') await emit_sys_msg(entity, 'inventory', messages.items_to_message(items))
def on_drop_item(self, item: Entity): return 'Posi {}'.format( item.get_component(AttributesComponent).keyword)
def on_entity_drop_item(self, item: Entity): return '{origin} posa a terra %s' % item.get_component( AttributesComponent).keyword
def item_picked(self, item: Entity): return 'raccogli {}'.format( item.get_component(AttributesComponent).keyword)
def entity_picked_item(self, item: Entity): return '{origin} raccoglie %s' % item.get_component( AttributesComponent).keyword