Resource layout and virtual paths
Keep the Python mod in one source file, but package its web resources at unique virtual paths. The authoring directory is not automatically the runtime path; only the files copied beneath the packaged resource root become visible through the game VFS.
gui/gen_resources/vendor_feature_panel.json
gui/gameface/mods/vendor/FeaturePanel/FeaturePanel.html
gui/gameface/mods/vendor/FeaturePanel/FeaturePanel.css
gui/gameface/mods/vendor/FeaturePanel/FeaturePanel.js
- Treat the JSON declaration and the actual packaged source tree as one contract.
- Resolve every
coui://path to the exact packaged file before changing names. - Use unique vendor and feature segments to avoid collisions with the client and other mods.
- Do not infer a directory layout from an abbreviated load log. Verify the VFS path directly.
WG 2.4.1 native resource contract
WG 2.4.1 loads every mounted JSON list under gui/gen_resources. Each mod owns a
uniquely named declaration file and a dotted ID beneath R.mods. Python imports the
generated tree from gui.impl.gen; there is no helper merge or readiness callback.
[
{
"type": "Layout",
"id": "R.mods.vendor.FeaturePanel.views.FeaturePanel",
"path": "coui://gui/gameface/mods/vendor/FeaturePanel/FeaturePanel.html",
"parameters": {
"entrance": "FeaturePanel",
"impl": "gameface"
}
}
]
| Field | Role |
|---|---|
id | Dotted generated accessor name. Keep the JSON and Python
spelling exactly aligned under a mod-owned R.mods.<vendor> subtree. |
type |
Resource schema type, such as a layout or string resource. |
path |
Exact packaged VFS path to the resource. |
entrance |
Gameface entrance exported or bootstrapped by the HTML/JavaScript resource. |
impl |
Declares the Gameface implementation. |
- Use a unique declaration filename. Mounted files with the same name can replace one another.
- Duplicate dotted IDs are skipped; package order is not an override mechanism.
- Remove legacy
itemIDandparameters.extensionfields from the WG declaration. - Do not persist or allocate the numeric value returned by an accessor.
- Validate strict JSON. A malformed native declaration can prevent resource loading and damage the UI.
from gui.impl.gen import R
LAYOUT = R.mods.vendor.FeaturePanel.views.FeaturePanel
def layout_id():
try:
value = LAYOUT()
except Exception:
LOG.exception('Failed to resolve the Gameface layout.')
return -1
return value if value >= 0 else -1
Registration and injection are separate
Native registration gives a layout a numeric ID. Existing-view injection publishes CSS and
JavaScript URLs through an owned child ViewModel. WG 2.4.1 changes the first concern,
not the second.
- A standalone registered HTML layout does not need
gf_mod_injectjust to load itself. - An existing native view still needs
gf_mod_injecton an owned child carrier. - OpenWG v1.2.2 reverted the brief root-model injection experiment; a root model alone is not a replacement for the carrier.
- Registration success does not prove that a version-sensitive parent view, DOM selector, model contract, or teardown still works.
Legacy WG through 2.4.0
The helper-managed path is the historical WG path through
2.4.0: declarations live under mods/configs/res_map, use itemID, and resolve
through ModDynAccessor after manager validation.
- Wait for
manager.isResMapValidatedoron_readybefore using the accessor. - Guard delayed readiness with a context generation so stale lobby or battle work cannot resurrect a view.
- A changed merged map can require the helper-managed client restart.
- On WG 2.4.1,
ModDynAccessor,on_ready, and lookup helpers are deprecated placeholders;manageris unavailable.
Resource cache invalidation
A file existing in the archive is only the first step. Gameface must resolve it through the
resource map, load it through the correct coui:// root, and avoid stale cached
copies from a previous build.
- Check case-sensitive paths and file extensions.
- Confirm the resource declaration for the target WG version includes the asset.
- Confirm the HTML uses the same virtual root that the host view owns.
- On historical WG, allow a required helper restart. On WG 2.4.1, inspect native JSON, package paths, duplicate IDs, and filename collisions instead of waiting for a helper restart.
- Use a visible versioned filename or cache-busting strategy only when the client resource flow supports it.
- Separate “asset packaged” from “asset resolvable by Gameface.”
Verify the native bootstrap independently of the frontend
In WG 2.4.1.0, gui.impl.gen.resources obtains R from GuiApplication.getInstance().resourceManager.getRootNode(). Older generated Python accessor files are not the current registration owner. Native lists under gui/gen_resources and the matching callable R.mods... accessor define the declaration boundary.
- Parse the complete declaration as strict JSON and check its unique filename.
- Match the dotted
idto Python exactly and resolve thecoui://path to the mounted asset. - Reject negative layout IDs before constructing a view; log unexpected lookup exceptions with their traceback.
- Check the loaded parent, model transaction, engine/DOM readiness, and non-zero surface size separately.
- Exercise repeated open/close, consecutive battles, and final teardown in the target client.
A non-negative accessor proves resource lookup, not visual compatibility. Augmenting an existing native document still requires the owned child carrier and the WG-compatible injection helper. Standalone layouts load their registered HTML directly.
Validate the whole asset chain
Check declaration filename ownership, dotted resource identity, callable accessor, mounted VFS location, HTML-relative imports and frontend execution independently. A successful numeric layout lookup proves registration, not that its HTML loaded or its JavaScript evaluated. A loaded page does not prove the newest cached script ran.
For current WG declarations, unique list filenames prevent mounted-package filename collisions; duplicate item IDs are skipped rather than providing a reliable override. The mounted res tree is the resource root, so authoring directories never belong in a coui URL. Native loader internals are not fully represented in the source dump: strict JSON and inventory parity checks are necessary, but not a substitute for actual view creation, update and recreation.
Keep native bootstrap diagnosis separate from OpenWG frontend helpers. Registration no longer depends on a legacy helper-ready callback. Follow model/bridge ownership after resolving the resource boundary.
Image request identity and settled layout
Observed scope: WG 2.4.1.0 #977 / Cohtml 2.2.8.1. Image load/error events worked for packaged resources, but complete, naturalWidth, naturalHeight, currentSrc and decode were unavailable. A load callback could still see zero geometry or the previous image's rectangle. A successful resource event is not settled layout.
Install exact load/error listeners before assigning the source. Reassigning the identical source produced no event in the tested documents. Rapid replacement could deliver multiple loads whose handlers all read the current source, so reading event.target.src does not identify the request that completed.
A tested current-request pattern creates one new image and request record per changed selection. Its handlers close over that record and accept a result only while the owner is alive, the request is current and its status is pending. Remove listeners and clear the owned deadline before committing. On success, append that same image; on failure, preserve the previously committed icon. A failed URL can be retried without duplicating a pending or loaded request.
Dispose the owner before releasing pending and displayed images. Reject stale or duplicate completions, and retain request identity through subsequent layout work. This controls application of results; it does not prove backend cancellation, decoder release or cache reclamation. Native late-event ordering and an actual timeout were not established by the retained-callback controls.
Reserve image space explicitly
A fixed width plus aspect-ratio:16/9 reserved the tested image rectangle before load and after source removal. aspect-ratio:auto 16/9 parsed but did not reserve fallback height in the same test. Prefer known dimensions or a tested fixed ratio, then verify the painted crop/stretch behavior separately. Detached preloaders' zero dimensions are not a reliable natural-size substitute.
Stylesheet events and layout readiness
Successful packaged stylesheet loads delivered load, while an intentional missing file delivered error. At the load callback, computed width still described the preceding style; the following tracked frame reflected the new width in those cases. This is evidence for a readiness check, not a universal one-frame delay for fonts, imports or images.
Install handlers before initiating the load, retain a bounded failure path, and measure the expected style/geometry after owned readiness work. A shared loader's asset insertion is not a completion contract for your measurements. On teardown, remove only owned links and exact listeners, cancel queued frames and invalidate late work. Removing a link does not prove resource-cache reclamation.
Continue with content size versus visual bounds and hidden-window scheduling before using frames as a readiness mechanism.