The garage can already be visible while some account data is still loading. A battle can exist before every entity or UI controller is ready. Lifecycle handling means waiting for the specific object you need and cleaning up when that object disappears.
Client context lifecycle
What you can observe in the client
- The mod file loads during startup, before the player reaches the garage.
- The garage may appear before inventory or dossier caches are populated.
- Entering battle destroys garage views and creates battle-side objects.
- Leaving battle creates a new garage view rather than restoring the previous instance.
- Reconnects and replay seeks can repeat parts of this sequence in a different order.
Import is discovery, not readiness
Top-level module code can run before the account, avatar, arena, dependency container, or target
view exists. Keep import-time behavior minimal, make init() idempotent, and use
fini() to cancel callbacks and release only owned resources.
Import is setup time
- Register constants, create lightweight controllers, and prepare idempotent installation functions.
- Do not assume the player object, GUI loader, cache, dependency manager, or mode package is available.
- Keep optional imports local so one missing feature does not stop the complete file from loading.
- Prefer explicit
init()work over irreversible import-time side effects.
Anchor work to the correct event
| Need | Useful readiness boundary | What can still be missing |
|---|---|---|
| Lobby feature | Account GUI and lobby-view lifecycle | The final main window or target view may still be loading. |
| Battle feature | Avatar and arena lifecycle | The arena may not yet be in the active battle period. |
| Inventory or dossier data | Cache synchronization event | Import-time cache objects may exist but contain stale data. |
| View augmentation | Target view loaded event | The model or DOM may not be ready during construction. |
| Mode-specific integration | Feature controller reports active | The package can exist while the current season or realm keeps it inactive. |
Common player lifecycle anchors
The shared player event hub exposes useful boundaries. Names and argument shapes remain version-sensitive, so inspect the target client before depending on them.
| Event family | Typical use |
|---|---|
onAccountShowGUI |
Start lobby UI work after the account GUI begins loading. |
onAccountBecomePlayer / onAccountBecomeNonPlayer |
Track transitions into and out of the lobby-side player entity. |
onAvatarBecomePlayer / onAvatarBecomeNonPlayer |
Create and remove battle-avatar behavior. |
onArenaPeriodChange |
Gate behavior on countdown, active battle, or post-battle periods. |
onPrebattleJoined |
Refresh prebattle-specific state after joining a room or queue context. |
onClientUpdated |
React to synchronized account data diffs without polling every frame. |
onDisconnected |
Clear account-session ownership and cancel work that must not survive a disconnect. |
Player events feed a second gameplay state machine
Account and Avatar callbacks are not the complete GUI lifecycle. The current client adapts global player events into a hierarchical gameplay state machine that separately tracks account entry, account GUI creation, avatar entry, arena information, battle GUI creation, arena loading, replay transitions, and exit.
ACCOUNT_ENTERING -> ACCOUNT_SHOW_GUI -> ACCOUNT_EXITING
AVATAR_ENTERING -> AVATAR_ARENA_INFO -> AVATAR_SHOW_GUI -> AVATAR_ARENA_LOADED
\---------------------> AVATAR_ARENA_LOADED
-> AVATAR_EXITING
The arena-information state can proceed directly to arena-loaded or pass through GUI creation, so observers must not assume one fixed intermediate route. The app loader maps these states to lobby and battle applications, loading views, GUI-space changes, and battle-page display.
onAvatarBecomePlayerestablishes battle ownership; it does not prove that the battle application or page is populated.- A feature that needs a populated battle page should bind to that page or a later verified gameplay-state boundary.
- Replay entry, rewind, finish, and exit add branches that ordinary live-battle teardown does not cover.
- Private state IDs and observer mappings are build-dependent; trace their current producers before using them as hook points.
Bounded readiness retries
A short retry is appropriate when an event selects the intended context but a service or view settles slightly later. It needs an attempt limit and a context token.
def wait_for_view(token, attempts_left=20):
global _callback_id
_callback_id = None
if token != _context_token or not _started:
return
view = resolve_target_view()
if view is not None and view_is_loaded(view):
attach_feature(view)
return
if attempts_left <= 0:
logger.warning('Target view did not become ready')
return
_callback_id = BigWorld.callback(
0.1,
lambda: wait_for_view(token, attempts_left - 1)
)
Context transitions are not always logout
- Account-to-avatar and avatar-to-account transitions are normal battle changes.
- Session state intended to survive a battle should clear on disconnect, account change, completion, or explicit cancellation—not simply when the account temporarily stops being the active player entity.
- Per-arena state should reset by arena identity and battle period.
- Per-view state should be recreated when the owning view instance changes.
Replay seeks are separate transitions
Replay time warp can rebuild battle entities, controllers, views, roots, and callback owners without leaving replay playback. Treat it as a dedicated teardown-and-recovery boundary rather than an ordinary repaint.
- Stop owned loops and clear transient battle state at time-warp start.
- Recreate at the actual UI or controller owner after time-warp finish.
- Use a generation token so several rapid seeks cannot revive stale work.
- Preserve disabled and hidden state instead of force-enabling the feature.
See Results & Replays for the complete recovery pattern.
Plugin membership does not establish lifecycle completion
When integrating with an existing client plugin collection, inspect each operation separately. The collection retains named objects rather than per-plugin initialized or started flags.
| Operation | Boundary |
|---|---|
| Bulk init, start, stop or fini | Logs individual plugin-method exceptions and continues when iteration permits. It does not snapshot the dictionary or isolate iterator failures. |
Dynamic add with autoStart=True | Stores the object before direct init/start calls. A failure propagates while the admitted object remains registered. |
| Dynamic remove | Pops the object before direct stop/fini calls. A failure can skip finalization or later requested removals after the object is already absent. |
When iteration completes, collection fini() clears the registry without calling
stop(). Preserve the owning component's
explicit stop/finalization order and inspect plugin overrides before treating registry absence
as successful cleanup. These latest-source contracts were checked with fake plugins and logging;
real client plugins, subscriptions, rendering, Python 2 iteration order and installed bytecode
were not exercised.
Teardown order
- Mark the controller inactive and advance the generation token.
- Cancel callbacks and stop recurring loops.
- Unbind input and unsubscribe events with the exact original handler objects.
- Destroy windows, GUI roots, observers, and temporary entities.
- Stop workers or local servers with bounded joins.
- Restore only hooks still owned by this mod.
- Clear context-specific state and references.
Player entity readiness sequence
BigWorld.player() changes among login, account, and avatar entity types. Reacquire it
at use time and invalidate retained work when the player entity starts changing. A false
entity-changing flag only means the transition guard has cleared; it does not prove caches,
routes, arena space, input, or vehicle visuals are ready.
| Boundary | What it establishes | What it does not establish |
|---|---|---|
onAccountBecomePlayer | Account helpers and proxies entered player mode. | Database ID, GUI context, cache sync, or hangar readiness. |
onAccountShowGUI | GUI context is unpacked and ordered lobby sync begins. | Completion of that synchronization chain. |
onAccountComponentsSynced | The selected chain completed or aborted. | Success unless its flag is true, or hangar work when the context skipped it. |
onAvatarBecomePlayer | Battle-scoped ownership can begin. | Space, input, HUD, vehicle, appearance, or CGF readiness. |
onAvatarReady | Required avatar initialization and arena loading have completed. | Every derived component state; vehicle property republishing can follow. |
Exit and disconnect order
Account non-player callbacks run after helper teardown and command cancellation have begun. Avatar non-player callbacks run after most battle GUI, arena, and visual services are already gone. Use the earlier player-entity-changing signal to cancel asynchronous work; use non-player events for final local invalidation rather than new dependent requests.
GUI personality cleanup clears caches and services before the global disconnected event fires. Treat it as final invalidation. Capture account, arena, player, vehicle, view, or generation identity for delayed work and revalidate it before applying a result.
Readiness matrix
For a compact comparison of account, avatar, arena, entity, Gameface, replay, and result readiness, see the Readiness Matrix.