ProductionGame/backend/catalog_loader.py

79 lines
3 KiB
Python

"""
Production - The Idle Game: Sector & Product Registry Loader
Parses modular JS sector files into authoritative Python dictionaries.
Ensures single-source-of-truth between frontend definitions and backend simulation.
"""
import os
import glob
import json
import re
def js_to_dict(js_code: str):
"""Converts a standard JavaScript object/array literal to a Python dict/list."""
# Remove single-line comments
clean = re.sub(r'//.*', '', js_code)
# Quote unquoted property keys: e.g. id: 'wheat' -> "id": "wheat"
clean = re.sub(r'([{,]\s*)([a-zA-Z0-9_]+)\s*:', r'\1"\2":', clean)
# Replace single quotes with double quotes
clean = clean.replace("'", '"')
# Remove trailing commas before } or ]
clean = re.sub(r',\s*([}\]])', r'\1', clean)
return json.loads(clean)
def get_sectors_directory() -> str:
"""Finds the directory containing sector .js files whether in local dev or Docker."""
candidates = [
os.path.join(os.path.dirname(__file__), "..", "frontend", "js", "data", "sectors"), # Local repo dev
os.path.join(os.path.dirname(__file__), "sectors"), # Docker mount /app/sectors
"/app/sectors", # Standard container mount
os.path.abspath("frontend/js/data/sectors")
]
for c in candidates:
if os.path.isdir(c) and glob.glob(os.path.join(c, "*.js")):
return os.path.abspath(c)
return ""
def load_all_sectors_and_products():
"""
Scans the sectors directory, parsing all modular JS sector files.
Returns:
(sectors_dict, products_dict)
"""
sec_dir = get_sectors_directory()
sectors = {}
products = {}
if not sec_dir:
print("[WARNING] No sector files directory found! Falling back to empty catalog.")
return sectors, products
for file_path in glob.glob(os.path.join(sec_dir, "*.js")):
try:
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
# Parse Sector Object
sec_match = re.search(r'export\s+const\s+[A-Z_]+_SECTOR\s*=\s*({[\s\S]*?});', content)
if sec_match:
sec_obj = js_to_dict(sec_match.group(1))
sectors[sec_obj["id"]] = sec_obj
# Parse Products Array
prod_match = re.search(r'export\s+const\s+[A-Z_]+_PRODUCTS\s*=\s*(\[[\s\S]*?\]);', content)
if prod_match:
prod_list = js_to_dict(prod_match.group(1))
for p in prod_list:
products[p["id"]] = p
except Exception as e:
print(f"[ERROR] Failed to parse sector file {file_path}: {e}")
return sectors, products
if __name__ == '__main__':
s, p = load_all_sectors_and_products()
print(f"Loaded {len(s)} sectors and {len(p)} products.")
for sid, sobj in s.items():
count = sum(1 for prod in p.values() if prod.get('sector') == sid)
print(f" - {sobj['name']} ({sid}): {count} commodities")