Merge of the two project copies into one self-contained plugin (the superset: variable_op + variables.py, full pcg_op runtime/declarative/preset ops, the CreateWidgetFloatAnimation widget tool, and full Voxel graph authoring). Made fully project- and machine-agnostic — no hardcoded paths: - New src/projectPaths.js auto-detects the host .uproject (walk-up), project name, Editor build target, log file, and engine install (EngineAssociation via launcher manifest/registry, else installed-engine scan). All overridable via UE_* env vars. - Rewired buildOrchestrator/insights/launcher/insightsExporter/server.js and the Python workers (cpp_scaffold, live_coding, apply_graph, console) off the old C:/Github/ihy, IHY*, E:/UE_Versions and UE_5.7 literals onto the resolver. - Voxel made optional: Build.cs auto-detects the Voxel plugin (env UE_MCP_WITH_VOXEL override) and the C++ compiles to stubs under WITH_MCP_VOXEL, so the module builds in projects without Voxel; .uplugin marks Voxel optional. - De-branded the agent-gateway and docs; scrubbed a leaked API key; excluded node_modules/Binaries/Intermediate/__pycache__/secrets from the repo. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
119 lines
3.6 KiB
Python
119 lines
3.6 KiB
Python
"""Console commands and CVars.
|
|
|
|
Ops:
|
|
run {command} — execute editor console command
|
|
get_cvar {name}
|
|
set_cvar {name, value}
|
|
list_cvars {prefix?} — search registered cvars by prefix (best-effort)
|
|
stat {name, enable?:true} — toggle stat command (`stat fps`, `stat unit`, ...)
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import traceback
|
|
|
|
try:
|
|
import unreal # type: ignore
|
|
except ImportError:
|
|
unreal = None
|
|
|
|
|
|
def _world():
|
|
return unreal.EditorLevelLibrary.get_editor_world()
|
|
|
|
|
|
def run(args):
|
|
cmd = args["command"]
|
|
try:
|
|
unreal.SystemLibrary.execute_console_command(_world(), cmd)
|
|
return {"ok": True, "command": cmd}
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
|
|
def get_cvar(args):
|
|
name = args["name"]
|
|
# No direct read API in stable Python; we issue ?-style print and grab from log.
|
|
try:
|
|
unreal.SystemLibrary.execute_console_command(_world(), name)
|
|
# Heuristic: also use ConsoleVariablesEditorFunctionLibrary if available
|
|
cvl = getattr(unreal, "ConsoleVariablesEditorFunctionLibrary", None)
|
|
if cvl and hasattr(cvl, "get_console_variable_string_value"):
|
|
return {"ok": True, "name": name, "value": cvl.get_console_variable_string_value(name)}
|
|
return {"ok": True, "name": name, "note": "value echoed to Output Log (no direct read API)"}
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
|
|
def set_cvar(args):
|
|
name = args["name"]
|
|
val = args["value"]
|
|
cmd = f"{name} {val}"
|
|
try:
|
|
unreal.SystemLibrary.execute_console_command(_world(), cmd)
|
|
return {"ok": True, "command": cmd}
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
|
|
def list_cvars(args):
|
|
prefix = args.get("prefix") or ""
|
|
cmd = f"DumpConsoleCommands {prefix}".strip()
|
|
try:
|
|
unreal.SystemLibrary.execute_console_command(_world(), cmd)
|
|
return {"ok": True, "note": "results dumped to log; use read_python_log or grep the project log",
|
|
"command": cmd}
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
|
|
def stat(args):
|
|
name = args["name"]
|
|
if not name.lower().startswith("stat "):
|
|
name = "stat " + name
|
|
try:
|
|
unreal.SystemLibrary.execute_console_command(_world(), name)
|
|
return {"ok": True, "command": name}
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
|
|
OPS = {
|
|
"run": run,
|
|
"get_cvar": get_cvar,
|
|
"set_cvar": set_cvar,
|
|
"list_cvars": list_cvars,
|
|
"stat": stat,
|
|
}
|
|
|
|
|
|
def entrypoint(payload_json):
|
|
try:
|
|
p = json.loads(payload_json)
|
|
except Exception as e:
|
|
return _tee({"ok": False, "error": f"bad json: {e}"}, {})
|
|
op = p.get("op"); fn = OPS.get(op)
|
|
if not fn:
|
|
return _tee({"ok": False, "error": f"unknown op {op!r}", "available": list(OPS)}, p)
|
|
try:
|
|
return _tee(fn(p), p)
|
|
except Exception as e:
|
|
return _tee({"ok": False, "error": str(e), "traceback": traceback.format_exc()}, p)
|
|
|
|
|
|
def _tee(result, p):
|
|
txt = json.dumps(result)
|
|
try:
|
|
if unreal is not None:
|
|
sd = unreal.Paths.project_saved_dir() + "MCP/"
|
|
os.makedirs(sd, exist_ok=True)
|
|
rid = p.get("__rid") or "last"
|
|
with open(sd + "last.json", "w", encoding="utf-8") as f: f.write(txt)
|
|
if rid != "last":
|
|
with open(sd + f"{rid}.json", "w", encoding="utf-8") as f: f.write(txt)
|
|
unreal.log("[MCP-CONSOLE] " + txt[:1500])
|
|
except Exception:
|
|
pass
|
|
return txt
|