Add game loop skeleton for periodic tick processing
This commit is contained in:
parent
d220835f7d
commit
075a6ce303
2 changed files with 45 additions and 0 deletions
|
|
@ -15,12 +15,15 @@ import mudlib.commands.fly
|
|||
import mudlib.commands.look
|
||||
import mudlib.commands.movement
|
||||
import mudlib.commands.quit
|
||||
from mudlib.effects import clear_expired
|
||||
from mudlib.player import Player, players
|
||||
from mudlib.world.terrain import World
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
PORT = 6789
|
||||
TICK_RATE = 10 # ticks per second
|
||||
TICK_INTERVAL = 1.0 / TICK_RATE
|
||||
|
||||
# Module-level world instance, generated once at startup
|
||||
_world: World | None = None
|
||||
|
|
@ -34,6 +37,18 @@ def load_world_config(world_name: str = "earth") -> dict:
|
|||
return tomllib.load(f)
|
||||
|
||||
|
||||
async def game_loop() -> None:
|
||||
"""Run periodic game tasks at TICK_RATE ticks per second."""
|
||||
log.info("game loop started (%d ticks/sec)", TICK_RATE)
|
||||
while True:
|
||||
t0 = asyncio.get_event_loop().time()
|
||||
clear_expired()
|
||||
elapsed = asyncio.get_event_loop().time() - t0
|
||||
sleep_time = TICK_INTERVAL - elapsed
|
||||
if sleep_time > 0:
|
||||
await asyncio.sleep(sleep_time)
|
||||
|
||||
|
||||
def find_passable_start(world: World, start_x: int, start_y: int) -> tuple[int, int]:
|
||||
"""Find a passable tile starting from (start_x, start_y) and searching outward.
|
||||
|
||||
|
|
@ -183,10 +198,13 @@ async def run_server() -> None:
|
|||
)
|
||||
log.info("listening on 127.0.0.1:%d", PORT)
|
||||
|
||||
loop_task = asyncio.create_task(game_loop())
|
||||
|
||||
try:
|
||||
while True:
|
||||
await asyncio.sleep(3600)
|
||||
except KeyboardInterrupt:
|
||||
log.info("shutting down...")
|
||||
loop_task.cancel()
|
||||
server.close()
|
||||
await server.wait_closed()
|
||||
|
|
|
|||
|
|
@ -103,3 +103,30 @@ def test_load_world_config_missing():
|
|||
"""Config loader raises FileNotFoundError for nonexistent world."""
|
||||
with pytest.raises(FileNotFoundError):
|
||||
server.load_world_config("nonexistent")
|
||||
|
||||
|
||||
def test_tick_constants():
|
||||
"""Tick rate and interval are configured correctly."""
|
||||
assert server.TICK_RATE == 10
|
||||
assert server.TICK_INTERVAL == pytest.approx(0.1)
|
||||
|
||||
|
||||
def test_game_loop_exists():
|
||||
"""Game loop is an async callable."""
|
||||
assert callable(server.game_loop)
|
||||
assert asyncio.iscoroutinefunction(server.game_loop)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_game_loop_calls_clear_expired():
|
||||
"""Game loop calls clear_expired each tick."""
|
||||
with patch("mudlib.server.clear_expired") as mock_clear:
|
||||
task = asyncio.create_task(server.game_loop())
|
||||
await asyncio.sleep(0.25)
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
assert mock_clear.call_count >= 1
|
||||
|
|
|
|||
Loading…
Reference in a new issue