| name | uloop-hot-reload |
| toolName | hot-reload |
| description | Hot reload applies method-body edits and can add new methods and fields (added members are visible to edited code in the same reload within the same assembly); it can also change signatures: a return-type change applies only when the same reload (or an earlier one) covers the old signature's compiled callers, while a rename or parameter change applies as an added method and warns about compiled callers it leaves on the old signature. New types, or members referenced from other assemblies or from files that are neither passed to the reload nor already hot-reloaded, require 'uloop compile'. |
uloop hot-reload
Replaces method bodies in the running Editor (EditMode or PlayMode) directly from edited
project source files — no domain reload, no attributes, no source markers. Private/internal
member access, static methods, return values, async methods, and iterators all work within
the limits below — including private access inside async, iterator, lambda, local-function,
and LINQ-query bodies. Methods that cannot be patched are reported per method as Skipped
or Failed; one unpatchable method never aborts the rest of the run.
Usage
uloop hot-reload --files Assets/Scripts/Enemy.cs
uloop hot-reload --files Assets/Scripts/Enemy.cs,Assets/Scripts/Boss.cs
uloop hot-reload
uloop hot-reload --revert-all
Multiple files are passed as one comma-separated value (or a JSON array); array options
consume exactly one value token.
A brand-new script — or any script under a brand-new .asmdef — cannot be hot-reloaded
before its first import: Unity has not compiled it into any assembly yet. Run
uloop compile once to import new files, then iterate on them with hot reload.
Parameters
| Parameter | Type | Default | Description |
|---|
--files | array | - | Project-relative .cs paths whose method bodies should be hot-reloaded. When omitted or empty on apply, selects the .cs sources whose bytes changed since the last compile snapshot, capped at 50 changed files per assembly with a warning when the cap trims the list; run uloop compile first when no snapshot exists, or pass explicit paths when no changed source is found |
--revert-all | flag | - | Remove every active hot-reload patch and clear the patch ledger. When set, --files is ignored |
--status | flag | - | Lists the currently active changes (patched methods and added members) without applying or reverting anything. |
When --files is omitted or empty, a source is selected only when its compilation assembly has a
snapshot directory and that source has its own snapshot file. A missing per-file snapshot is
left out rather than guessed as changed, so pass the file explicitly or run uloop compile
to establish a complete baseline.
Status
uloop hot-reload --status lists the currently active changes without applying or
reverting anything; it cannot be combined with --files or --revert-all. Patches are
static Editor state, so after a domain reload it authoritatively reports zero. Each
Active row's InvocationCount counts calls into the patched body since the patch was
applied — read it as a reachability signal only while the code is actually being driven;
interpretation rules are in references/troubleshooting.md.
How It Works
The edited files are grouped by the compiled assembly they belong to. Per group, every
editable method body is rewritten into a static shim by an out-of-process Roslyn worker
(private/internal access becomes accessor delegates where needed), the shims of the whole
group compile into one shim assembly against publicized reference copies and load into the
Editor domain, and each original method is patched with a Harmony transpiler (ID
io.github.hatayama.uloop.hot-reload). Because a group shares one shim assembly, a body
edited in one file can call a method or field added in another edited file of the same
assembly. Re-running after a real edit
replaces the patch; an unchanged file after a fully applied reload reports
AlreadyActive rows and changes nothing, unless another edited file of the same
assembly is in the reload — then it is re-applied with that group, and other
files of the assembly that hold active patches and are unchanged since they were
applied are re-applied too, so every active patch binds to the newest shim. With a compile-time source baseline, only
methods whose bodies actually changed are patched (UnchangedTotal counts the rest),
and a patched body that matches the baseline again is unpatched on that run.
Scope in Brief
- Patched: ordinary method bodies and property getters with a body.
- Added members: new methods, fields, and supported properties apply as
Added rows
(see the scope reference for the property shapes still skipped), visible to edited code in the same reload
within the same assembly (pass the declaring file and its callers together), and vanish
on any compile or domain reload (an Editor-session illusion). New types, references
from other assemblies or from files that are neither passed to the reload nor
already hot-reloaded, reflection, serialization,
and Unity message discovery need uloop compile.
- Signature changes (return type, rename, parameters) follow the added-member rules. A
return-type change is gated: it is
Skipped unless the same reload — or an earlier one —
has patched every live compiled caller of the old signature. A rename or parameter-list
change is not gated: it follows the delete rules — the old signature is reported removed,
and a Warnings entry names each compiled call site left on the old behavior until
uloop compile.
- Constructors, operators, compiled setter/init/indexer accessors, and event accessors
are
Skipped; finalizers and interface members are silently not applied. const and
other outside-body edits never change runtime behavior (drift is warned where
detectable).
- A reload applies each file all-or-nothing: any
Failed method leaves that file
unapplied; patches in other files still apply, except bodies that call an added method
whose own shim failed to compile — those are Skipped until it compiles.
Full rules and the Skipped/Failed condition tables: references/scope-and-limits.md.
Workflow
Treat hot reload as the exploration phase and uloop compile as the landing phase:
keep edits inside the edited files, collect structural changes, and compile once —
every compile drops all patches and pause points and resets the PlayMode session (the compile response's Warning states how many were live).
While patches are active, AutoRefreshHeld is true so returning focus does not
recompile; uloop compile or --revert-all releases the hold.
One-shot methods (Awake, Start, initialization helpers) patch successfully but show
no effect on the call that already ran; the response marks them with LifecycleNote.
For values you expect to tune while playing, expose a static property getter instead of
a const.
Reference Guides
All files live in references/ beside this skill; read the one whose trigger matches:
references/scope-and-limits.md — full scope rules: added members, signature changes, Skipped/Failed tables, source baselines, one-shot code, tunable getters.
references/mechanism-and-lifecycle.md — patch mechanism, convergence, what survives which reload, Editor-code iteration without PlayMode.
references/troubleshooting.md — Patched but no behavior change, JIT inlining, reading --status and InvocationCount.
references/pause-point-interaction.md — how patches re-target or suppress armed pause points; one-way reachability checks.
references/output.md — every response field: ErrorCode, NextActions, Methods rows, Warnings, totals.