game_assetsο
GameGirl Color β Per-game asset manager.
Stores labeled image URLs/references per game session in Redis. Assets are categorized (title_screen, enemy, character, item, background, ui, special) and can be referenced by the LLM during game narration. # π¨π CORRUPTED ASSET REGISTRY
- game_assets.resolve_asset_path(relative_path)[source]ο
Resolve a relative asset path under the project root, blocking traversal.
Joins
relative_pathontoPROJECT_ROOT(this moduleβs own directory) and then verifies, after symlink/.. resolution, that the result still lives inside that root. This keeps asset lookups portable across machines (no hardcoded absolute paths) while preventing directory-traversal escapes such as../../etc/passwd. Touches the filesystem only via path resolution; it does not open or create anything.Exercised by
tests/test_game_engine_remediation.py(both the happy path and the traversal-rejection case); no production callers were found via grep.- Parameters:
relative_path (
str) β A path fragment to resolve relative to the project root.- Returns:
The resolved path object located within
PROJECT_ROOT.- Return type:
- Raises:
PermissionError β If the resolved path would fall outside
PROJECT_ROOT.
- class game_assets.GameAsset(name, category, url, uploaded_by='', turn_added=0, created_at=<factory>)[source]ο
Bases:
objectA labeled image asset belonging to a single game session.
Lightweight dataclass record describing one uploaded image (its
name,category, imageurl, who uploaded it, the game turn it was added on, and a creation timestamp). Instances are serialized to and from the JSON list stored at thegame:assets:{game_id}Redis string viato_dict()andfrom_dict(), and surfaced to the LLM during narration throughget_asset_summary(). The category must be one ofVALID_CATEGORIES; validation is enforced byupload_asset()rather than in__init__.Constructed by
upload_asset()(new uploads) andget_assets()(rehydration from Redis).- Parameters:
- to_dict()[source]ο
Serialize this asset to a plain JSON-safe dict.
Delegates to
dataclasses.asdict()to flatten every field (name,category,url,uploaded_by,turn_added,created_at) into a dict. The result is what gets persisted to thegame:assets:{game_id}Redis string viajson.dumps(). No side effects.Called by
upload_asset(), which appends the dict to the asset list before writing it back to Redis.
- classmethod from_dict(d)[source]ο
Reconstruct a
GameAssetfrom a stored dict, ignoring extras.Filters d down to the dataclassβs declared fields (via
cls.__dataclass_fields__) before constructing the instance, so any stray or legacy keys read back from Redis are dropped rather than raising aTypeError. Missing optional fields fall back to their dataclass defaults. No side effects.Called by
get_assets()when deserializing each entry of the JSON list loaded from thegame:assets:{game_id}Redis key.
- async game_assets.upload_asset(game_id, name, category, url, user_id='', turn=0, redis=None)[source]ο
Register or update a named image asset for a game session.
Validates the category against
VALID_CATEGORIES, loads the existing asset list from thegame:assets:{game_id}Redis string, and either updates an asset with a matching (case-insensitive) name in place or appends a newGameAsset(serialized viaGameAsset.to_dict()). The updated list is written back withredis.set. New uploads are capped at_MAX_ASSETS; once that limit is hit, creation is refused so the registry cannot grow without bound. All Redis errors are caught and returned as anerrorpayload rather than raised.Called by the asset-producing tools and UI:
tools/game_asset_upload.py,tools/grok_imagine.py,tools/suno_music.py, andgame_ui/components.py.- Parameters:
game_id (
str) β Session identifier used to build the Redis key.name (
str) β Asset label; collisions update the existing asset.category (
str) β Asset category; must be inVALID_CATEGORIES(case-insensitive).url (
str) β Image URL/reference to store.user_id (
str) β Uploader identifier, recorded on the asset.turn (
int) β Game turn the asset was added on.redis (
Any) β Async Redis client;Noneshort-circuits to an error payload.
- Returns:
On success, a dict with
successplusaction("created"or"updated") and asset details; otherwise a dict with anerrormessage (bad category, limit reached, no Redis, or write failure).- Return type:
- async game_assets.get_assets(game_id, category=None, redis=None)[source]ο
Load a gameβs assets from Redis, optionally filtered by category.
Reads the JSON list at the
game:assets:{game_id}Redis string and rehydrates each entry into aGameAssetviaGameAsset.from_dict(). Whencategoryis given, the result is narrowed to assets whose category matches (case-insensitive). Read-only: nothing is written back. Any decode or connection error is logged and reported as an empty list so callers degrade gracefully.Called by
get_asset_by_name()andget_asset_summary()within this module, and bygame_ui/components.pyto render the asset list.- Parameters:
- Returns:
The matching assets, or an empty list when absent or on any error.
- Return type:
- async game_assets.get_asset_by_name(game_id, name, redis=None)[source]ο
Look up a single game asset by name (case-insensitive).
Fetches the full asset list via
get_assets()and returns the firstGameAssetwhose name matchesnameignoring case, orNoneif no such asset exists. Read-only convenience wrapper used when a tool needs to reference one specific asset rather than the whole set.Called by
tools/grok_imagine.pyandtools/compose_gameboard.pyto pull a named image (e.g. as an img2img reference or board element).
- async game_assets.delete_asset(game_id, name, redis=None)[source]ο
Remove a named asset from a gameβs asset list in Redis.
Loads the raw JSON list at the
game:assets:{game_id}Redis string, rebuilds it excluding any entry whose name matchesname(case-insensitive), and writes the pruned list back withredis.set. If nothing matched, the list is unchanged andFalseis returned without a write. Any Redis/decode error is logged and treated as a failed delete.No callers were found via grep; this is a public maintenance helper for the asset registry (e.g. to clear an asset before re-uploading).
- async game_assets.get_asset_summary(game_id, redis=None)[source]ο
Render a compact, category-grouped asset listing for LLM context.
Loads the gameβs assets via
get_assets(), buckets them by category, and emits a small human-readable block (a[GAME ASSETS]header followed by oneCATEGORY: name, nameline per category, sorted) suitable for injection into the modelβs prompt so it can reference uploaded images during narration. Returns a fixed placeholder string when no assets exist. Read-only; no Redis writes.No callers were found via grep; intended for prompt-context assembly that surfaces the asset registry to the LLM.