1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
import asyncio
import uuid
import time
from typing import List, Optional
from kennel.config import config
from kennel.engine.components.component import ComponentType
from kennel.engine.entities.entity import Entity, EntityManager
from kennel.engine.entities.laser import Laser
from kennel.engine.entities.cat import Cat
from kennel.engine.game import Game
from kennel.engine.systems.markov_transition_state_system import (
MarkovTransitionStateSystem,
)
from kennel.engine.systems.network import (
EntityPositionUpdateEvent,
EntityBornEvent,
EntityDeathEvent,
Event,
EventType,
NetworkSystem,
UpstreamEventProcessor,
)
from kennel.kennelcats import KennelCatService, KennelCat
from kennel.engine.systems.system import SystemManager
from kennel.engine.systems.world import WorldSystem
from .app import logger
entity_manager = EntityManager()
system_manager = SystemManager()
class KennelEventProcessor(UpstreamEventProcessor):
def accept(
self, entity_manager: EntityManager, event: tuple[Event, str]
) -> Optional[Event]:
client_event, client_id = event
if client_event.event_type == EventType.ENTITY_POSITION_UPDATE:
return self._process_entity_position_update(
entity_manager, client_event, client_id
)
def _process_entity_position_update(
self,
entity_manager: EntityManager,
event: EntityPositionUpdateEvent,
client_id: str,
) -> Optional[Event]:
entity = entity_manager.get_entity(event.data["id"])
if entity is None:
logger.error(f"Entity(id={event.data['id']}) does not exist")
return
controllable = entity.get_component(ComponentType.CONTROLLABLE)
if controllable is None or controllable.by != client_id:
logger.error(f"Entity {entity} is not controllable by client {client_id}")
return
position = entity.get_component(ComponentType.POSITION)
if position is None:
logger.error(f"Entity {entity} has no position")
return
position.x = event.data["position"]["x"]
position.y = event.data["position"]["y"]
entity.add_component(position)
entity_manager.add_entity(entity)
return event
class KennelCatsManager:
kennel_cat_service: KennelCatService
entity_manager: EntityManager
network_system: NetworkSystem
last_seen: set[str]
poll_interval_sec: int
running: bool
def __init__(
self,
kennel_cat_service: KennelCatService,
entity_manager: EntityManager,
network_system: NetworkSystem,
poll_interval_sec: int,
):
self.kennel_cat_service = kennel_cat_service
self.entity_manager = entity_manager
self.network_system = network_system
self.poll_interval_sec = poll_interval_sec
self.last_seen = set()
self.running = False
async def start(self) -> None:
logger.info("starting kennel cats manager")
if self.running:
return
self.running = True
while self.running:
logger.info("polling kennel cats service")
cats = self.kennel_cat_service.get_kennel()
cats_table = {cat.id: cat for cat in cats}
cat_ids = set([cat.id for cat in cats])
removed_cats = [cats_table[id] for id in self.last_seen.difference(cat_ids)]
added_cats = [cats_table[id] for id in cat_ids.difference(self.last_seen)]
logger.info(f"removing {removed_cats}, adding {added_cats}")
for removed in removed_cats:
self.entity_manager.remove_entity(removed)
entity_death = EntityDeathEvent(removed.id)
self.network_system.server_global_event(entity_death)
for added in added_cats:
new_cat = Cat(added.id, added.spritesheet)
self.entity_manager.add_entity(new_cat)
entity_born = EntityBornEvent(new_cat)
self.network_system.server_global_event(entity_born)
self.last_seen = cat_ids
await asyncio.sleep(self.poll_interval_sec)
def stop(self) -> None:
logger.info("stopping kennel cats manager")
self.running = False
network_system = NetworkSystem(KennelEventProcessor())
world_system = WorldSystem(config.WORLD_WIDTH, config.WORLD_HEIGHT)
markov_transition_state_system = MarkovTransitionStateSystem(network_system)
system_manager.add_system(network_system)
system_manager.add_system(world_system)
kennel = Game(entity_manager, system_manager, config.MIN_TIME_STEP)
kennel_cat_service = KennelCatService(config.HATECOMPUTERS_ENDPOINT)
kennel_cats_manager = KennelCatsManager(
kennel_cat_service, entity_manager, network_system, config.KENNEL_CATS_POLL_SEC
)
def create_session_controllable_entities(session: str) -> List[Entity]:
laser = Laser(uuid.uuid4().hex[:10], session)
return [laser]
|