6.3 KiB
as9-island-dominion Architecture
Project Identity
- Directory:
as9 - CMake project name:
as9-island-dominion - Executable target name:
as9-island-dominion - Game name:
Island Dominion
High-Level Structure
The project is split into five main areas:
src/ecs/: the homegrown ECS, following the storage and lookup patterns fromas8src/components/: plain data components used by the ECSsrc/systems/: free-function systems that execute in a fixed order frommainsrc/game/: authoritative game-state structs and pure rules codesrc/net/: ASIO TCP networking and protocol serialization
ECS Conventions
The ECS should stay very close to as8.
Entityis a numeric ID type (size_t)- Component type IDs come from a single global component counter in a
.cppfile - Each component type uses sparse-set style storage with:
datapacked_entitiessparse_indices- The central registry owns all component storages via a vector of base pointers, like
Contextinas8 - Systems are free functions, not inheritance-heavy classes
- Main owns the registry and executes systems explicitly in order each frame
as8 did not need entity destruction, but this game does. as9 should preserve the same sparse-set design while adding safe entity destruction and component removal for unit and fortress elimination.
Game State Split
Two representations exist on purpose:
- ECS state: runtime entities used for rendering, selection, and UI interactions
GameState: plain serializable structs used for rules, host authority, and network snapshots
Authoritative rules should operate on GameState, not directly on ECS entities. ECS entities are rebuilt or synchronized from snapshots/state transitions as needed.
Authoritative Networking Model
- The host is always player 1
- The client is always player 2
- Both players run the same executable
- The host owns the authoritative
GameState - The client never mutates the game directly; it sends intents to the host
- After every accepted state change, the host sends a fresh
GAME_STATEsnapshot to the client
This keeps validation and turn order in one place and avoids divergence between peers.
Networking Responsibilities
NetworkManager owns:
- the ASIO
io_context - host accept/connect logic
- async TCP read/write queues
- decoded inbound message queues for the game layer
- disconnect detection and recovery to the startup screen
Protocol messages should stay small and explicit:
CONNECT_REQUESTCONNECT_ACKGAME_STATEACTION_REQUESTCOMBAT_CHOICEGAME_OVER
TCP framing should use one consistent format throughout the project. A small length-prefixed binary format is preferred so snapshots remain predictable and easy to parse.
Privacy Rules
Hands are private information.
- A local player may always know their own hand
- A received remote snapshot must not expose the opponent's hand contents
- Public state still includes hand size, discard size, deck size, area control, units, fortresses, turn state, and combat state
Combat Flow
Combat is blocking.
- A move into an enemy-occupied area starts combat immediately
- No other actions are processed until the current combat resolves
- The host waits until both players have submitted a
COMBAT_CHOICE - After both choices arrive, the host resolves one combat round
- If both sides still have units, the host requests another round
- When combat ends, the host checks fortress capture/removal and win conditions, then broadcasts the updated snapshot
Rules Ownership
GameRules should provide pure functions for:
- board adjacency checks
- area controller calculation
- resource collection and cap enforcement
- action validation
- action application
- combat round resolution
- fortress capture/removal logic
- win-condition checks
Pure rules code keeps the host logic testable and makes snapshot synchronization simpler.
Rendering and UI Ownership
RenderSystemdraws the board, areas, units, fortresses, and highlights using raylib-cpp typesUISystemdraws the startup screen, HUD, combat overlay, disconnect notices, and game-over screenInputSystemtranslates local mouse/keyboard input into high-level actions or combat choices
The local host can apply validated local actions directly through the host game-logic path. The client instead packages equivalent intents into ACTION_REQUEST or COMBAT_CHOICE messages.
Frame Update Order
Each frame should follow a stable order:
- Pump network events and decode inbound messages
- Update local input and build pending local intents
- Apply host-side game logic if this instance is authoritative
- Update ECS/view state from the latest authoritative snapshot
- Draw world and UI
This order keeps render state consistent with the latest confirmed game state.
Build and Library Layout
- Root build file:
as9/CMakeLists.txt - Raylib wrapper comes from
../raylib-cppfollowing theas6pattern - ASIO lives in
as9/lib/asio - Include path should be
lib/asio/asio/include - Compile definitions should include:
ASIO_STANDALONEASIO_NO_DEPRECATED
Platform link requirements:
- Linux:
pthread - Windows:
ws2_32andmswsock
Initial Source Layout
Planned layout:
src/ecs/Entity.hppsrc/ecs/ComponentStorage.hppsrc/ecs/Registry.hppsrc/ecs/System.hppsrc/components/TransformComponent.hppsrc/components/RenderComponent.hppsrc/components/UnitComponent.hppsrc/components/FortressComponent.hppsrc/components/AreaComponent.hppsrc/components/PlayerComponent.hppsrc/components/CardComponent.hppsrc/components/NetworkComponent.hppsrc/systems/RenderSystem.hpp/.cppsrc/systems/InputSystem.hpp/.cppsrc/systems/NetworkSystem.hpp/.cppsrc/systems/GameLogicSystem.hpp/.cppsrc/systems/CombatSystem.hpp/.cppsrc/systems/UISystem.hpp/.cppsrc/game/GameState.hppsrc/game/GameRules.hpp/.cppsrc/game/CardDeck.hpp/.cppsrc/net/Protocol.hppsrc/net/NetworkManager.hpp/.cppsrc/main.cpp
Implementation Priorities
Build the project in this order:
- CMake and third-party wiring
- ECS infrastructure split from the
as8pattern - Serializable game state and rules
- Networking protocol and manager
- UI flow for startup, match, combat, disconnect, and game over
- Rendering polish and interaction cleanup