149 lines
4.6 KiB
Python
149 lines
4.6 KiB
Python
import asyncio
|
|
from abc import abstractmethod
|
|
from enum import Enum
|
|
from typing import List, Optional
|
|
|
|
from kennel.app import logger
|
|
from kennel.engine.entities.entity import Entity, EntityManager
|
|
|
|
from .system import System, SystemType
|
|
|
|
|
|
class EventType(str, Enum):
|
|
INITIAL_STATE = "INITIAL_STATE"
|
|
SET_CONTROLLABLE = "SET_CONTROLLABLE"
|
|
ENTITY_BORN = "ENTITY_BORN"
|
|
ENTITY_DEATH = "ENTITY_DEATH"
|
|
ENTITY_POSITION_UPDATE = "ENTITY_POSITION_UPDATE"
|
|
|
|
|
|
class Event:
|
|
def __init__(self, event_type: EventType, data: dict):
|
|
self.event_type = event_type
|
|
self.data = data
|
|
|
|
def __str__(self) -> str:
|
|
return f"Event({self.event_type}, {self.data})"
|
|
|
|
def to_dict(self) -> dict:
|
|
return {"event_type": self.event_type, "data": self.data}
|
|
|
|
@staticmethod
|
|
def from_dict(data: dict) -> Optional["Event"]:
|
|
if "event_type" not in data or "data" not in data:
|
|
return
|
|
event_type = data["event_type"]
|
|
if event_type == EventType.INITIAL_STATE:
|
|
return InitialStateEvent(
|
|
data["data"]["world"]["width"],
|
|
data["data"]["world"]["height"],
|
|
data["data"]["entities"],
|
|
)
|
|
if event_type == EventType.SET_CONTROLLABLE:
|
|
return SetControllableEvent(data["data"]["id"], data["data"]["client_id"])
|
|
if event_type == EventType.ENTITY_BORN:
|
|
return EntityBornEvent(data["data"])
|
|
if event_type == EventType.ENTITY_POSITION_UPDATE:
|
|
return EntityPositionUpdateEvent(
|
|
data["data"]["id"], data["data"]["position"]
|
|
)
|
|
if event_type == EventType.ENTITY_DEATH:
|
|
return EntityDeathEvent(data["data"]["id"])
|
|
|
|
logger.warn(f"Unknown event type: {data['event_type']}")
|
|
return Event(EventType(data["event_type"]), data["data"])
|
|
|
|
|
|
class InitialStateEvent(Event):
|
|
def __init__(self, world_width: int, world_height: int, entities: List[Entity]):
|
|
self.world_width = world_width
|
|
self.world_height = world_height
|
|
self.entities = entities
|
|
super().__init__(
|
|
EventType.INITIAL_STATE,
|
|
{
|
|
"world": {"width": world_width, "height": world_height},
|
|
"entities": [entity.to_dict() for entity in entities],
|
|
},
|
|
)
|
|
|
|
|
|
class SetControllableEvent(Event):
|
|
def __init__(self, entity_id: str, client_id: str):
|
|
self.entity_id = entity_id
|
|
self.client_id = client_id
|
|
super().__init__(
|
|
EventType.SET_CONTROLLABLE, {"id": entity_id, "client_id": client_id}
|
|
)
|
|
|
|
|
|
class EntityBornEvent(Event):
|
|
def __init__(self, entity: Entity):
|
|
self.entity = entity
|
|
super().__init__(EventType.ENTITY_BORN, {"entity": entity.to_dict()})
|
|
|
|
|
|
class EntityPositionUpdateEvent(Event):
|
|
def __init__(self, entity_id: str, position: dict):
|
|
super().__init__(
|
|
EventType.ENTITY_POSITION_UPDATE,
|
|
{"id": entity_id, "position": position},
|
|
)
|
|
|
|
|
|
class EntityDeathEvent(Event):
|
|
def __init__(self, entity_id: str):
|
|
super().__init__(EventType.ENTITY_DEATH, {"id": entity_id})
|
|
|
|
|
|
class Publishable:
|
|
@abstractmethod
|
|
async def publish(self, event: Event):
|
|
pass
|
|
|
|
|
|
class EventProcessor:
|
|
@abstractmethod
|
|
def accept(
|
|
self, entity_manager: EntityManager, event: Event | tuple[Event, str]
|
|
) -> None:
|
|
pass
|
|
|
|
|
|
class NetworkSystem(System):
|
|
def __init__(self, event_processor: EventProcessor):
|
|
super().__init__(SystemType.NETWORK)
|
|
self.event_processor = event_processor
|
|
|
|
self.events = []
|
|
self.client_events = []
|
|
self.clients = {}
|
|
|
|
async def update(self, entity_manager: EntityManager, delta_time: float) -> None:
|
|
if len(self.events) + len(self.client_events) == 0:
|
|
return
|
|
for event in self.events + self.client_events:
|
|
self.event_processor.accept(entity_manager, event)
|
|
client_sendable = self.events + [event for event, _ in self.client_events]
|
|
await asyncio.gather(
|
|
*[
|
|
client.publish(event)
|
|
for client in self.clients.values()
|
|
for event in client_sendable
|
|
]
|
|
)
|
|
self.events = []
|
|
self.client_events = []
|
|
|
|
def client_event(self, client_id: str, event: Event) -> None:
|
|
self.client_events.append((event, client_id))
|
|
|
|
def add_event(self, event: Event) -> None:
|
|
self.events.append(event)
|
|
|
|
def add_client(self, client_id: str, client: Publishable) -> None:
|
|
self.clients[client_id] = client
|
|
|
|
def remove_client(self, client_id: str) -> None:
|
|
del self.clients[client_id]
|