Compare commits

..

1 Commits

Author SHA1 Message Date
Elizabeth Hunt e47d451426
woaj
continuous-integration/drone/pr Build is passing Details
2024-09-07 20:20:07 -07:00
15 changed files with 16 additions and 178 deletions

View File

@ -17,7 +17,5 @@ class Config:
COOKIE_MAX_AGE = int(os.getenv("COOKIE_MAX_AGE", 60 * 60 * 24 * 7)) # 1 week COOKIE_MAX_AGE = int(os.getenv("COOKIE_MAX_AGE", 60 * 60 * 24 * 7)) # 1 week
KENNEL_CATS_POLL_SEC = int(os.getenv("KENNEL_CATS_POLL_SEC", 10))
config = Config() config = Config()

View File

@ -5,7 +5,6 @@ from enum import Enum
class ComponentType(str, Enum): class ComponentType(str, Enum):
POSITION = "POSITION" POSITION = "POSITION"
CONTROLLABLE = "CONTROLLABLE" CONTROLLABLE = "CONTROLLABLE"
MARKOV = "MARKOV"
class Component: class Component:

View File

@ -1,24 +0,0 @@
from kennel.engine.components.component import Component, ComponentType
class MarkovTransitionState(Component):
def __init__(
self,
state_names: dict[int, str],
initial_state_vector: list[float],
transition_matrix: list[list[float]],
):
# TODO: Poll rate per state?
# TODO: State being an enum instead of a vector, just choose max and map
self.state_names = state_names
self.state = initial_state_vector
self.transition_matrix = transition_matrix
super().__init__(ComponentType.MARKOV)
def get_max_state_name(self, state_vector: list[float]):
max_val = max(state_vector)
return self.state_names[state_vector.index(max_val)]
def to_dict(self):
return {"state": self.get_max_state_name(self.state)}

View File

@ -1,18 +1,6 @@
from kennel.engine.components.position import Position
from .entity import Entity, EntityType
class Cat(Entity):
def __init__(self, id: str):
components = [Position(0, 0)]
super().__init__(EntityType.CAT, id, components)
# #
# # IDLE, FROLICKING, EEPY, ALERT, CHASING_CURSOR, CHASING_CAT, SCRATCHING, ITCHY # # IDLE, FROLICKING, EEPY, ALERT, CHASING_CURSOR, CHASING_CAT, SCRATCHING, ITCHY
# state_stochastic_matrix = [ [1, 0] # state_stochastic_matrix = [
# # IDLE # # IDLE
# [0.5, 0.1, 0.1, 0.1, 0.1, 0.05, 0.05, 0], # [0.5, 0.1, 0.1, 0.1, 0.1, 0.05, 0.05, 0],
# # FROLICKING # # FROLICKING

View File

@ -6,7 +6,6 @@ from kennel.engine.components.component import Component, ComponentType
class EntityType(str, Enum): class EntityType(str, Enum):
LASER = "LASER" LASER = "LASER"
CAT = "CAT"
class Entity: class Entity:

View File

@ -1,13 +0,0 @@
from kennel.engine.components.component import ComponentType
from kennel.engine.entities.entity import EntityManager
from kennel.engine.systems.system import System, SystemType
from kennel.engine.systems.network import NetworkSystem
class MarkovTransitionStateSystem(System):
def __init__(self, network_system: NetworkSystem):
super().__init__(SystemType.MARKOV)
def update(self, entity_manager: EntityManager, delta_time: float):
entity_manager.get_entities_with_component(ComponentType.MARKOV)
return

View File

@ -7,7 +7,6 @@ from kennel.engine.entities.entity import EntityManager
class SystemType(str, Enum): class SystemType(str, Enum):
NETWORK = "NETWORK" NETWORK = "NETWORK"
WORLD = "WORLD" WORLD = "WORLD"
MARKOV = "MARKOV"
class System: class System:
@ -15,7 +14,7 @@ class System:
self.system_type = system_type self.system_type = system_type
@abstractmethod @abstractmethod
async def update(self, entity_manager: EntityManager, delta_time: float) -> None: async def update(self, entity_manager: EntityManager, delta_time: float):
pass pass

View File

@ -1,27 +1,18 @@
import asyncio
import uuid import uuid
import time
from typing import List, Optional from typing import List, Optional
from kennel.config import config from kennel.config import config
from kennel.engine.components.component import ComponentType from kennel.engine.components.component import ComponentType
from kennel.engine.entities.entity import Entity, EntityManager from kennel.engine.entities.entity import Entity, EntityManager
from kennel.engine.entities.laser import Laser from kennel.engine.entities.laser import Laser
from kennel.engine.entities.cat import Cat
from kennel.engine.game import Game from kennel.engine.game import Game
from kennel.engine.systems.markov_transition_state_system import (
MarkovTransitionStateSystem,
)
from kennel.engine.systems.network import ( from kennel.engine.systems.network import (
EntityPositionUpdateEvent, EntityPositionUpdateEvent,
EntityBornEvent,
EntityDeathEvent,
Event, Event,
EventType, EventType,
NetworkSystem, NetworkSystem,
UpstreamEventProcessor, UpstreamEventProcessor,
) )
from kennel.kennelcats import KennelCatService, KennelCat
from kennel.engine.systems.system import SystemManager from kennel.engine.systems.system import SystemManager
from kennel.engine.systems.world import WorldSystem from kennel.engine.systems.world import WorldSystem
@ -67,76 +58,12 @@ class KennelEventProcessor(UpstreamEventProcessor):
return event return event
class KennelCatsManager: system_manager.add_system(WorldSystem(config.WORLD_WIDTH, config.WORLD_HEIGHT))
kennel_cat_service: KennelCatService system_manager.add_system(
entity_manager: EntityManager NetworkSystem(KennelEventProcessor()),
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}")
logger.info(f"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)
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 = 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]: def create_session_controllable_entities(session: str) -> List[Entity]:

View File

@ -37,10 +37,10 @@ class KennelCat:
class KennelCatService: class KennelCatService:
def __init__(self, hc_endpoint: str): def __init__(self, endpoint: str):
self.hc_endpoint = hc_endpoint self.endpoint = endpoint
def get_kennel(self) -> List[KennelCat]: def get_kennel(self) -> List[KennelCat]:
response = requests.get(f"{self.hc_endpoint}/kennel") response = requests.get(f"{self.endpoint}/kennel")
response.raise_for_status() response.raise_for_status()
return [KennelCat.from_dict(cat) for cat in response.json()] return [KennelCat.from_dict(cat) for cat in response.json()]

View File

@ -30,7 +30,6 @@ from kennel.kennel import (
create_session_controllable_entities, create_session_controllable_entities,
entity_manager, entity_manager,
kennel, kennel,
kennel_cats_manager,
system_manager, system_manager,
) )
@ -44,14 +43,12 @@ loop = asyncio.get_event_loop()
async def startup_event(): async def startup_event():
logger.info("Starting Kennel...") logger.info("Starting Kennel...")
loop.create_task(kennel.run()) loop.create_task(kennel.run())
loop.create_task(kennel_cats_manager.start())
@app.on_event("shutdown") @app.on_event("shutdown")
async def shutdown_event(): async def shutdown_event():
logger.info("Stopping Kennel...") logger.info("Stopping Kennel...")
kennel.stop() kennel.stop()
kennel_cats_manager.stop()
loop.stop() loop.stop()
logger.info("Kennel stopped") logger.info("Kennel stopped")

View File

@ -4,9 +4,10 @@
"version": "0.1.0", "version": "0.1.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite",
"build": "tsc && vite build", "build": "tsc && vite build",
"preview": "vite preview", "preview": "vite preview",
"dev": "./node_modules/nodemon/bin/nodemon.js --watch './src/**/*' -e ts,html --exec \"npm run build\"" "watch": "./node_modules/nodemon/bin/nodemon.js --watch './src/**/*' -e ts,html --exec \"npm run build\""
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-inject": "^5.0.5", "@rollup/plugin-inject": "^5.0.5",

View File

@ -1,14 +1,12 @@
import { import {
Component, Component,
ComponentType, ComponentType,
PositionComponent,
RenderableComponent, RenderableComponent,
TrailingPositionComponent, TrailingPositionComponent,
} from "./component"; } from "./component";
export enum EntityType { export enum EntityType {
LASER = "LASER", LASER = "LASER",
CAT = "CAT",
} }
export interface Entity { export interface Entity {
@ -30,16 +28,3 @@ export const create_laser = (base: Entity) => {
base.components[ComponentType.RENDERABLE] = renderable; base.components[ComponentType.RENDERABLE] = renderable;
return base; return base;
}; };
export const create_cat = (base: Entity) => {
const renderable: RenderableComponent = {
name: ComponentType.RENDERABLE,
};
base.components[ComponentType.RENDERABLE] = renderable;
base.components[ComponentType.POSITION] = {
component: ComponentType.POSITION,
x: Math.random() * 1_000,
y: Math.random() * 1_000,
} as unknown as PositionComponent; // TODO: hack
return base;
};

View File

@ -3,7 +3,7 @@ import {
PositionComponent, PositionComponent,
TrailingPositionComponent, TrailingPositionComponent,
} from "./component"; } from "./component";
import { create_cat, create_laser, Entity, EntityType } from "./entity"; import { create_laser, Entity, EntityType } from "./entity";
import { import {
EntityBornEvent, EntityBornEvent,
EntityDeathEvent, EntityDeathEvent,
@ -110,9 +110,6 @@ export class NetworkSystem extends System {
if (entity.entity_type === EntityType.LASER) { if (entity.entity_type === EntityType.LASER) {
return create_laser(entity); return create_laser(entity);
} }
if (entity.entity_type === EntityType.CAT) {
return create_cat(entity);
}
return entity; return entity;
} }

View File

@ -1,8 +1,4 @@
import { import { ComponentType, TrailingPositionComponent } from "./component";
ComponentType,
PositionComponent,
TrailingPositionComponent,
} from "./component";
import { Game } from "./game"; import { Game } from "./game";
import { System, SystemType } from "./system"; import { System, SystemType } from "./system";
import { drawLaserPen } from "laser-pen"; import { drawLaserPen } from "laser-pen";
@ -29,17 +25,6 @@ export class RenderSystem extends System {
] as TrailingPositionComponent; ] as TrailingPositionComponent;
if (trailing_position.trails.length < 3) return; if (trailing_position.trails.length < 3) return;
drawLaserPen(ctx, trailing_position.trails); drawLaserPen(ctx, trailing_position.trails);
return;
}
if (ComponentType.POSITION in entity.components) {
const position = entity.components[
ComponentType.POSITION
] as PositionComponent;
ctx.beginPath();
ctx.arc(position.x, position.y, 50, 0, 2 * Math.PI);
ctx.stroke();
return;
} }
}); });
} }

View File

@ -28,11 +28,11 @@ $(async () => {
const publisher: EventPublisher = new WebsocketEventPublisher(ws); const publisher: EventPublisher = new WebsocketEventPublisher(ws);
const network_system = new NetworkSystem(queue, publisher); const network_system = new NetworkSystem(queue, publisher);
const gamecanvas = $("#gamecanvas").get(0)! as HTMLCanvasElement; const gamecanvas = $("#gamecanvas").get(0)!;
const input_system = new InputSystem(publisher, gamecanvas); const input_system = new InputSystem(publisher, gamecanvas);
setDelay(500); setDelay(1_000);
const render_system = new RenderSystem(gamecanvas); const render_system = new RenderSystem(gamecanvas as HTMLCanvasElement);
const trailing_position = new TrailingPositionSystem(drainPoints); const trailing_position = new TrailingPositionSystem(drainPoints);