Planning

RKClient Core Systems

Design for the next layer of RKClient itself — input, per-entity state, assets, and UI — as opposed to the feature plugins (Character/Stats/Skills, Currency, etc.) that will consume them. All still design stage, brainstorming, subject to change.

Build order (not rigid): RKDev → RKInput (keys/mouse) → Asset loader → Base UI → Toast. Sub-systems like PlayerController/NpcController aren't their own numbered step — they'll get built whenever the first thing that actually needs per-entity state comes up, likely during the RKInput step.

Framework vs. plugin: the decision rule

Before scoping any of the systems below, a test for where each one belongs: does it arbitrate a single shared resource that multiple plugins would otherwise independently collide over? If yes, it belongs in RKClient core, the same tier as the already-core packet registry, event pipeline, and settings system — splitting it into its own plugin buys no real isolation, since every feature plugin would still need a hard dependency on it, identical in practice to depending on RKClient directly. If no — it's self-contained business logic that consumes the shared infrastructure rather than arbitrating it — it should be its own standalone plugin.

Core (RKClient)Why
RKInput (keys/mouse)Key registration is a genuinely global, accumulating native resource — confirmed against the real Plugin API, not assumed (see below).
PlayerController / NpcControllerRKClient itself already needs this for its own state (verified/redirect-locked/NavigationController). Foundational attachment point, not a feature with its own data.
Base UI dockOne dock, one cursor, one arbitrator — same reasoning as Input.
Toast notificationsThin display utility riding on the same UI root the dock owns — no independent business logic or update cycle.
Asset loaderA shared texture/asset registry queried by name — two plugins registering the same name would silently collide or double-load.
Standalone pluginWhy
RKDevBy definition — its whole purpose is proving the framework's plugin-facing API works from outside RKClient's own source.
Character, Stats, Skills, CurrencyReal, separable business data/logic. See Character & Skills.
NPC role behaviors (e.g. a Vendor plugin)Plugs into NpcController's interaction dispatch, doesn't arbitrate it.
Cities & PortalsSelf-contained feature, see Plans.

PlayerController & NpcController

One consolidated object per connected player (and, mirrored, per relevant NPC), attached via Rising World's own native generic attribute storage rather than a parallel lookup map:

player.setAttribute("Player_Controller", playerController);
npc.setAttribute("Npc_Controller", npcController);

Confirmed both Player and Npc support this natively (setAttribute/getAttribute/hasAttribute/deleteAttribute/getAttributes) — checked directly against the Plugin API, and against an old prior project's code which already used exactly this pattern (player.setAttribute("PlayerController", playerController)).

Current reality, not a strawman: RKClient today has no unified per-player object. PlayerStateTracker is a set of parallel maps, all independently keyed by player UID — online players, verified players, redirect-locked players, needs-default-spawn, NavigationControllers, cached player data. Nothing consolidates these, and there's no extension point for a new plugin to attach its own per-player data. PlayerController replaces this with one object other systems attach sub-objects to instead of maintaining their own independent map.

Generalizes beyond static data to per-player recurring state too — counters, cooldowns, timers. The current pattern in places like DataSyncManager.syncLoop() is a manager-owned loop over every online player with a per-iteration filter. The shift is toward that state living on the controller itself, so a check becomes "does this player's controller say it's due" rather than "loop everyone and filter."

Per-player settings flow

Mirrors the two-tier model an old prior project already used for its theme system (see below): on first join, the player picks character/race/avatar info, and a copy of the current UI defaults (loaded from JSON, not hardcoded) gets attached to their controller alongside it. That combined data — character info + UI appearance settings + keybind assignments (RKInput's combos are player-remappable, not just plugin-assigned defaults) — gets saved to the backend through the normal player-data sync path. The player can customize any of it later (colors, fonts, keybinds), same changes-buffer-then-apply shape the old theme system used.

These settings apply everywhere, not just the shared dock. Any UI surface — including standalone dedicated UIs like future NPC interaction UIs (merchant/quest dialogs, not registered into the dock) — pulls its fonts/colors/borders/background from the same per-player settings on the controller, not its own separate styling. One shared appearance source for every UI a player sees.

RKInput — key/mouse combo registry

Same "one owner exposing a resolved callback" principle already used for cancelable player-lifecycle events, applied to input.

Verified against the real Plugin API (not assumed):

Design: config holds one or more named modifier channels as a JSON array, e.g. [{"name": "primary", "key": "LeftShift"}] — starts with one entry, structurally ready for more later. Plugins register a trailing Key against a named channel (defaulting to "primary"), not a full combo themselves. RKClient owns the actual registerKeys()/setListenForKeyInput()/isKeyPressed() calls and resolves the combo internally, handing each plugin only its own resolved "this fired" callback.

Open, not decided: what happens when two plugins request the same trailing key under the same channel.

Distinct from the already-planned chat command registry (registerCommand(...), slash-command text parsing) — RKInput is raw keyboard/mouse hotkeys during live gameplay, a separate complementary system.

Asset loader

A shared texture/asset registry in RKClient, queried by name. Has to exist before UI/theme work — the old theme system (below) depended entirely on a shared-asset name lookup for backgrounds and icons.

Two distinct sources, both need to land in the same name-keyed registry:

Explicitly flagged as needing rework: "most assets will be uploaded via the Assets in the updater (this may need changed)." The existing centralized-only design was never built with plugin-bundled assets in mind — reconciling the two into one coherent loading path is not yet designed.

Base UI dock

User's stated target: a dock on the right side of the screen; other plugins register a button into it, and registering the button is what adds their window/panel to the system — the button registration is the panel registration, one action. This matches an old prior project's pattern exactly (see below) — it just needs generalizing into one reusable RKClient-owned component instead of each plugin hand-rolling its own copy the way the old code did.

Mouse cursor visibility likely doesn't need its own arbitration system — it can be derived from the dock's state (visible iff any plugin's UI element is currently showing) rather than tracked separately, avoiding the exact collision the old code actually had (two independent systems each calling setMouseCursorVisible() with nothing arbitrating between them).

Confirmed real UI API (checked against the Plugin API, not assumed): a genuine hierarchical, CSS-flexbox-like toolkit — UIElement base class (subclassed by UILabel, UITextField, UIScrollView, UIMesh, UIPainter2D), real tree structure (addChild/removeChild/getChilds/getParent), a Style object per element plus USS stylesheet assets and CSS-style class lists for shared theming, and — importantly — UI elements are already natively isolated per plugin (Player.getAllUIElements(includeFromOtherPlugins), false returns only your own plugin's elements). Unlike keys, the engine already prevents one plugin from seeing or clobbering another's UI by default, which changes why a shared dock is needed — it's not fixing a collision risk the way Input is, it's about a shared page-switching container and consistent look, not native safety.

Prior art from an old project

Full scan of 4 old zips (RPCore, RPCoreCurrency, RPCoreNPC, MobSpawner) turned up a consistent, real precedent — not a fresh design:

Old theme system

File-based (.thm Properties files), loaded at startup into named Theme presets — background image, header/text/button/menu fonts and colors, border styling, author. Two-tier design: a player's live UI state is their own personal customized copy (loaded from saved DB settings on connect), not a reference to a preset — browsing the theme gallery and clicking a swatch copies that theme's values onto a live preview + a pending-changes buffer, only becoming the real saved state on Apply. A "Share" button let a player package their current customization into a brand-new named theme file for others to pick — player-generated themes, not just admin-authored, gated by a permission check.

Format decision: JSON, not Properties, going forward. RK already has working JSON read/write infrastructure (the whole Settings/*.json convention) — any equivalent data files rebuilt for RK (themes, flavor-text pools, etc.) should be JSON instead of introducing a second file format.

Rough edge, not to repeat: some Apply changes in the old code required kicking the player to force a reconnect before fully taking effect — the code's own comment flagged it as unfinished. Worth designing around properly this time.

NPC manager — aspirational scope reference

Explicitly not for verbatim reuse — "not in its entirety, we're open to improve... just a look at what I would like the end result to be."

One subscriber convention, applied everywhere

Most new registration-style systems should use the same shape already established for packets and events (registerPacketHandler/registerEventHandler — one owner, plugins register a handler, the owner resolves and calls out) rather than each system inventing its own registration mechanism. Applies to: RKInput's key-combo registration, the base UI dock (registering a button is registering a panel), and NPC interaction dispatch on NpcController (a plugin registers "I handle interactions for role X," instead of a central class hardcoding role checks).

Considered and declined: embedded FTP for updates

Raised as a way to get faster parallel downloads for Updates/. Assessment: declined for this reason. Parallel chunked downloading isn't a protocol feature — it's a client behavior (opening several concurrent range requests for the same file and reassembling them), and UpdateHttpServer already supports byte-range requests that this would ride on for free, no new server needed. FTP would actually work against the stated multi-server-over-the-internet scale (other people's servers linking in via portal travel): separate control/data channels and passive/active mode are notoriously NAT/firewall-unfriendly for exactly that kind of remote-operator network, and FTP has no TLS by default unlike the HTTP path (which gets TLS for free from the reverse proxy work). Recommended alternative: parallelize range requests client-side in RKUpdater against the existing HTTP endpoint. Not fully closed — if a different, non-speed motivation comes up later, worth reopening on its own merits.