Content Pack System¶
MAID uses a content pack system to separate game content from engine infrastructure. Content packs can provide systems, commands, events, and data.
Content Pack Architecture¶
flowchart TB
subgraph Pack["Content Pack"]
M[Manifest<br/>name, version, deps]
SY[Systems]
CMD[Commands]
EV[Events]
SCH[Schemas]
HL[Lifecycle Hooks]
end
subgraph Engine["Game Engine"]
SM[System Manager]
CR[Command Registry]
EB[Event Bus]
DS[Document Store]
end
M --> Engine
SY --> SM
CMD --> CR
EV --> EB
SCH --> DS
HL --> Engine
Creating a Content Pack¶
from maid_engine.plugins.protocol import BaseContentPack
from maid_engine.plugins.manifest import ContentPackManifest
class MyContentPack(BaseContentPack):
"""Custom content pack example."""
@property
def manifest(self) -> ContentPackManifest:
return ContentPackManifest(
name="my-pack",
version="1.0.0",
display_name="My Pack",
description="Custom game content",
dependencies={"stdlib": ">=0.1.0"}, # Depends on stdlib
provides=["custom-feature"],
requires=["ecs", "event-bus"],
)
def get_dependencies(self) -> list[str]:
"""Packs that must be loaded before this one."""
return ["stdlib"]
def get_systems(self, world: World) -> list[System]:
"""ECS systems provided by this pack."""
return [
MyCustomSystem(world),
AnotherSystem(world),
]
def get_events(self) -> list[type[Event]]:
"""Event types defined by this pack."""
return [MyCustomEvent, AnotherEvent]
def register_commands(self, registry: CommandRegistry) -> None:
"""Commands provided by this pack."""
registry.register(my_command)
registry.register(another_command)
def register_document_schemas(self, store: DocumentStore) -> None:
"""Document schemas for persistence."""
store.register_schema("my_collection", MyModel)
async def on_load(self, engine: GameEngine) -> None:
"""Initialize when pack is loaded."""
# Load data files, connect to services, etc.
pass
async def on_unload(self, engine: GameEngine) -> None:
"""Cleanup when pack is unloaded."""
# Save state, close connections, etc.
pass
Loading Content Packs¶
from maid_engine.core.engine import GameEngine
from maid_stdlib.pack import StdlibContentPack
from maid_classic_rpg.pack import ClassicRPGContentPack
# Create engine
engine = GameEngine(settings)
# Load packs in dependency order
engine.load_content_pack(StdlibContentPack())
engine.load_content_pack(ClassicRPGContentPack())
# Start engine (calls on_load for each pack)
await engine.start()
Content Pack Discovery¶
Packs can be discovered automatically via Python entry points:
# In your pack's pyproject.toml
[project.entry-points."maid.content_packs"]
my-pack = "my_package.pack:MyContentPack"
Or from local directories:
from maid_engine.plugins.loader import discover_content_packs, ContentPackLoader
# Discover packs from entry points and directories
packs = discover_content_packs(
search_paths=[Path("./custom_packs")],
use_entry_points=True,
)
# Use loader for dependency resolution
loader = ContentPackLoader()
for pack in packs:
loader.register(pack)
# Get packs in dependency order
ordered = loader.resolve_load_order()
Pack Dependency Graph¶
flowchart BT
subgraph Available["Available Packs"]
STD[stdlib]
CRP[classic-rpg]
MY[my-custom-pack]
end
CRP -->|depends on| STD
MY -->|depends on| STD
MY -.->|optional| CRP