Skip to content

Commit 165802d

Browse files
refactor: launcher + demo — remove stale 47-module hardcoded list, full 8-phase demo
launcher.py: replaced brittle 47-module hardcoded reload list with sys.modules pop pattern (v2.2.0 canonical nuclear reload). Updates tool count from 171→355 in docstring. demo.py: rewrote 4-step demo as full 8-phase pipeline (recon, health, scaffold, layout, verse deploy, build, error loop, publish audit + snapshot). Adds texture audit and activity stats bonus steps. Showcases the complete AI game-building workflow. .gitignore: added tools_dump.json (stale cached output, not version-controlled).
1 parent cffcc81 commit 165802d

3 files changed

Lines changed: 146 additions & 96 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ credentials.json
164164

165165
# ─── UEFN Toolbelt Local Data ─────────────────────────────────────────────────
166166
docs/api_level_classes_schema.json
167+
tools_dump.json
167168

168169
# UEFN Internal Stubs (Proprietary / Large Files)
169170
unreal_engine_blueprint.pyi

demo.py

Lines changed: 130 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,168 @@
1-
# UEFN TOOLBELT — Project Setup Demo
1+
# UEFN TOOLBELT — Full Pipeline Demo
22
# ====================================
33
# Run this in the UEFN Python REPL (Output Log > Python (REPL) tab)
4-
# to see the full AI project setup workflow live.
4+
# to see the complete 8-phase AI game-building pipeline live.
55
#
66
# Prerequisites:
7-
# 1. UEFN is open with any level loaded
7+
# 1. UEFN is open with a BLANK TEMPLATE LEVEL (not a production project)
88
# 2. UEFN Toolbelt is installed (Content/Python/ contains UEFN_Toolbelt/)
9-
# 3. Run the nuclear reload if you just installed:
9+
# 3. Nuclear reload first — paste this into the REPL:
1010
# import sys; [sys.modules.pop(k) for k in list(sys.modules) if "UEFN_Toolbelt" in k]; import UEFN_Toolbelt as tb; tb.register_all_tools(); tb.launch_qt()
1111
#
12-
# Then paste each block below into the REPL one at a time.
13-
# Each block must complete before running the next (Quirk #22).
12+
# Each block below is a separate paste into the REPL.
13+
# Wait for each block to complete before running the next (Quirk #22).
14+
# ⚠️ Phases 1–3 are read-only. Phase 4 onward modifies the level.
1415

1516
import UEFN_Toolbelt as tb
1617

1718
# ------------------------------------------------------------------
18-
# STEP 1Scaffold the project + deploy Verse game manager
19+
# PHASE 0RECON: Know the level before touching it
1920
#
20-
# What happens:
21-
# - Creates 56 professional folders in Content Browser
22-
# (/Game/MyGame/Maps, /Meshes, /Materials, /Verse, etc.)
23-
# - Generates a wired MyGameManager Verse device skeleton
24-
# - Deploys it directly to your project's Verse directory
21+
# Exports every actor in the level (transforms, class, folder,
22+
# bounds, asset path, tags) to Saved/UEFN_Toolbelt/world_state.json.
23+
# The summary block at the top shows class counts + folder map —
24+
# read that first for a quick level overview.
25+
# ------------------------------------------------------------------
26+
result = tb.run("world_state_export")
27+
print(f"Actors exported: {result.get('actor_count')}")
28+
print(f"Output: {result.get('output_path')}")
29+
30+
31+
# ------------------------------------------------------------------
32+
# PHASE 1 — RECON: Build the device palette
33+
#
34+
# Scans the Asset Registry for every Creative device Blueprint
35+
# available in Fortnite (not just what's placed — the full catalog).
36+
# Writes device_catalog.json — Claude reads this to pick devices.
37+
# ------------------------------------------------------------------
38+
result = tb.run("device_catalog_scan")
39+
print(f"Devices found: {result.get('device_count')}")
40+
print(f"Output: {result.get('output_path')}")
41+
42+
43+
# ------------------------------------------------------------------
44+
# PHASE 2 — HEALTH CHECK: Level audit before building
2545
#
26-
# Run this first. Wait for it to return before step 2.
46+
# Runs 6 audit categories: actors, memory, assets, naming,
47+
# LODs, performance. Returns a score 0–100 with grade (A+…F)
48+
# and ordered list of issues to fix before publishing.
49+
# ------------------------------------------------------------------
50+
result = tb.run("level_health_report")
51+
print(f"Health score: {result.get('score')}/100 ({result.get('grade')})")
52+
print(f"Status: {result.get('overall_status')}")
53+
for issue in result.get("top_issues", [])[:5]:
54+
print(f" • {issue}")
55+
56+
57+
# ------------------------------------------------------------------
58+
# PHASE 3 — SCAFFOLD: Professional folder structure + Verse skeleton
59+
#
60+
# Creates 56 content folders in the Content Browser and generates
61+
# a wired Verse game manager skeleton (creative_device subclass,
62+
# @editable refs, OnBegin stub) deployed to the Verse source dir.
2763
# ------------------------------------------------------------------
2864
result = tb.run("project_setup", project_name="MyGame")
2965
print("Verse file:", result.get("verse_path"))
3066
print("Next steps:", result.get("next_steps"))
3167

3268

3369
# ------------------------------------------------------------------
34-
# STEP 2 — Spawn a symmetrical Red vs Blue arena
70+
# PHASE 4LAYOUT: Spawn a symmetrical competitive arena
3571
#
36-
# What happens:
37-
# - Places 493 actors in the viewport (floor tiles, walls, platform)
38-
# - Red spawn cluster at X+ / Blue spawn cluster at X-
39-
# - Fully undoable with Ctrl+Z
40-
#
41-
# Run AFTER step 1 returns. Separate call required (Quirk #22).
72+
# Places floor tiles, walls, and ramp platforms in a symmetric
73+
# Red vs Blue layout. Fully undoable (Ctrl+Z).
74+
# Change size to "small", "large", or "extra_large".
4275
# ------------------------------------------------------------------
43-
result = tb.run("arena_generate", size="medium")
76+
result = tb.run("arena_generate", size="medium", apply_team_colors=True)
4477
print(f"Arena: {result.get('placed')} actors placed")
4578
print(f" Red spawns: {result.get('red_spawns')}")
4679
print(f" Blue spawns: {result.get('blue_spawns')}")
4780

4881

4982
# ------------------------------------------------------------------
50-
# STEP 3Build Verse (one manual click)
83+
# PHASE 5VERSE: Deploy a battle-tested game template
5184
#
52-
# In UEFN: Verse menu -> Build Verse Code
53-
# Wait for the build to complete, then run step 4.
85+
# Lists available templates first, then deploys one.
86+
# Templates are production-tested skeletons: elimination scoring,
87+
# zone capture, round flow, item spawner cycle, countdown race.
88+
# Claude reads verse_template_list, picks the right one, fills in
89+
# device labels from world_state.json, then deploys.
5490
# ------------------------------------------------------------------
91+
result = tb.run("verse_template_list")
92+
print("Available templates:")
93+
for t in result.get("templates", []):
94+
print(f" {t['name']:30s} {t['description']}")
95+
96+
# Deploy the elimination scoring template
97+
result = tb.run("verse_template_deploy",
98+
name="elimination_scoring",
99+
filename="game_manager.verse",
100+
overwrite=True)
101+
print("Deployed:", result.get("verse_path"))
55102

56103

57104
# ------------------------------------------------------------------
58-
# STEP 4Close the error loop
105+
# PHASE 6BUILD: Trigger Verse compilation
59106
#
60-
# Reads the build log, extracts any errors, returns file content
61-
# so Claude (or you) can fix and redeploy in one shot.
62-
# If the build succeeded, returns build_status: SUCCESS.
107+
# One manual step: In UEFN, click Verse → Build Verse Code.
108+
# Wait for the build to finish, then run Phase 7.
109+
# ------------------------------------------------------------------
110+
# [User clicks Build Verse in UEFN menu]
111+
112+
113+
# ------------------------------------------------------------------
114+
# PHASE 7 — ERROR LOOP: Read errors, fix, redeploy
115+
#
116+
# Reads the build log, extracts structured errors (file, line, col,
117+
# message, error_type, fix_hint) + full content of every erroring
118+
# .verse file so Claude can fix and redeploy in one shot.
119+
# Repeat until build_status == "SUCCESS".
63120
# ------------------------------------------------------------------
64121
result = tb.run("verse_patch_errors")
65122
print("Build status:", result.get("build_status"))
66123
print("Errors:", result.get("error_count"))
67-
if result.get("errors"):
68-
for e in result["errors"]:
69-
print(f" Line {e['line']}: {e['message']}")
124+
if result.get("errors_by_file"):
125+
for filepath, errors in result["errors_by_file"].items():
126+
print(f"\n {filepath}:")
127+
for e in errors[:3]:
128+
print(f" Line {e['line']}: [{e.get('error_type','?')}] {e['message']}")
129+
if e.get("fix_hint"):
130+
print(f" Fix: {e['fix_hint']}")
131+
132+
133+
# ------------------------------------------------------------------
134+
# PHASE 8 — VERIFY + CHECKPOINT
135+
#
136+
# Re-export world state to confirm level matches design intent,
137+
# run a publish readiness audit (actor budget, required devices,
138+
# Verse build status, memory, redirectors), then save a snapshot.
139+
# ------------------------------------------------------------------
140+
result = tb.run("publish_audit")
141+
print(f"Publish ready: {result.get('overall_status')}")
142+
print(f"Score: {result.get('score')}")
143+
for step in result.get("next_steps", [])[:5]:
144+
print(f" → {step}")
145+
146+
result = tb.run("snapshot_save", name="demo_v1")
147+
print(f"Checkpoint saved: {result.get('snapshot_name')}")
148+
print(f" Actors: {result.get('actor_count')}")
149+
print(f" Path: {result.get('output_path')}")
150+
151+
152+
# ------------------------------------------------------------------
153+
# BONUS — TEXTURE AUDIT: Catch oversized textures before publishing
154+
# ------------------------------------------------------------------
155+
result = tb.run("texture_audit")
156+
print(f"Textures audited: {result.get('total_audited')}")
157+
for t in result.get("oversized", [])[:5]:
158+
print(f" ⚠ {t['name']} {t.get('width')}×{t.get('height')} {t.get('compression')}")
159+
160+
161+
# ------------------------------------------------------------------
162+
# BONUS — ACTIVITY LOG: Review everything the toolbelt did this session
163+
# ------------------------------------------------------------------
164+
result = tb.run("toolbelt_activity_stats")
165+
print(f"Tools called: {result.get('total_calls')}")
166+
print(f"Error rate: {result.get('error_rate_pct')}%")
167+
print(f"Slowest: {result.get('slowest_tool')}")
168+
print(f"Most called: {result.get('most_called_tool')}")

launcher.py

Lines changed: 15 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
1515
The launcher:
1616
1. Adds Content/Python/ to sys.path
17-
2. Hot-reloads all toolbelt modules (safe to run repeatedly during development)
18-
3. Registers all tool modules (171 tools)
17+
2. Clears all cached Toolbelt modules (safe nuclear reload)
18+
3. Registers all 355 tools
1919
4. Opens the PySide6 tabbed dashboard (falls back gracefully if PySide6 missing)
2020
2121
Install PySide6 (one-time, run OUTSIDE UEFN in a regular terminal):
@@ -27,83 +27,33 @@
2727

2828
import sys
2929
import os
30-
import importlib
3130
import unreal
3231

3332
# ── 1. Path setup ─────────────────────────────────────────────────────────────
3433

35-
_CONTENT_DIR = unreal.Paths.project_content_dir()
36-
_PYTHON_ROOT = os.path.join(_CONTENT_DIR, "Python")
34+
_CONTENT_DIR = unreal.Paths.project_content_dir()
35+
_PYTHON_ROOT = os.path.join(_CONTENT_DIR, "Python")
3736

3837
if _PYTHON_ROOT not in sys.path:
3938
sys.path.insert(0, _PYTHON_ROOT)
4039

41-
# ── 2. Hot-reload every module (safe to run repeatedly while developing) ───────
40+
# ── 2. Nuclear reload — clears all cached modules so every import is fresh ───
41+
#
42+
# Safe to run repeatedly during development. The hardcoded module list approach
43+
# was removed in v2.2.0 — it was brittle (missed new modules) and caused stale
44+
# reload bugs. The sys.modules pop pattern is authoritative and always complete.
45+
#
46+
# ⚠️ If this was the FIRST time deploying a new module (new entry in
47+
# tools/__init__.py), do a full UEFN restart instead — nuclear reload can
48+
# crash on brand-new modules. See docs/UEFN_QUIRKS.md Quirk #26.
4249

43-
_ALL_MODULES = [
44-
# Core
45-
"UEFN_Toolbelt",
46-
"UEFN_Toolbelt.core",
47-
"UEFN_Toolbelt.registry",
48-
"UEFN_Toolbelt.tools",
49-
"UEFN_Toolbelt.dashboard_pyside6",
50-
# Tools
51-
"UEFN_Toolbelt.tools.material_master",
52-
"UEFN_Toolbelt.tools.arena_generator",
53-
"UEFN_Toolbelt.tools.spline_prop_placer",
54-
"UEFN_Toolbelt.tools.bulk_operations",
55-
"UEFN_Toolbelt.tools.verse_device_editor",
56-
"UEFN_Toolbelt.tools.smart_importer",
57-
"UEFN_Toolbelt.tools.verse_snippet_generator",
58-
"UEFN_Toolbelt.tools.text_painter",
59-
"UEFN_Toolbelt.tools.asset_renamer",
60-
"UEFN_Toolbelt.tools.foliage_tools",
61-
"UEFN_Toolbelt.tools.lod_tools",
62-
"UEFN_Toolbelt.tools.spline_to_verse",
63-
"UEFN_Toolbelt.tools.project_scaffold",
64-
"UEFN_Toolbelt.tools.memory_profiler",
65-
"UEFN_Toolbelt.tools.api_explorer",
66-
"UEFN_Toolbelt.tools.prop_patterns",
67-
"UEFN_Toolbelt.tools.reference_auditor",
68-
"UEFN_Toolbelt.tools.level_snapshot",
69-
"UEFN_Toolbelt.tools.asset_tagger",
70-
"UEFN_Toolbelt.tools.screenshot_tools",
71-
"UEFN_Toolbelt.tools.mcp_bridge",
72-
"UEFN_Toolbelt.tools.measurement_tools",
73-
"UEFN_Toolbelt.tools.localization_tools",
74-
"UEFN_Toolbelt.tools.integration_test",
75-
"UEFN_Toolbelt.tools.api_capability_crawler",
76-
"UEFN_Toolbelt.tools.smart_organizer",
77-
"UEFN_Toolbelt.tools.system_perf",
78-
"UEFN_Toolbelt.tools.asset_importer",
79-
"UEFN_Toolbelt.tools.procedural_geometry",
80-
"UEFN_Toolbelt.tools.text_voxelizer",
81-
"UEFN_Toolbelt.tools.verse_schema",
82-
"UEFN_Toolbelt.tools.system_build",
83-
"UEFN_Toolbelt.tools.foliage_converter",
84-
"UEFN_Toolbelt.tools.entity_kits",
85-
"UEFN_Toolbelt.tools.selection_utils",
86-
"UEFN_Toolbelt.tools.project_admin",
87-
"UEFN_Toolbelt.tools.lighting_mastery",
88-
"UEFN_Toolbelt.tools.sequencer_tools",
89-
"UEFN_Toolbelt.tools.sim_device_proxy",
90-
"UEFN_Toolbelt.tools.config_tools",
91-
"UEFN_Toolbelt.tools.verse_device_graph",
92-
"UEFN_Toolbelt.tools.plugin_manager",
93-
]
94-
95-
for _mod in _ALL_MODULES:
96-
if _mod in sys.modules:
97-
try:
98-
importlib.reload(sys.modules[_mod])
99-
except Exception as _e:
100-
unreal.log_warning(f"[TOOLBELT] Reload skipped for {_mod}: {_e}")
50+
[sys.modules.pop(k) for k in list(sys.modules) if "UEFN_Toolbelt" in k]
10151

10252
# ── 3. Launch ─────────────────────────────────────────────────────────────────
10353

10454
try:
10555
import UEFN_Toolbelt as _tb
106-
_tb.launch() # registers all tools → opens Qt dashboard
56+
_tb.launch() # register_all_tools() → opens Qt dashboard
10757
except Exception as _err:
10858
unreal.log_error(f"[TOOLBELT] Launch failed: {_err}")
10959
raise

0 commit comments

Comments
 (0)