aboutsummaryrefslogtreecommitdiff
path: root/kennel/kennel.py
blob: 6d4f8f9faca136df46ed392bb851c83288344a83 (plain) (blame)
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]