Skip to content

Playable housing, GM tools and authoring, and the Referee Tools panel - #163

Draft
BubbaJoeX wants to merge 29 commits into
Open-Source-Free-Realms:mainfrom
BubbaJoeX:feature/gm-authoring
Draft

BubbaJoeX wants to merge 29 commits into
Open-Source-Free-Realms:mainfrom
BubbaJoeX:feature/gm-authoring

Conversation

@BubbaJoeX

Copy link
Copy Markdown

Playable housing, GM tools and authoring, and the Referee Tools panel

This brings housing to a playable state (placement, decorate tools and collision on furniture), adds referee (GM) tools with an in-game panel, adds authoring for scripts, conversations and custom quests, and merges current main (quests, pets, collections, terrain areas) with referee tooling for those too.

Housing

  • Houses, house fixtures and world fixtures in both databases (AddHousing, AddWorldFixtures).
  • 31 housing lot zones and Houses.json with build areas and exit doors. Some lots' build areas had Min/Max swapped on an axis and are normalized.
  • Enter / leave / visit, owner edit mode, the house list in the game dock, house settings (name, lock, flora, pet autospawn), exit door interaction.
  • Furniture placement end to end: placement ghost, place / move / pickup, texture and tint variants. Placed furniture is an NPC bound to its fixture through FixtureInstance.NpcGuid.
  • The client's decorate tools work on placed furniture:
    • House fixtures use their 32-bit instance id as their guid. The client keys fixtures by that id and its decorate tools pass it back, so tagged guids never matched: Move did nothing and Turn crashed.
    • Saving a moved or turned fixture moves its existing NPC rather than respawning it under a new guid, which left the client's fixture pointing at a removed actor.
    • The fixture's model name and definition Category are sent; the client needs both before Move/Turn work.
  • Furniture is sent as interactable to the house owner, which gives it collision and the decorate menu. Entering or leaving decorate mode toggles its hover cursor with NpcRelevance.
  • !house rotate yaw|pitch|roll <degrees> for exact rotation of the last placed or moved piece.
  • Marketplace: buying houses and any item type, whole-cart pricing (overflow checked), wallet refresh after purchase, coupon handling in preview.

Referee (GM) tools

  • World placement of persisted props (!place), !give, !furniture, !gm lookups, goto / summon, !godmode.
  • World NPCs and props: spawn, edit (name, model, scale, animation, attitude, behavior, speed, event), saved collision on/off (!npc collide, !prop collide; AddCollidable migration), Lua scripts per NPC/prop, and NPC conversations with a player dialog window.
  • First-time events and tutorials (!fte, !tutorial), world events, server lock, MOTD, packet logging, !reward list, and more. See !help.
  • Tooling for the upstream features: !quest list|info|find|start|skip|finish|reset (via a new IQuestManager.SkipGoal), !pet list|find|give, !collection status|grant|reset, !area here|list|goto|client.
  • Npc.FaceTowards uses the flat facing rotation. The yaw quaternion it built was drawn as a tilt, tipping NPCs over after a script or the editor turned them.

Authoring

  • Script library: reusable scripts for NPCs and props. Nine read-only starter templates in Resources/ScriptTemplates (greeter, proximity greeter, give once, wander, patrol, conversation on click, emote loop, toggle collision, quest giver) describe themselves with -- @name/@description/@param headers and {{key}} placeholders. GMs save their own with !scriptlib and apply any entry with !npc|!prop script use <name> key=value; values are type-checked and escaped for Lua. Scripts can call player:acceptQuest and player:hasCompletedQuest.
  • Custom quests: !questedit authors talk / collect / reach quests, published into the regular quest collection as ids 900000+. The quest window only shows locale strings, so it shows generic ones (or a GM-chosen string id), and the custom text is sent in chat on accept, each goal and completion.

Referee Tools panel (client mods)

  • A panel in the chat window: item / NPC / player / house lookups with icons and search, world tools, an NPC/prop editor, quest / pet / collection / area shortcuts.
  • Talk tab: conversations are click-built cards; answers lead to a new line (created automatically), the end, or an existing line, so only text is typed.
  • Script tab: the script library and templates beside a larger editor; templates open a parameter form and apply to the selected NPC or prop.
  • Quests tab: create, edit and publish custom quests.
  • Movable anywhere on screen, resizable from the corner grip, and maximizable. The chat window is enlarged and the chat movie draws 1:1 anchored bottom-left, so chat stays where it was.
  • Panel text keeps leading, trailing and repeated spaces and tabs (sent as \s / \t), so script indentation survives chat.
  • A purple Referee Tools tree on the main hub opens it.

Merge with main

All conflicts are resolved; the commit message of ffefdbf lists each one. Notable points:

  • Upstream's DisableCollision / Collidable replaces our guessed Unknown42, which had actually been turning collision off for placed furniture.
  • Both model snapshots were regenerated with EF and checked with has-pending-model-changes.
  • The branch is up to date with main and merges cleanly.

Assets that aren't in this repository

Nothing from the game is committed. ClientMods/README.md lists every outside asset and how to get or build it:

  • The built client mods are git-ignored. build_client_mods.py --client <your client folder> builds them from your own client's asset packs; --assets takes a local copy of the asset server instead, with identical output.
  • install_client_mods.py installs them into a client (--uninstall undoes it).
  • Names.json is generated from the client locale by extract_names.py.
  • The WebAPI serves the mods and patches them into the asset manifest it proxies (AssetUpstreamAddress, or a local copy in AssetsDirectory).

Testing

  • Builds. Game (80), Scripting (7), UdpLibrary (5) and the SQLite migration test pass. The MySQL test needs a running MySQL server and wasn't run.
  • Checked in the client against a local server: entering/leaving houses and the exit door; placing furniture; collision on placed furniture; the decorate menu and Turn left/right on placed furniture (Move goes through the same client path); the panel (lookups with icons, movable/resizable/maximize); and the script editor round trip keeping indentation and spaces.
  • Not yet confirmed in the client: the hover glow on furniture in decorate mode, !house rotate pitch/roll, the Talk/Script/Quests tabs end to end, and the new quest/pet/collection/area commands.

Housing:
- Houses, house fixtures and world fixtures in both databases (AddHousing,
  AddWorldFixtures migrations), house ownership and provisioning from items.
- 31 housing lot zones, Houses.json with build areas and exit doors.
- Enter/leave/visit, owner edit mode, the house list in the game dock, house
  settings (name, lock, flora, pet autospawn), exit door interaction.
- Fixture placement end to end: placement ghost, place/move/pickup, texture
  and tint variants. Placed furniture is rendered as an NPC bound through
  FixtureInstance.NpcGuid. The model name and definition Category are filled
  in, which the client needs before Move/Turn work.
- Build areas are normalized (some lots have Min/Max swapped on an axis).
- !house list|enter|leave|visit|grant|unbound|actors|rotate, !furniture.
- Marketplace: buying houses and other item types, wallet refresh after
  purchase, coupon handling in preview.

GM tools (with the GM commands work merged in this tree):
- !place world placement (persisted world props), !give, !gm lookups,
  goto/summon/pos, !godmode, NPC/prop editing, world NPCs and events,
  first-time events and tutorials, Lua scripts on world NPCs/props, and NPC
  conversations with a player dialog. New migrations: AddFirstTimeEvents,
  AddWorldNpcs, AddGmScripts.

Client mods (ClientMods/):
- Referee Tools panel in the chat window: item/NPC/player/house lookups
  with icons and search, world tools, an NPC/prop editor, script and
  conversation authoring, and the player conversation window.
- Purple Referee Tools tree on the main hub.
- build_client_mods.py builds them from your own client's asset packs
  (--client) or a local asset server copy (--assets).
  install_client_mods.py installs them into a client.
- The WebAPI serves the mods and patches them into the asset manifest it
  proxies from the upstream asset server, or from a local copy.

No game files are committed: the built mods are git-ignored. See
ClientMods/README.md for every outside asset and how to get or build it.
Conflict resolutions:
- Database: keep both sides' entities (houses/fixtures/world NPCs/events
  and pets/quests/collections). Both model snapshots were regenerated with
  EF and verified with has-pending-model-changes. Upstream's migrations
  (Sep 28 - Oct 16) sort before ours (Oct 26/27).
- Npc: our god-mode editor click runs before upstream's menu/atlas/
  interaction-list flow. Upstream's MovementType and Tilt for placed
  objects, plus its collision flag (DisableCollision/Collidable), replace
  our guessed Unknown42/HasCollision, which was actually turning collision
  off for placed furniture. The duplicate IsMoving/StopMoving written
  against the old path API are dropped in favor of upstream's PathState.
- Scripting: both API sets on IScriptableNpc and the Lua NPC table (emote,
  face, startConversation plus upstream's animation/effect/collision/
  movement functions).
- Player: god-mode glow alongside upstream's pet treasure chest check and
  idle state. Ability overrides kept next to upstream's equipped-item
  helper.
- In-game purchase: upstream's result codes, overflow and stack-size checks
  and pet purchases, plus our whole-cart pricing (now overflow-checked),
  house deeds granted after saving, any other item type going to the
  inventory, and a wallet refresh.
- Login: our server lock check with upstream's renamed packet variable.
  Callers updated for upstream's UpdatePosition(position, rotation).
- !quest: list a player's log, info and find (by title), start, skip the
  current goal and finish, alongside the existing reset. Skipping goes
  through the quest manager's own goal completion (new
  IQuestManager.SkipGoal), so saving, the journal and badges stay in sync,
  and finishing turns the quest in with its rewards.
- !pet: list a player's pets, find pet items by name, and give a pet the
  same way buying one does (saved pet, PetHelper.AddPet, pet list).
- !collection: status, grant and reset for a player's collections, next to
  the existing node tools. Item entries are granted as items; other entries
  are saved as collected. The client copy is refreshed with the same
  Remove/Start packets the coin pickup uses.
- !area: here (tile, terrain environment, zone area, POI and chat area),
  list, goto an area's point of interest, and toggle client areas.
- GameNameHelper: one shared English name lookup for referee tools.
  extract_names.py now also collects quest titles and collection, POI and
  zone area names.
- Referee Tools panel: a Game systems row and prompts for the new commands
  on the World tab.
Chat trims leading/trailing spaces and collapses runs of spaces, which
broke script indentation and double spaces sent from the Referee Tools
panel. Those spaces now travel as \s and tabs as 	 (the server already
decodes both), for script chunks and conversation text. Script chunks drop
from 100 to 80 characters so a worst-case escaped chunk stays short.
The client clips each UI movie to its window, so the panel was stuck in
the chat window. install_client_mods.py now enlarges the chat window to
2000x1045 (the client bottom-aligns it), and the chat movie draws 1:1
anchored bottom-left (noScale, BL), so chat stays where it was while the
panel can use the whole screen:
- drag anywhere in the visible area
- bottom-right grip scales the panel (every tab keeps its layout)
- [ ] maximizes, with a margin so it stays on screen

Window patches are now lists of replacements applied to the original
file, so reinstalling or upgrading an older install is idempotent.
- !npc collide on|off and !prop collide on|off use upstream Npc.SetCollidable
  and are saved (Collidable on WorldNpcs and WorldFixtures, default true;
  AddCollidable migration for both providers).
- The referee editor lines carry the collidable flag as a trailing field.
- !reward list shows the reward tables.
Script library: reusable scripts for NPCs and props. Nine starter
templates ship read-only in Resources/ScriptTemplates (greeter,
proximity greeter, give once, wander, patrol, conversation on click,
emote loop, toggle collision, quest giver) and describe themselves with
-- @name/@description/@PARAM headers and {{key}} placeholders. GMs save
their own entries with !scriptlib and apply any entry with
!npc/!prop script use <name> key=value; values are type-checked and
escaped for Lua. Scripts can now call player:acceptQuest and
player:hasCompletedQuest.

Custom quests: !questedit authors talk/collect/reach quests that are
published into the regular quest collection as ids 900000+. The client's
quest window only shows locale strings, so custom quests use generic
ones there (or a GM-chosen string id) and their free text is sent in
chat when a player accepts, starts each goal and completes the quest.
Referee Tools panel:
- Talk: conversations are click-built cards. Answers lead to a new line
  (created automatically), the end, or a linked existing line; node ids
  are picked client-side, so only text is typed.
- Script: the shared script library and templates beside a larger
  editor. Templates open a parameter form and apply to the selected NPC
  or prop; editor text can be saved to the library by name.
- Quests: create and edit custom quests (title, text, giver, turn-in,
  coins, prerequisite, ordered goals, item rewards) and publish them.

Server:
- House furniture and GM world props are spawned as placed objects
  (Npc.Tilt from the fixture rotation, as upstream does for its placed
  objects), so the client treats them like the zone's own static objects.
  Saving a fixture respawns it so tilt follows rotation.
- Npc.FaceTowards uses the flat facing rotation; the real yaw quaternion
  it built was drawn as a tilt, tipping NPCs over after a script or the
  editor turned them.
- The experimental !house actors toggle is removed.
Testing showed the tilt did not give collision, and it stopped the client
from showing the hover glow and radial menu on placed furniture. Fixtures
spawn as before; the FaceTowards fix stays.
- House fixtures use their 32-bit instance id as their guid. The client
  keys fixtures by that id and its decorate tools pass it back, so the
  tagged fixture guids never matched: Move did nothing and Rotate
  crashed dereferencing a placement preview that was never created.
- Saving a moved or rotated fixture moves its existing NPC instead of
  respawning it under a new guid, which left the client's fixture
  pointing at a removed actor and crashed the next edit.
- Furniture is sent as interactable to the house owner, which gives it
  collision and the client's decorate menu. Entering or leaving decorate
  mode only toggles its hover cursor (NpcRelevance); re-adding the NPCs
  broke the client's fixture bindings.
- Fixture NPCs face the fixture's yaw (stored in RotationX) instead of
  receiving the raw value, which gave unrotated items a zero direction.
NPCs and props:
- Every appearance field is editable and saved (texture/tint alias, tint,
  composite effect, subtitle, name id, nameplate image, height offset,
  image set) through !npc set, stored as JSON in a new Properties column
  on WorldNpcs and WorldFixtures (AddNpcProperties for both providers).
  Props also keep their name, animation and nameplate there.
- Overhead icons (quest, Station Cash, festive, minigame, ...): !npc icon
  add|set|clear, and !npc icon list sends a catalog of every icon the
  game's NPCs use, labelled with example NPCs, for the panel's picker.
- Yaw, pitch and roll: !npc rotate and a Rotate submenu in the referee
  radial (client labels Rotate Left/Right, Up/Down, Left/Right, Flip,
  Reset). Pitch and roll are sent as a tilt and saved.
- Props are read with the client's placement angles (yaw in X), so they
  keep the yaw they were placed or moved with.

Placed objects:
- House furniture and world props are always sent as placed objects
  (tilt, MovementType 0), interactable for every player, with no idle
  animation or idle state. As plain NPCs the client simulated them
  (pieces pushed each other upward), dropped them to the ground without
  god mode, gave no collision, and looped their animation forever (music
  blocks never stopped).

Housing:
- Furniture rotation works on the client's stored angles (yaw, pitch,
  roll); !house rotate also takes "level", and only reports failures.
- A decorate bar in the chat window mod for every owner in decorate mode:
  yaw, pitch and roll steps, flip and level for the last placed, moved or
  turned piece (the client's own wheel only turns in fixed yaw steps).

GM panel (ClientMods/chatWindowCtrl):
- Larger base layout with a tab sidebar; lookups show 18 per page.
- The editor is a grouped, scrollable two-column form generated from the
  server's field list (RefereeFieldsHelper), with the icon picker,
  rotation steps, actions and emotes.

Client mods:
- dialog: the client's NPC dialog stays hidden when sent empty, so GM
  conversations get its camera zoom while the chat window draws a
  right-side speech pane.
- housing: the decorate furniture tray shows 10 items a page and has a
  search (letter buttons; typing when the client hands it the keyboard).
…nigame groundwork

- GM conversations open the client's own NPC dialog (empty, hidden by the
  dialog mod) for its camera zoom, and end it when the conversation ends.
- The client's request to close its dialog (Escape) is now confirmed and
  ends any quest or NPC conversation; before, the UI stayed locked.
- One-time session requests (0x75) are answered (0x76: success, token,
  type, url; layout from the client's reader) and logged.
- Minigame packets (0x27, the client's BaseMiniGamePacket family) are
  logged in full. Sub-ops come from the client's packet constructors and
  line up with Clone Wars Adventures' (soir20/oxide) one lower.
- Repeating activities (opcode 0x8F, byte sub-op): RepeatingActivityAddPacket,
  layout from the client's reader. Each player gets a "wheel" activity with one
  spin a day (reset at midnight UTC); the client's daily wheel only sends its
  start request while that activity has a count left.
- CommandPacketInteractionStartWheel (BaseCommand sub-op 0x0B) is logged.

The Games portal's game lists are still empty: the minigame definitions it
reads come from the server and aren't sent yet, so the wheel can't start.
- The Games window (Main Menu > Games) lists activities the server sends,
  per ActivityCategories.txt category. ActivityPacketListOfActivities
  (opcode 0xA7, service 1, sub-op 1) and its 21-field activity record,
  layout and column names from the client.
- Each player gets "Spin For The Win!" (minigame 8) under Wheels (category
  17), as a minigame activity (type 1) the player can join.
- Activity packets from the client are handled: a list refresh re-sends the
  list; anything else is logged. PLAY now sends a join request (sub-op 2,
  activity id and the player's UTC offset), which isn't answered yet.
- !activities re-sends the list.
The Games window's join request (activity sub-op 2) now starts the
activity's minigame on the client with MiniGameInfoPacket (0x27 sub-op
0x10). The header and body layouts come from the client's readers; field
meanings follow Clone Wars Adventures' CreateActiveMinigame where the
client doesn't name them. The client doesn't react to it yet.
@BubbaJoeX
BubbaJoeX marked this pull request as draft October 27, 2026 06:31
Item generation: !itemgen copies an existing item into a new one
(ids 9000000+) and overrides fields such as model, texture, tint,
icon, rarity, cost, stack size and effect. Custom items are registered
in the item definitions at startup and pushed to everyone online when
edited; deleting one is refused while any inventory still holds it.

Redeem codes: !code creates codes with item and coin rewards, total and
per-character use limits, start/expiry dates, members-only and enabled
flags. Players redeem them with !redeem <code>; uses are recorded before
rewards are granted, unknown and disabled codes share one reply, and
failed attempts are rate-limited. The client's own game-code box calls
SOE's commerce web service on a hard-coded host, so it isn't used.
The dock's Redeem Code button sends !redeem with no code; answer it with a redeem|open panel line so the chat window mod shows its code popup instead of the usage text.
The client routes a running minigame's packets by the header's first int, so it now carries the stage id.
On the start request the server loads game_wheel.swf (CommandPacketStartFlashGame) and starts the game;
the movie's tab-separated messages drive a ten-slot coin wheel with one spin a day. Payloads are
null-terminated like the client's own, and closing sends game over and leave, then hides the window.
Item Gen lists custom items and edits their fields (Set, Clear, on/off toggles, icon preview, give,
delete). Codes lists redeem codes and edits coins, limits, dates, flags, note and items. Every player
gets a gold Redeem Code icon that opens a small code box (!redeem).
Select props and saved NPCs by radius or the last god-mode click, then nudge (±1/±5 on X, Y, Z), turn
(±15/±90), move to the GM, copy, delete or undo, with the selection listed and removable per item. Kits
save the selection and place it at the GM's position. Tabs shrink to fit twelve in the sidebar.
sanctuaryLoadingScreen.gfx strips the stock loading screen down to its tip text and loading bar over new art: "Open Source" arched over the OSFR tree and "Free Realms" curved under it, drawn at build time in the FreeRealmsDemiBold font exported from the client's gfxfontlib.gfx. The bar and spinner textures are embedded at their target size so they keep their edges. install_client_mods.py makes it the only entry in LoadingScreen.xml.
Items only name an ActivatableAbilityId and what each one did lived on the original
servers, so 239 consumables fell through to the default handler and did nothing.
extract_item_abilities.py rebuilds 198 abilities from the client: descriptions and
item tiers pick heals/energy (instant or over time), coin/star boosts, stat buffs,
run speed, size, auras and fireworks; AbilityEffectTypeSettings.txt gives the
CharacterStatId each buff tooltip reads; every composite effect id is checked
against ActorCompositeEffectDefinitions.xml.

Players now track hitpoints (regen, potions, !kill) and an energy cap that MaxMana
bonuses raise. PlayerEffect gains groups, stat bonuses and ticked restores. Coin and
star boosts apply to reward drops and quest rewards. !itemability lists, triggers
and clears them.
46 illusion foods, masks and juice bottles named a creature but had no transformation.
ItemAbilities.json can now carry a ModelId, applied through ApplyTemporaryAppearance
with the usual poof; the generator checks each model against Models.txt and uses the
race-assigned rows the Consumables.json transformations already use.
Tile editor: !tile commands and world kits saved in a new WorldKits table.
fork_zone.py publishes a renamed copy of a client zone (gm_fork), and
zone_terrain.py rebuilds its chunks' terrain mesh, Bullet collision trees
and colour maps from a heightmap. Also carries the migrations and model
snapshots that remove membership and Station Cash.
…in store

Station Cash and membership no longer exist. Accounts and characters lose IsMember, MembershipStatus
and StationCash (the migrations are in 24e22b0), along with StartingStationCash, ShowMemberNagScreen,
MemberByDefault, !flags member and Station Cash rewards. The client still locks member content
behind its membership flags, so the packets that carry them say everyone is a member, and everyone
pays what used to be the member price.

The Station Cash Marketplace is gone: its store data, order, wallet, payment and membership handlers,
and the welcome screen's Station Cash picks. Its items are in the coin store (CoinStoreItems.json),
priced in coins, with houses and mounts now bought there. The coin store gains the Marketplace's
categories it lacked (Rides, Card Games, kart, house and headwear categories, Mysterious Merchant),
item tints come from coin store tint groups, and test and never-sold items are out. The coin store
sends every definition the first time it's asked, since batches that followed the server's order
left cards blank.

Client mods: the Marketplace window only shows the coin store, with one price, working sort options
and Chatty where the Station Cash wallet was; its tab bar keeps only Coin Shop; the hub's shop icon
is a coin; the welcome screen shows coins and Chatty in place of the Popular Items, member bonus and
Member badge; Station Cash wallets and Add Station Cash buttons are gone from the other windows.
CreateEffect read the tick interval off the energy restore whenever there was no health
restore over time, so every timed buff without one (coin and star scrolls, speed, size,
stat foods, auras, fireworks) threw a NullReferenceException and took the gateway down.
Only restores over time set a tick now. A test builds the effect of every shipped ability.
Fortune teller, dance floor, firework launcher, chicken chase, red light
green light, quiz master, weather totem, squeaky plate, heckler, sleepy
guard and pinata. A new test runs every hook of every shipped template.
Load put a library template's raw source in the editor and Save to prop committed it
verbatim. Lua compiles {{hits}} as a nested table, so the pinata saved fine and then
failed every click with "attempt to sub a table value (upvalue 'health')". Committed
scripts now render their placeholders with the @PARAM defaults, and a placeholder
with no @PARAM is rejected instead of reaching Lua.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant