diff --git a/kennel/config.py b/kennel/config.py index 8cec699..4452044 100644 --- a/kennel/config.py +++ b/kennel/config.py @@ -5,8 +5,8 @@ load_dotenv() class Config: - SIMULATION_WIDTH = int(os.getenv("SIMULATION_WIDTH", 1000)) - SIMULATION_HEIGHT = int(os.getenv("SIMULATION_HEIGHT", 1000)) + WORLD_WIDTH = int(os.getenv("WORLD_WIDTH", 1000)) + WORLD_HEIGHT = int(os.getenv("WORLD_HEIGHT", 1000)) HATECOMPUTERS_ENDPOINT = os.getenv( "HATECOMPUTERS_ENDPOINT", "https://hatecomputers.club" diff --git a/kennel/engine/components/controllable.py b/kennel/engine/components/controllable.py index 49b0557..25ec835 100644 --- a/kennel/engine/components/controllable.py +++ b/kennel/engine/components/controllable.py @@ -10,4 +10,5 @@ class Controllable(Component): return f"Controllable(by={self.by})" def dict(self) -> dict: - return {"by": self.by} + # don't serialize who owns this + return {} diff --git a/kennel/engine/entities/entity.py b/kennel/engine/entities/entity.py index 0ca8666..b72e996 100644 --- a/kennel/engine/entities/entity.py +++ b/kennel/engine/entities/entity.py @@ -23,6 +23,13 @@ class Entity: def add_component(self, component: Component) -> None: self.components[component.component_type] = component + def to_dict(self) -> dict: + return { + "entity_type": self.entity_type, + "id": self.id, + "components": {k: v.dict() for k, v in self.components.items()}, + } + class EntityManager: def __init__(self): @@ -58,3 +65,6 @@ class EntityManager: def get_entity(self, entity_id: str) -> Optional[Entity]: return self.entities.get(entity_id) + + def to_dict(self) -> dict: + return {k: v.to_dict() for k, v in self.entities.items()} diff --git a/kennel/engine/systems/network.py b/kennel/engine/systems/network.py index 7df8088..c9696c8 100644 --- a/kennel/engine/systems/network.py +++ b/kennel/engine/systems/network.py @@ -6,6 +6,7 @@ import asyncio class EventType(str, Enum): + INITIAL_STATE = "INITIAL_STATE" ENTITY_BORN = "ENTITY_BORN" ENTITY_POSITION_UPDATE = "ENTITY_POSITION_UPDATE" ENTITY_DEATH = "ENTITY_DEATH" diff --git a/kennel/engine/systems/system.py b/kennel/engine/systems/system.py index 0e51f3a..78bda58 100644 --- a/kennel/engine/systems/system.py +++ b/kennel/engine/systems/system.py @@ -5,6 +5,7 @@ from abc import abstractmethod class SystemType(str, Enum): NETWORK = "NETWORK" + WORLD = "WORLD" class System: diff --git a/kennel/engine/systems/world.py b/kennel/engine/systems/world.py new file mode 100644 index 0000000..396e7cb --- /dev/null +++ b/kennel/engine/systems/world.py @@ -0,0 +1,24 @@ +from kennel.engine.systems.system import System, SystemType +from kennel.engine.entities.entity import EntityManager +from kennel.engine.components.component import ComponentType +from kennel.app import logger + + +class WorldSystem(System): + def __init__(self, width: int, height: int): + super().__init__(SystemType.WORLD) + self.width = width + self.height = height + + async def update(self, entity_manager: EntityManager, delta_time: float): + entities = entity_manager.get_entities_with_component(ComponentType.POSITION) + for entity in entities: + position = entity.get_component(ComponentType.POSITION) + if position is None: + logger.error(f"Entity {entity} has no position component") + continue + + position.x = max(0, min(self.width, position.x)) + position.y = max(0, min(self.height, position.y)) + + entity.add_component(position) diff --git a/kennel/kennel.py b/kennel/kennel.py index 0b38261..61847a5 100644 --- a/kennel/kennel.py +++ b/kennel/kennel.py @@ -10,6 +10,7 @@ from kennel.engine.systems.network import ( Event, EventType, ) +from kennel.engine.systems.world import WorldSystem from kennel.config import config from .app import logger from typing import List @@ -59,9 +60,6 @@ class KennelClientEventTransformer(ClientEventTransformer): logger.error(f"Entity {id} does not exist") return event for component_type in entity.components: - if component_type == ComponentType.CONTROLLABLE: - # Do not send controllable components to clients - continue component = entity.get_component(component_type) if component is not None: event.data[component_type] = component.dict() @@ -69,8 +67,9 @@ class KennelClientEventTransformer(ClientEventTransformer): return event +system_manager.add_system(WorldSystem(config.WORLD_WIDTH, config.WORLD_HEIGHT)) system_manager.add_system( - NetworkSystem(EventProcessor(), KennelClientEventTransformer()) + NetworkSystem(EventProcessor(), KennelClientEventTransformer()), ) kennel = Game(entity_manager, system_manager, config.MIN_TIME_STEP) diff --git a/kennel/main.py b/kennel/main.py index 122e40a..17a143b 100644 --- a/kennel/main.py +++ b/kennel/main.py @@ -12,16 +12,16 @@ from fastapi.staticfiles import StaticFiles from kennel.engine.systems.system import SystemType from kennel.engine.systems.network import Event, Publishable, EventType from typing import Annotated, Optional -from .kennel import ( +from kennel.kennel import ( kennel, system_manager, entity_manager, create_session_controllable_entities, ) -from .app import app, templates, logger -from .kennelcats import KennelCatService -from .middleware import logger_middleware -from .config import config +from kennel.app import app, templates, logger +from kennel.kennelcats import KennelCatService +from kennel.middleware import logger_middleware +from kennel.config import config import asyncio import uuid @@ -85,6 +85,16 @@ async def websocket_endpoint( await websocket.accept() logger.info(f"Websocket connection established for session {session}") + await websocket.send_json( + { + "event_type": EventType.INITIAL_STATE, + "data": { + "world": {"width": config.WORLD_WIDTH, "height": config.WORLD_HEIGHT}, + "entities": kennel.entity_manager.to_dict(), + }, + } + ) + session_entities = create_session_controllable_entities(session) try: network_system = system_manager.get_system(SystemType.NETWORK) diff --git a/templates/index.html b/templates/index.html index 1bb53e8..07acb30 100644 --- a/templates/index.html +++ b/templates/index.html @@ -5,10 +5,10 @@