Week of June 29, 2026. This is an archive page; back to the current week.
2026-07-05 · 66 items
Headlines
- The buy order search is live on the test shard: server and client shipped together as build 23, adding the Vendor Listings window's search field, All Zones and Buy Orders switches, and best-offer-per-unit sorting. Login smoke test and the full security check passed.
- Banning a player from your housing lot now removes them from the property when both of you are flagged for open PvP. Outside that case a ban keeps its old behavior, so it cannot be used to escape a fight.
- First optimized release client built with the GM tools compiled in and unlocked by account flags; the build's own hygiene check confirmed the still-restricted developer surface stayed out. A follow-up traced both the GM tools and the Oracle window coming up empty in the packaged client to optimization stripping the interface types they load at runtime, and fixed it.
- Simplified Chinese is in the client: the full interface and every NPC conversation are translated and rendering, taking Chinese text coverage to about 99 percent. Player and place nameplates render Chinese too, and the test shard launcher now offers it in its language picker. Native-speaker review is the remaining step.
- The Photon replacement crossed from proofs to a working client and server: the replacement networking library now carries the game's real multiplayer room code, compiled unedited, serving clients over a lossy connection through joins, broadcast events, shared properties, and clean disconnects, booted by a new one-command process host. The client half is built inside the game engine as a drop-in for the old connection object, and an in-editor test connects it to the actual new server process and gets a ping answered through the room layer's real dispatch. The full proof suite also ran green on the Linux build machine, and the encryption stack passed its checks inside an optimized, code-stripped release client, closing the last crypto gate. The in-editor suite stands at 446 passing.
- Completed the tracker move from Gitea to self-hosted Redmine: all 164 issues imported with their history, and the in-game bug reporter and log triage now file there.
- Turned 18 months of the player wishlist forum into a live board on the team tracker: about 205 threads read, the roughly 100 decoration requests set aside, and the 93 gameplay, crafting, and quality-of-life asks filed one per entry with their forum link, community support, and area. Entries rank by votes, where a vote is watching one.
GM tools
- Added a permanent devoice option: the Players window's Voice action now offers blocking a player's public chat for 12 hours or until manually revoiced, and the /devoice chat command accepts a perm option. Undevoice lifts either kind. The changed network operation was exercised end to end against a local server, and the never-expires marker was verified to store and read back intact.
- Documented that the generated network operation codes hash the full method signature; adding a parameter to an operation changes its wire code, so client and server carrying such a change must deploy together. The protocol documentation previously claimed the code covered only the name.
- Added a voice moderation strip to the main GM panel: type any character name (offline players included, using the same server search as the mail recipient picker) and devoice for 12 hours, devoice until revoiced, or undevoice, without opening the Players roster. A new editor test pins the panel's control names against the code that binds them.
- Fixed the GM window devoice actions never resolving their target: the windows send only the character's name while the server looked the target up by internal id alone, so every window-issued devoice would have come back "name not found". The server now falls back to a name lookup. The /devoice chat command was unaffected because it resolves the name first.
- Fixed the /gm and /oracle windows never opening in a packaged client. Both load their interface from a bundled file at runtime, and the optimized build stripped out the interface types they need, so the window built empty and closed itself while the command looked like it did nothing. The build now keeps those types, with an editor test guarding the regression. The cause was read straight from the client log; a rebuilt client goes to the test shard next.
Economy
- Shipped the buy order search to the test shard end to end: the game server and client build 23 deployed together, so testers can now open Vendor Listings and search buy orders across every zone, sorted by the best offer per item, with the new search field, All Zones switch, and Per Unit column.
- Retired the old search index copy after the migration soak; a precondition check proved nothing still pointed at it, and searches were unaffected.
- Verified the search mirror across a server restart: orders the server cleaned up on boot disappeared from the search results within seconds.
Build pipeline
- Ran the first optimized (IL2CPP) release client build with the GM tools shipping in it; the build passed the updated hygiene check, confirming the crafting test mode and automation harness stayed out.
- Corrected the hygiene check's success message, which still claimed the build carried no GM code; it now names the exact restricted symbols it verified absent.
- Fixed the client build job failing after a successful build: the pipeline tried to put back an editor plugin folder that no longer exists now that the plugin comes from a package registry, so a finished build was thrown away. The check now only demands back what it actually took.
Dev tooling
- Removed the vendored in-repo copy of the Unity editor automation package and switched it to resolve from the package registry, which unblocked updating it to the latest release; the vendored copy had shadowed the registry entry and made package updates fail.
Server networking
- Fact-checked the Photon replacement plan against the codebase with three parallel review passes and approved it. The architecture held on every load-bearing claim; the pass corrected the coupled-file counts, two configuration assumptions, and the channel-count premise, and folded in how the program sequences around the October go-live. The transport cutover itself remains a 2027 item.
- Recovered the server build's one proprietary dependency from the retiring version-control system and preserved it in the new one, verified byte for byte; the client's mirrored subset was confirmed identical with no drift. This closes the plan's source-recovery gate.
- Wrote the key-handling runbook skeleton for the new transport's encryption, covering storage custody, routine rotation, and compromise response, so the operational story exists before the first key does.
- Built and passed the concurrency-layer spike: a new work-scheduler on modern runtime primitives that reproduces the old library's behavior exactly. A shared test corpus runs against both the old component and the new one and every outcome matches, including the subtle cases that decide whether a stall becomes a freeze.
- Built and passed the session-encryption spike: the new handshake and per-message encryption check out against the published cryptographic standards, plus reject-tampering, reject-downgrade, key-rotation, and no-nonce-reuse-under-load tests. It uses a portable library so the identical code runs on the server and inside the game client.
- Built and passed the message-codec spike: a compact self-describing format for the game's binary messages that round-trips every value type, the special ID types, and the hot-path movement updates, and rejects malformed input before it reaches game code. The same source compiles into the client, proven by an in-editor test suite that runs green (437 of 437).
- Built the walking skeleton: a client and server talking over a real network socket that run the whole new stack end to end, connect gate, encrypted handshake, per-channel message encryption, and the codec, holding up under a quarter of packets dropped. Reliable messages all arrive intact, unreliable ones decode with no replays slipping through, and tampered packets are refused. Recorded the protocol write-up alongside the code.
- Cleared the biggest open question in the plan: whether the old load-balancing configuration still reads under the new runtime. Ran the real configuration files through the real config classes on the new runtime and they load with the exact right values, pick up a live edit, and fall back safely on a bad file. That gate is green.
- Proved the new session encryption also runs inside the game client, not just on the server: the same crypto library loads and computes correctly under the client's runtime, verified by an in-editor test suite. The client-side test count went from 437 to 443, all passing.
- Pinned the trickier networking rules the plan flagged: movement updates apply newest-first per object regardless of arrival order, an update that arrives before the thing it describes is created is held rather than misapplied, and an oversized batch is rerouted to a guaranteed-delivery channel and still arrives whole under heavy loss.
- Proved the central bet of the whole effort: the game's message envelope rides the new wire with no changes. Took the real, unedited envelope class from the server and ran it against a thin stand-in for the networking library plus the recovered shared library on the modern runtime, and every message shape - the movement hot path, logins, replies, events, nested payloads - encodes and decodes back to the same values. That is the "the networking library is a swappable bottom layer" claim, shown on the actual seam code rather than asserted.
- Got the recovered shared library building on the modern runtime as part of that, which turned up two things to fix: a duplicate build-metadata clash, and a genuinely ambiguous call the newer runtime flags where the old one quietly picked one path. Fixed both without changing behavior and flagged a dormant bug in that same method for the owner rather than quietly altering it.
- Stood up a working server connection object on the new stack, the shape the game's hundreds of networking classes are built on. A real connection now takes an incoming request, runs it on that connection's own single worker so handling stays one-at-a-time exactly like today, reads it through the real message envelope, and answers with a reply and a separate broadcast event, all encrypted over a live socket with packets being dropped. Thirty requests in flight at once were each handled in order and answered correctly. This ties every piece built so far together under the same interface the existing code already uses.
- Promoted the networking stand-in from spike code into a real library: the full server-facing surface the game code compiles against, the listener and connection-accept stack with its pre-handshake connection gate, outbound server-to-server connects, and the logging and stats plumbing the applications expect. The low-level message framing moved into the source shared between client and server so the two sides cannot drift apart.
- Compiled the game's real multiplayer room layer, unedited, against that library and served live traffic with it: two test clients joined a room over a connection dropping one packet in ten, exchanged broadcast events through the real generated message dispatch, set shared room properties, and left, with the room reference-counting itself away after the last disconnect. The test subclasses the room code exactly the way the main game server does, so the seams the port depends on are the ones exercised.
- Added the server process host: one program that loads a game application from configuration and runs it on the new stack, ready for service management on the target platform. It booted the room layer unchanged and shut down cleanly, and the full spike suite now runs nine groups, all green.
- Built the client half inside the game engine: a drop-in replacement for the old client connection object with the same frame-by-frame operating model, callbacks, and status codes, so the game's connection sites can switch over mechanically. The message layout and value encoding moved into the source shared between client and server, so the two sides compile the identical serialization code.
- Proved the two halves speak to each other for real: an in-editor test starts the actual new server process hosting the actual room layer, and the new client connects from inside the engine, completes the authenticated handshake, and gets a ping answered through the room layer's real message dispatch. The in-editor test suite grew from 443 to 446, all passing, including a check that a client pinned to the wrong server key refuses to connect at all.
- Closed the remaining crypto gate: built an optimized release client through the standard pipeline with a new one-flag diagnostic that runs the full encryption, encoding, and framing checks inside the finished, code-stripped executable. All seven checks pass and the diagnostic writes its report and exits on its own, so the gate can run unattended after any build.
- Ran the entire server proof suite on the Linux build machine: every group passes there too, including the live config reload watcher (a flagged open question for the target platform), the real room layer serving traffic over the network, and the process host booting the application. Along the way fixed a project-file pattern that only misbehaves on Linux and logged that generated server source has to be staged for builds outside the old version-control system.
Infrastructure
- Added a Chaos Shard Launcher download card to the developer portal, backed by a stable latest-version link on the patch host that every launcher release now refreshes. The launcher updates itself after the first install, so the link never goes stale.
- Published the internal build server's console through the developer portal. The build machine keeps an outbound encrypted tunnel open to the portal host, so nothing on the build network accepts inbound connections; access requires the portal sign-in and then the build server's own account login. The portal's Build & CI card now opens it.
- Added the new endpoint to the pre-deploy security checks: certificate health, the sign-in gate, and the tunnel's loopback-only binding are now asserted on every preflight run.
- Fixed a first-setup ordering bug in the build machine provisioning: the tunnel service no longer starts before its access key is registered on the far side, which had been triggering the intrusion-lockout protection and briefly blocking the office network. Also corrected a certificate-rollout sanity check that miscounted a comment as a configuration directive.
- Fixed a test-shard lockout from yesterday's unified sign-in switch: game logins moved to the shared directory but existing accounts were never migrated into it, so their passwords stopped working and resets on the accounts page had no effect. The affected account is onboarded, and the accounts page now writes the directory directly: account creation, password resets, and deletes stay in step from one place, and a failed directory write is reported in the page instead of passing silently.
- Confirmed the single sign-in works from one place: a password set on the accounts page logs into both the game and the issue tracker, verified end to end. Also cleared a fault that had blocked the accounts page itself, traced to a shared key that had drifted between the portal and the tool.
- Removed the internal portal link from the public work log's header, leaving the roadmap link, so the public page no longer points at the login-only developer portal.
- Made the accounts page the single place to manage access. Creating an account now grants the game, the dev portal, and the tracker together by default, and every account has an access row for granting or revoking portal and tracker membership. The directory sync behind it gained revoke and a read of current membership to drive the new access column.
- Kept the game checking its own stored password rather than the shared directory, so changing who can reach the portal or tracker can never affect who can log into the game.
- Reorganized the site front door: the public work log now lives at the root address, and the internal developer portal moved to /portal behind its login. The old /blog and /roadmap addresses still resolve.
- Made the roadmap's dates clearly tentative: added a prominent note up top that every date is a target and will change as the work progresses, and labeled the October date as a target rather than a fixed commitment.
- Completed the bug and task tracker move to self-hosted Redmine: imported all 164 existing issues with comments and their open or closed state, switched the in-game bug reporter and log triage to file into it, and left the old Gitea issues in place read-only so existing links still resolve.
- Gave the tracker a dark theme, written in-house rather than pulled from a third party, so it reads comfortably in low light.
- Finished the dark theme's coverage. The account sidebar, menus, pop-ups, calendar, error pages, and code blocks were still drawing light backgrounds under light text; they now match the rest of the dark interface. The remaining light surfaces were found by reading the tracker's own base stylesheet rather than page by page.
- Added a real vote button to the wishlist board. Each suggestion now has a Like button anyone signed in can click, with a live count the board sorts by. Voting had been hidden behind the Watch control; existing watch-votes were carried over so nothing was lost.
- Pinned the vote count as a default column on the wishlist issue list, so the whole team sees it without changing their own view settings.
Community wishlist
- Swept the player wishlist forum for the last 18 months and wrote up the suggestions worth acting on: about 205 active threads read, roughly 100 pure decoration requests dropped, and the 93 remaining gameplay, crafting, economy, housing, and quality-of-life asks captured with a source link, the community traction each drew, and a note on whether it fits the game. The write-up leads with the asks the developers have already answered, shipped, on the list, or declined, so they do not get raised again.
- Turned that write-up into a live suggestion board on the team tracker, one entry per suggestion, grouped into eleven player-facing categories. A vote is watching an entry, and a scheduled job tallies watchers into a sortable count so the board ranks by demand; developer-answered asks were filed already closed or rejected. The board is staff-facing for now, and opening it to players is a separate step.
Localization
- Added Simplified Chinese as a selectable language and made the UI render it: a CJK-capable font now drives the menus, options, tooltips, and item text, replacing the missing-glyph boxes. A font check across the on-screen labels came back clean.
- Machine-translated the full in-scope corpus to Simplified Chinese: interface, item and skill names, journal, help, loading tips, and all NPC dialogue, reaching about 99 percent coverage. Native-speaker review is still to come.
- Extended the translation pipeline with a lower-cost machine-translation model as a pluggable bulk translator, chosen through a blind translation-quality comparison; it kept the in-game formatting tags intact.
- Added an in-editor language switcher for QA and automated tests covering the new language's setup.
- Made player and place nameplates render Chinese. Nameplates use a separate text system from the rest of the interface, with fixed character sets baked into its three fonts; a Chinese-capable font was registered as the shared fallback, so any character those fonts lack now draws correctly. Verified in the editor on all three nameplate fonts, with Russian rendering unchanged, and covered by automated tests.
- Added Simplified Chinese to the test shard launcher's language picker, shown in its native script with the translation completion percentage, and shipped the launcher update. Installed launchers apply it automatically on next start; the update was verified end to end on a real install.
- Opened up the Chinese entry in the in-game language options to all players. It had been limited to developer builds while the translation was pending; that restriction lifts with the next test shard client build.
Client UI
- Added a color for friends in the nearby-players list. Names of players already on your friends list now show green, alongside the existing party, guild, PvP, and developer colors, so you can tell at a glance who you have not friended yet. The list already recolors live when you add or remove a friend. From a player forum request.
- Fixed the mouse wheel zooming the game camera while scrolling the Oracle window. Scrolling over the window now only scrolls its text. The window is a newer UI Toolkit panel that the camera's over-UI guard could not see, so its zoom kept firing underneath.
- Added an Only craftable filter to the crafting station window. A checkbox on the recipe list dims the recipes you are missing an ingredient for and sorts them below the ones you can make, so a long list stops hiding what is actually available. It remembers its setting between sessions, and reads what you are carrying. From a player forum request.
Gameplay
- Stopped the jump key from cancelling a spell mid-cast. With Prevent Movement While Casting on, pressing Jump during a cast that locks movement now does nothing instead of interrupting the skill, so the cast finishes. Casts that already allow movement still let you jump. From a player forum request.
- Fixed a major repair failing when a player's Crowns of the Obsidian were split across stacks. Gold Crowns granted from different sources did not stack together, so a player who held enough in total across a purchased stack and an earned stack could not combine them or complete the repair. Gold Crowns are now a single zero-value, tradeable currency that always stacks, and a database pass normalizes existing Crowns so they stack too. From a player forum report.
- Added a Guild only choice for location sharing. The Multiplayer options now offer Everyone, Guild only, or No one in place of the old on/off toggle, so guildmates can see where you are in the friends and guild list and teleport to you while everyone else cannot. The server decides who sees a location per viewer by guild membership, and existing settings carry over. Covered by unit tests. From a player forum request.
- Added a way to find empty planter spots. A new key, default Ctrl-U, beacons any planter that still has an open plant spot, the way Ctrl-W beacons plants that need water, so a missed spot is easy to find when planting a lot of seeds. From a player forum request.
- Added an Auto-Decline Party Invites option to the Multiplayer settings. When it is on, incoming party invites are turned away without a popup, the way Auto-Decline Duels already works, and a short chat line notes who was declined. From a player forum request.
- Banning a player from your housing lot now teleports them just off the property if they are standing on it, with a message telling them why. This applies only when both the lot owner and the banned player are flagged for open PvP and the owner is present in the scene; in every other case a ban still just blocks re-entry, so flipping a ban on and off cannot be used to escape a fight. A player killed in their own house used to watch the attacker stay inside after the ban. From a player forum request.
Images
2026-07-04 · 34 items
Headlines
- Built a global buy order search, a community request open since 2023: the vendor listings window can now search every buy order on every vendor in every zone, best price per unit first. The server side is live on the test shard; the client build follows.
- Fixed the forum-reported Tabula Rasa PvP bug: the chaos specialization damage-over-time effect was invisible to its target and impossible to cleanse. The same fix makes specialization bonus effects on about fifty skills behave consistently in multiplayer, and Purify can now remove chaos DOTs.
- Made the in-game GM tools and Oracle window work on the ordinary release client for accounts flagged as developer or admin. Both were compiled out of optimized release builds, which left them dead for the team; they now ship in every build and unlock purely by account flags the server checks, so supporting players no longer needs a special build.
- Fixed the game launcher on the test shard patching slowly and then freezing partway through. It now downloads a file's changed pieces in parallel and drops a wedged connection instead of waiting on it forever, and it updates itself to the fixed version.
- Reworked fishing experience so a catch is worth about four times its old value and bigger trophy fish give more, replacing a flat per-location rate players reported as too low to be worth the time.
- Began moving the team's bug and task tracker off Gitea to a self-hosted Redmine, so everyone signs in with their own account rather than one shared login, with the in-game bug reporter and log triage set to file into it. Built and staged, ready to switch on.
- Set up a shared login directory for the test setup, so one username and password now signs a person into the tracker, the developer site, and the test game, with each person's access managed from one console.
Combat
- Fixed a PvP bug reported on the player forum where the Tabula Rasa chaos specialization damage-over-time effect was invisible to its target: the victim took the damage but never saw the debuff icon and had no way to remove it. Each player's game was evaluating specialization-gated effects against the wrong character's skill training, so the effect only ever existed on the caster's screen.
- The same fix repairs specialization bonus effects on about fifty skills that misfired in multiplayer, including knockdowns and stat debuffs that did nothing against other players.
- Purify and Purify Burst can now remove chaos damage-over-time effects. They previously stripped only death, fire, and poison, which left chaos DOTs without a counter.
- Wrote up how combat effects replicate between game clients and the rule that combat formulas may only use inputs every machine agrees on, and filed a design task to review the DOT's damage numbers now that it can be seen and cleansed.
Gameplay
- Reworked how fishing grants experience, from a forum request. A catch is now scored from the fish landed instead of a flat per-location value, and the old per-location number became a multiplier, so richer spots still pay proportionally more. A normal catch is worth about four times its old value, and trophy fish scale higher by their length.
- Added editor tests covering the new fishing experience formula, including the trophy-length scaling, the per-location multiplier, and the case where a location grants no fishing experience.
Economy
- Built a global search for vendor buy orders, requested on the player forum since 2023. The vendor search index now also mirrors every buy order on player and public vendors in every zone, updated within seconds of an order being placed, filled, or expiring, with no game server changes.
- Added a Buy Orders switch to the Vendor Listings window with a new Per Unit price column, sorted by the best offer per item first. Filling an order is unchanged: travel to the vendor and sell as before.
- Wired the Vendor Listings window's search field and new All Zones switch: typing narrows the loaded list as before, and pressing Enter now searches the index, across every zone when the switch is on.
- Added editor tests for the new search result handling and offline self-checks to the mirror service and the search endpoint, covering the buy order record format end to end.
- Deployed the server side to the test shard: migrated the search index to the new format with a full rebuild in about 35 seconds, moved the live search over in one atomic step with no downtime, and verified a test buy order becomes searchable in under a second and disappears within seconds of being removed. The in-game client build is the remaining piece.
Client
- The corner build label in test builds now shows the configuration (dev or release), the engine backend, the build number, and the date and time the build was made, at half its old text size. The version string the client reports to the server is unchanged.
- Fixed the /gm and /oracle commands doing nothing in the optimized release client, even for developer and admin accounts. The GM tool suite was compiled out of release builds by design; it now ships in every build and opens only for accounts the server has flagged as developer or admin, with the server still validating every privileged operation on its side.
- Release clients now refuse to open the GM tools in offline play, where no server has vouched for the account; the offline shortcut remains available for editor testing and internal GM builds.
- Added editor tests that pin the new arrangement: the GM windows stay compiled in, the offline shortcut stays out of release, and the GM and Oracle window assets stay packed into the game's asset bundles.
Launcher
- Tracked down why the launcher patched slowly and then froze partway through on the test shard. A large file's changed pieces were pulled one at a time, and every big file queued behind a single shared memory guard that was held for its whole download, so a client used roughly a fifth of the bandwidth the host could give it. A connection that stopped sending had no timeout and hung the patch with no way to recover.
- The launcher now frees that memory guard before it starts downloading, fetches a file's changed pieces several at a time, and gives every transfer a stall timeout so a wedged connection is dropped and retried in seconds instead of hanging. Moved the launcher to build 9, which it installs by updating itself, and added tests for the new download path and the stall timeout.
Game data
- Fixed the Explorer's Shovel crafting pattern in the item catalog pointing at an asset reference that no longer existed, and refreshed three dungeon room display names.
Build pipeline
- Tracked down why the in-game GM tools stopped opening in the editor. Release builds set aside the file that switches on the developer and GM features while they build, and a build that was interrupted part-way never put it back, so the editor rebuilt itself without the GM window, developer console, and test harness, none of which gave any error.
- The build pipeline now checks for that leftover on every editor reload and restores the file by itself, with a log warning saying what happened.
- Recorded the test shard patch channel's move to build 11 in the repo's staging state.
- Added an opt-in GM build of the optimized release client for the internal test shard: it compiles in the developer console, GM tools, and control harness while keeping the release optimizations, so the shard can be tested on a build that matches what players run. It is marked as a GM build, and the existing publish check keeps it off the public release channels.
- The release build hygiene check now recognizes that opt-in GM build as intentional while still proving it carries no debug symbols or internal addresses, and it still fails any ordinary release build that has GM code compiled in by mistake. Added editor tests for the build's config rules.
- Exposed that same GM build option on the build farm's client jobs, so an optimized release client with the developer tools can be produced by continuous integration and not only from a developer's editor. It defaults off, and the resulting artifact is tagged so it is easy to tell apart from a clean build.
- Updated the release build hygiene scan for the GM tools shipping in all builds: the scan no longer treats the GM window code as leaked debug code, while still failing any release build that carries the crafting test mode or the automation harness.
Infrastructure
- Built the move of issue tracking from Gitea to a self-hosted Redmine tracker: a container deploy with nightly backups, a setup step for the project, categories, and milestones, and an importer that carries every open and closed issue across with its comments and history.
- Wired the in-game bug reporter (Oracle) and the automatic log-error triage to file into Redmine through a single setting, so turning it on or rolling back is one change. Nothing is switched over yet; the deploy and cutover wait on a go-ahead.
- Stood the new tracker up on the server and confirmed it runs, fixing the one-time data-load step that was missing a startup key. It is running privately for validation; it is not public and nothing has moved off Gitea yet.
- Configured the new tracker end to end (issue types, priorities, statuses, custom fields, workflow, and a bot account) and rehearsed the full import from Gitea: all 163 existing issues copied into a throwaway project with their comments and open or closed state, zero failures. Fixed the import so closed issues arrive closed.
- Brought the new tracker online at its own web address with a valid certificate, forced login, and sign-ups turned off, and secured the default administrator account. The team can start signing in; the existing bug and task data has not been moved over yet.
- Stood up a small login directory and connected the tracker and the developer site to it, so a person signs into both with one directory account and their group membership decides what they can reach. The old shared site password still works as a fallback.
- Connected the test game login to the same directory, so one account now signs a person into the game as well as the tracker and the site, creating their game character record automatically on first login. The change is limited to the test shard and turns off with a single setting; the live login is untouched.
Documentation
- Read a community forum thread of PvE balance suggestions and checked the main claims against the game's own data, writing up which ones hold as input to the planned combat-balance work. Confirmed that spellcasters are at a disadvantage against undead, death magic most of all, because undead carry a broad resistance to magic damage on top of a death-specific one; and that a boss draws from a large flat pool of possible artifacts with no way to farm a specific one.
- An early automated read of the creature-resistance data had it backwards; re-checked it three independent ways and added a test that pins the resistance values' numbering so a future code reshuffle cannot silently mis-map every creature's resistances.
- Started a running balance change log: one place that records every balance-affecting change to the game rules (combat, experience, loot, economy, and PvP) with the reasoning for why each was needed. Seeded it with the work so far, including two earlier fixes to the PVP experience bonus that had been paying the risk reward to players who were kept safe, and the recent review of community balance suggestions.
2026-07-03 · 27 items
Headlines
- The Quest Graph tool finished its first full version and learned to edit. Double-clicking any quest step now opens that NPC's complete dialogue tree (keywords, responses, yes/no branches, and what each one does), and a new Edit mode changes dialogue text, gate conditions, and actions in place, with every save validated first and written so precisely that only the edited value's bytes change on disk. The quest view groups steps by NPC and shows story-wide flags as dimmed satellites, a new scene-data layer taught the validator about flags set by doors, chests, and triggers (cutting the warning list by a third), the remaining 174 hard errors are a reviewable checklist, and a new check runs on every build so no new quest wiring bugs can land unnoticed.
- Russian is now fully translated in the game. Every NPC conversation plus all the interface, item, skill, journal, help, title, and script text was filled in, taking Russian from 59% to 99.8% coverage and dialogue from 64% to 100%. It is the first language in a non-Latin alphabet to be completed, and the game's existing fonts already carry the full Russian alphabet, so it needed no art changes.
- Players can now choose their language in the launcher before pressing Play. The dropdown lists all seven supported languages with a translation-completeness percentage beside each, defaults to English, remembers the choice, and shares it with the in-game language setting so the two always agree.
- The team's server-side unit tests now run inside the game client. Around 290 automated checks covering crafting, itemization, loot, stats, tax, and shared math were brought into the client's test runner and pointed at the client's own copy of that shared code, so if the copy ever drifts from the server a test fails. New tests fill in client-only logic the server tests could not reach, and a nightly build-farm job runs the whole suite.
Quest tools
- Added the conversation view to the Quest Graph window: double-clicking a quest step drills into that NPC's full dialogue tree, drawn as keyword cards leading to response cards (each showing its gate conditions and the actions it fires) and on to yes/no query branches and their chains, with include files shown as reference cards. A back button returns to the quest view.
- The quest view now draws each NPC's steps inside a labeled box, shows story-wide flags (the virtue paths and similar) as dimmed per-step satellites so they stop visually gluing unrelated quests together, and can overlay the cosmetic quest-indicator assets that watch a quest's flags behind a toolbar toggle.
- Taught the validator about the rest of the world: a reader for the game's baked scene-reference database now tells it which flags are set and read by doors, treasure chests, teleporters, tutorials, and other scene objects that conversations cannot see. Measured on the full corpus, the error count dropped from 184 to 174 and warnings from 1,224 to 876, with the stale-quest-indicator checks down 39 percent.
- Wrote the remaining 174 hard errors up as a reviewable triage document, grouped by failure type, so the content team can decide fix-or-accept case by case; accepted findings get a suppression entry with a written reason.
- Quest validation now runs headless for the build farm: a wrapper script runs the checks in a batch-mode editor and fails only when a rule produces more errors than the committed baseline, so legacy findings never block a build but newly introduced quest bugs do.
- The tool's editor test suite grew to 22 tests, covering the new conversation view, the scene-reference reader, and a guard that story-wide flags can only ever appear in a quest graph as satellites.
- Conversations are now editable from the tool: selecting a response, query, or talk offers an Edit mode for its dialogue text, gate conditions, item requirements, emote, and action list. Saves are surgical: the tool locates the exact value inside the raw file and replaces only that span, proves the whole file still parses with the game's own loader before writing a byte, and the automated tests assert that each save changes exactly one contiguous region of the file. Bad gate expressions and invalid actions are rejected with the reason shown inline.
- Text edits handle a subtlety that would otherwise make them invisible: in the built game, response text comes from a per-NPC override file (which carries the clickable-keyword markup), not the main conversation file. The editor updates both in one save, keeping the markup in the override and a plain-text copy in the main file, so the change shows up in game and in every tool. The test count stands at 27.
- Reworked the dialogue-text inputs in the edit form: the label now sits above a full-width multiline box instead of beside it, which had squeezed the editable text into a narrow column.
- Fixed the graph layout scattering parts of a quest several screens apart with nothing in between. Cycle edges from repeatable quests were inflating the layer ranking, and empty layers still consumed screen width; the layout now detects those cycles, packs columns tight, and arranges disconnected pieces side by side. The largest offender went from a sparse multi-screen sprawl to roughly two screens wide. The toolbar gained an Auto Arrange button and a persistent column-spacing slider, and the test suite grew to 31.
- Conversation editing gained undo and redo, and saves got about ten times faster. Every save is now recorded as a transaction (including edits that touch both the conversation file and its display-text override), Undo and Redo buttons plus the usual keyboard shortcuts walk the history, and each step verifies the files on disk still match what the tool wrote before changing anything, refusing cleanly if something else edited them. A save that fails part-way is now one undo from clean. Under the hood, the tool stopped rebuilding its entire index after each save; it re-reads just the edited conversation and re-derives the rest from cached data, taking the after-save refresh from about 5 seconds to under 0.6. Twelve new tests bring the suite to 43.
- Undo now also covers moving things around on the canvas: dragging nodes (or a whole NPC box) and Auto Arrange each record one undoable step on the same history as file edits, so Ctrl+Z after a drag puts the nodes back. Entries for moves are dropped when the view re-lays itself out, since the positions they refer to no longer exist; file edits stay. The suite stands at 45 tests.
Build farm / CI
- The client build jobs gained two opt-in checkboxes that chain a release off a finished build: one publishes the new build to the test shard, the other runs a full release that also deploys the server. Both hand off to the existing release job, reusing its version-downgrade guard, patch-ready check, and login smoke-test, and pin it to the exact build just produced so the publish always ships that build rather than whichever finished last. Off by default and wired only to the test shard.
- Fixed a bug in that new wiring caught on its first real run: the release step asked for the client build by a selector name the build system does not have, so a full release failed right after the client build finished. The build itself was fine; corrected to the supported selector name and re-generated the jobs.
- Made the test-shard publish accept the newest build no matter which platform's build job produced it. Client builds number their patch version from that job's own build counter, so a platform whose counter had fallen behind the channel could never publish to the test shard. Its release now advances the channel to the next number instead of refusing; the live and QA channels keep the strict no-downgrade guard.
- Separated build health from deploy health on those chained releases. A client build that compiled and packaged cleanly but whose optional publish step failed was being marked as a failed build, which reads as broken code. Now it stays green when it built and deployed, shows yellow (with a pointer to the release run) when it built fine but the publish or deploy failed, and only shows red when the build itself broke.
- The dev Windows client now builds and deploys to the test shard on its own. A new build-farm job watches the working branch every few minutes and, when a commit changes client or server code, refreshes the tree and runs the full build-publish-deploy so testers get the latest without anyone pressing a button. Commits that only touch docs, the blog, or build scripts are skipped, and it tracks the last deployed commit so it never repeats work.
- Tracked down and fixed a build that shipped a client missing a chunk of its art. The Linux build machine treats file extensions case-sensitively, and the rule that stores big binaries outside normal source control was written for lowercase extensions only, so thousands of uppercase-named model and texture files (about 8,000 models) never downloaded their real contents on that machine and sat as tiny placeholder stubs. Unity couldn't read them, imported them empty, and a rebuild baked the gaps into the shipped bundles, dropping roughly 2 GB of houses, decorations, monsters, and characters. Fixed the rule to match any capitalization, restored the real files, and added a guard that fails the build if any placeholder stub is ever left behind.
- Pulled the automatic build-and-deploy wiring back out for now. The job that watched the working branch and rebuilt, published, and deployed the dev client to the test shard on each commit is gone, along with the per-build publish and full-release checkboxes on the client jobs. Builds and deploys are back to being started by hand through the existing release job. The change is reversible if we want it back later.
Testing
- Brought the server's unit-test suite into the game client's test runner. The client keeps a byte-for-byte copy of the server's shared game code (crafting formulas, item and loot logic, stat and tax rules, shared value types), and that code had no client-side tests. About 290 in-editor tests now exercise it, reading the server test suite's own data files in place so the client parses the exact same bytes. Because both sides share the code, a check that passes on the server but fails here means the client's copy has drifted, which is the failure this catches.
- Added tests for client-only logic the server suite could not reach: the experience and skill-level tables, damage-type classification, and the shared data structures, hashing, and JSON helpers.
- Wrote a headless runner and a nightly build-farm job that run the whole suite in a batch-mode editor, publish the results, and mark the build yellow if any test fails. Day-to-day runs use the editor's own test window.
Localization
- Filled every missing Russian line with the same machine-translation pipeline used for German, French, and Portuguese, taking Russian from 59% to 99.8% coverage and NPC dialogue from 64% to 100%. About 48,600 lines were translated and checked for formatting, then a second grammar-and-meaning pass corrected 151 more. Thirty conversation areas that had no Russian text at all now have full translations. The only untranslated text left is in-game books, which ship on a separate track.
- Confirmed Russian displays correctly before shipping it: the game's interface fonts and overworld name plates already carry the full Cyrillic alphabet, so Russian needed no new font work. Checked it against the font glyph tables and with a live rendering test in the editor.
Launcher
- Added a Language picker to the launcher, next to the existing graphics option. It lists the seven shipped languages by name with each one's translation-completeness percentage (English is the source language), defaults to English, and saves the choice to the same file the game reads at startup and the in-game options menu writes, so a change in either place shows up in the other. It writes only when the player actually picks a language, so anyone relying on automatic system-language detection keeps it.
- The percentages come from a small data file built into the launcher, regenerated from the live translation database by the existing coverage tool, which gained an option to print the numbers as data. Current coverage: German, French, Portuguese, and Russian at 99 percent, Spanish at 29, Italian at 27. A note beside the file and in the localization docs covers refreshing it when new translation work lands.
- Published the updated launcher so installed copies self-update to it on next start.
2026-07-02 · 98 items
Headlines
- The move to new infrastructure has a date: October 1, 2026. A full audit of everything the old build-and-release system did against what the new stack has produced a 26-item gap register, eleven new critical tasks with target dates working back from the cutover, a dated target on every roadmap epic, and a public roadmap page at /roadmap/ that players can follow.
- GM events are getting real tooling. A new in-game GM window suite opened all four of its planned panels in the dev client: a main panel with a one-glance ghost-mode button and live dev info; an event panel with scene time dilation, timed stat buffs for one player or the whole scene, and a weather picker; a Players Online window listing everyone on the shard with one-click teleport-to-player (arriving ghosted), message, mute, and summon actions; and a prize Mailbox that delivers reward items and gold straight to any player's mail — even offline players — with fuzzy search over recipients and the full item catalog.
- A new Quest Graph tool reads every NPC conversation in the game and cross-checks the quest system's invisible wiring: which dialogue sets each knowledge flag, which gates read it, and which journal tasks and rewards they connect to. Its first full validation pass over all 2,303 conversations found 184 hard errors in shipped quest content, from mistyped flag names that keep quests from advancing to task completions that name tasks which do not exist. By end of day it also sorted the game's 2,477 flags into 677 recognizable quests and opened its first visual canvas: each quest drawn as a node graph of flags, dialogue steps, journal entries, and rewards, with validation errors badged right on the offending node.
- The nightly server-report regeneration is back after nine months off. The per-scene data the game servers read town and player-housing information from had no working generator since the legacy build job was paused last fall — the root cause of June's vanished-houses incident. A new nightly job on the build farm regenerates it from current source, the release path consumes it automatically, and the first Unity 6 output was verified to carry every one of the 442 player-owned towns, including the one that vanished. Its first runs also flushed out a real engine-upgrade bug in the housing-plot validator.
- Vendor search is being rebuilt around a live, all-world index, and the data half is done. Instead of pulling a single town's listings through the game's rate-limited connection (minutes of transfer in the biggest markets before recent fixes), every listed item in every town now sits in a dedicated search service on the test shard: all 286,000 existing listings were loaded in about half a minute, a new listing is searchable within two seconds of being posted, and a sold or removed item disappears just as fast. A name search over the entire world answers in under 30 milliseconds. The game servers were not touched to make this happen.
- The in-game Oracle's knowledge base passed its first full fact check. All 38 help topics were verified claim by claim against the game's data files, dialogue, and code: 1,762 claims checked, 497 corrected, 36 unsupportable details removed. The Oracle also no longer answers from its language model's memory of the game; every fact it states must come from the knowledge base or recipe data it just looked up.
- French is now fully translated. Every in-game line outside the separate book pipeline, from NPC conversations to item names and menus, was filled through the machine-translation pipeline that already finished German and Portuguese, taking French from 53% to 99.6% coverage with all dialogue translated. A follow-up quality pass reviewed the flagged lines and corrected 146 of them.
Go-live planning
- Set the go-live date for moving the live game onto the new infrastructure: October 1, 2026. Cross-referenced everything the legacy build system orchestrated (167 build configurations across builds, deploys, tests, scheduled reports, and backups), the old manual release runbook, and the recurring data artifacts the servers consume against what the new stack actually has, and wrote the result up as a 26-item gap register with priorities, sizes, evidence, and an explicit list of what will not make the date and ships after launch instead.
- The critical-task list grew from 4 entries to 15, each with a target date: the live hosting decision, multi-environment release automation with a promotion gate and rollback, a cutover runbook with two timed rehearsals, push alerting so no scheduled job can fail silently, the remaining CI build jobs, live-scale load testing, an on-call and incident framework, a legal and compliance refresh, a player-support baseline, and TLS/DNS/secrets consolidation.
- The internal roadmap now carries a target on every epic, a new top-level go-live epic with the month-by-month milestone table, and six newly tracked epics that the audit surfaced: client crash aggregation, player support systems, the localization pipeline rebuild, per-commit data validation in CI, host redundancy, and launch communications.
- Published a public roadmap page at /roadmap/: the player-safe rendering of the internal roadmap, with the October 1 date, the road-to-launch timeline, and what comes after. It is served without a login next to this work log and ships with the same publish script.
Vendor search
- Started the replacement for the zone-limited vendor listings window. Listed items from every town will live in a dedicated search index, kept current in real time by a service that follows the game database's own change feed (which also catches deletions made by expiry sweeps and admin tools that in-process hooks would miss), and queried by the client over the web instead of the game's rate-limited message channel. Wrote the full design into the plan of record, and stood up the first piece: the index service is provisioned on the test shard with least-privilege write/read accounts, locked to the game VM's private bridge, and its setup script proves the update-ordering and delete-tombstone guarantees on every run.
- Built and deployed the sync service that feeds the index. It runs beside the game servers without touching them, streams every listing change from the database's change feed, and stamps each write with the database's own ordering so replays and races can never resurrect a sold item. It loaded the test shard's 286,321 existing listings in 34 seconds, and a synthetic listing showed up in search results 2 seconds after insert and vanished 2 seconds after delete, including changes made while the service was stopped (it resumes where it left off).
- Each indexed listing carries what the search list needs (name, price, quantity, durability, the "+N" enchantment rating, town, vendor, seller) plus two stored payloads: a slim item for rows and the full item for tooltips. The rating and slim form are computed once when the item is listed, which is the fix for the earlier attempt that computed them per search and made things slower.
- The item names, enchantment scores, and zone names the indexer needs live only in the game's asset data, so a new extraction script exports them (20,352 item names, 781 effect scores, 386 zones) as committed data files that deploy beside the service, with a self-test against known values.
- Three repair layers back up the live feed: a per-vendor count check every few minutes, an hourly refresh that catches vendors moving between towns, and a nightly full comparison of every listing id both ways. The first count check over the full index finished in half a second.
- Built and deployed the search endpoint the game client will call: a small query service in front of the index that accepts only a fixed set of parameters (search text, town, price range, a whitelisted sort), pages results with stable cursors, rate-limits per address, and holds a read-only credential, so no write path exists anywhere in the public-facing chain. World-wide searches require actual search text; grabbing everything at once is rejected.
- Locked the new pieces into the security posture the pre-deploy check enforces: the index and query ports are unreachable from the internet (verified from an outside vantage), reachable only from the game VM's private bridge and the web portal respectively, and the full security preflight passes on all the new assertions. The public web address for the search is staged and waits only on a DNS record.
- Wrote the team doc of record for the whole system (architecture, correctness model, operations runbook, measured numbers), marked the old zone-transfer performance doc as superseded, and added the Global vendor search epic to the internal and public roadmaps.
- Wrote the client side: the vendor listings window now pulls its rows from the search service over the web when configured, with the original in-game download kept untouched as the automatic fallback. Rows arrive in a slim form (no per-item effect parsing, ratings pre-computed), a new Location column shows which town each listing is in, directions grey out for vendors in other towns, and hovering a row quietly fetches the full item for its detailed tooltip. Verified the whole data path against the test shard: an authenticated search returned a +16 two-handed sword with its rating riding along, and price/rating filters and paging all worked.
- Also confirmed the trickiest deletion path end to end: a test listing removed by the database's own timed cleanup (no game or sync code involved) disappeared from the search index on its own. Remaining before this reaches testers: the search box and all-towns toggle widgets in the window layout, a play-mode pass, and the usual gated client build.
- The public web endpoint for the search went live: name, certificate, and the proxy on the web host, verified from the outside (secure redirect in place, searches without the client key are refused, a 50-row result page travels as about 8 KB compressed). The full hardening-header set is on, and the pre-deploy security check asserts all of it from now on.
- The first real in-game use surfaced a rate-limiter bug: fetching a town walks the results page by page, and the walk exhausted its own per-address request budget (made worse by the proxy chain making every player look like one address), so the search failed and correctly fell back to the old download. Fixed on both ends: the service now identifies real client addresses through both proxy hops, page continuations get their own generous budget while brand-new searches stay tightly limited, pages carry 500 rows instead of 200, and the client waits out a brief limit instead of giving up. Re-verified with a full walk of the largest market (13,766 listings, 28 pages, no refusals).
- The first run in a real (non-editor) client exposed the next bottleneck: the download finished in 15 seconds, but building 13,766 result rows took another minute and a half, because in a built client every item asset load costs at least one frame through the async loading queue. The loader already had a fast synchronous mode it uses behind loading screens, so bulk callers can now opt into it; the vendor search does, behind its own loading spinner. Expected result is a big-town search in the twenty-second range in the real client, pending a rebuild.
- Profiling the search hitch frame by frame found the next layer: the client asked the asset system for each item type once per listing (13,766 requests that resolved to only about 1,000 unique types), and processed the whole batch inside a single frame. The loader now deduplicates bulk requests (which also speeds up plot decoration loading and the bank preload), and the search builds its rows in slices so no single frame carries the whole cost.
- A player found that the search only worked in the first zone visited: the window's zone-change refresh relied on a marker that only the old download path recorded, so after an indexed search the next zone's window silently kept the previous zone's state. The search path now records the marker the same way, and a failed load no longer wedges the window shut until the refresh button is pressed.
Quest tools
- Started the Quest Graph editor tool. Quests in this game have no single quest object in code; each one is a chain of knowledge flags, journal tasks, dialogue gates, and reward descriptors spread across four separate authoring surfaces, connected only by string names that fail silently when mistyped. The tool reads every exported NPC conversation in the repository (2,303 conversations across 148 scenes) with the game's own parsers and builds the cross-reference index the game never had: which dialogue sets each flag, which gates read it, and which journal entries and rewards they connect to. A full index build takes about 4 seconds.
- Shipped the first working piece: a 16-rule validator over that index. It checks expression syntax, flags that are read but never written (with a did-you-mean suggestion for near-miss names), journal and task references, reward-descriptor resolution against NPC conversation prefabs, include-file integrity, and dialogue gates that can never pass.
- The first full run over live game data found 184 hard errors in shipped quest content: 105 task-complete actions naming tasks that do not exist, 51 flag reads that differ from their writers only by case or a one-letter typo (flag lookups are case sensitive, so those gates never open), 17 conversations with references that break the conversation load outright, and 9 malformed dialogue actions.
- Tuning the rules against runtime behavior mattered: duplicate keyword ids across shared include files are deduplicated by the game on purpose (2,537 initial warnings deleted as noise), while duplicate response ids make the game refuse to load the whole conversation (kept as errors). Reward descriptors turn out to bind to conversations through fields on the NPC prefab, not the prefab file name, which cut another 775 false alarms once read correctly.
- Verified the tool against the documented two-NPC reference quest with 16 automated editor tests, including one that asserts the tool reproduces that quest's full flag, journal, and reward wiring exactly.
- Validation also runs headless with a per-rule baseline, so continuous integration can fail on newly introduced quest errors without demanding the legacy backlog be fixed first.
- Wrote the plan of record for the remaining milestones (automatic quest clustering, the node-graph canvas, scene-reference cross-checking, and eventually visual quest authoring with git as the source of truth) and added the Quest Graph epic to the roadmap.
- Built the clustering pass that turns the flat pile of 2,477 flag names into recognizable quests: name-stem grouping plus journal stitching discovered 677 quest clusters, with high-traffic story flags (the virtue paths, oracle state) deliberately kept from gluing unrelated quests together. The largest cluster is the starter-island quest chain at 191 connected pieces; the tool also exported all 314 non-trivial quests as reviewable graph files.
- Opened the first version of the visual canvas: a Quest Graph editor window with a searchable quest list showing per-quest error counts, an automatically laid-out node graph of each quest's flags, dialogue steps, journal tasks, and rewards, error badges pinned to the affected nodes, an inspector showing the underlying dialogue text and actions for any node, and a findings list that jumps the view to the offending node on double click.
- The editor test suite for the tool grew to 19 tests, including one that asserts the reference quest's discovered cluster contains exactly its own flags, journals, and NPCs and nothing else, and one that guards against the clustering ever collapsing into one giant blob.
Build farm / CI
- The per-flavor client build jobs and the nightly server-report job wrote Unity's entire output to a log file on the agent, so a successful build's console showed only the pipeline steps and the packaged-artifact line, and the log itself was overwritten by the next run. The jobs now stream that log into the build console while Unity runs: the Windows path tails the file by byte offset inside the existing exit-poll loop, the Linux path follows it with a background tail, and both handle Unity recreating the log on a licensing retry. The on-failure tail of the last hundred lines stays as a fallback.
- Documented the new console behavior in the build-machine notes, including the plain-text console view for very large full-build logs.
- Reviewed the never-yet-run server-build and release jobs end to end and fixed what the review found. The server job never cleaned old zips out of its workspace, so every run would have re-archived its predecessors' packages, and the release job, which unzips the first zip it finds, could have deployed an old server build. It also built whatever happened to sit in the build tree; it now updates the tree to current source first (on by default), sweeps the assemble directory before packaging so removed files can't linger into future releases, and runs under a timeout so a hung compile can't occupy the build node indefinitely.
- Hardened the release job the same way. The confirmation prompt for non-test channels used to hold the node's only executor while waiting for a human answer, which would have frozen every other job sharing that machine; it now asks before taking the executor. The job also reads the build manifest inside each client package and refuses anything that isn't a successful full build (a quick build can carry a plausible version number but no asset bundles), routes each artifact to its own platform's patch channel instead of assuming Windows, and unpacks the multi-gigabyte client zip with the native unzip tool instead of the controller's slower built-in.
- Added a dry-run option to the release job that rehearses the whole flow (artifact copy, unpack, manifest checks, argument wiring) without changing anything on the server or patch host, and ran it for the first time. The job side all passed, the manifest gate correctly flagged the only available artifact as a non-releasable test build, and the run surfaced one missing tool on the deploy machine (its PowerShell interpreter), which is now part of the machine's provisioning script.
- Reviewed the last Windows client build's full log. The build itself passed cleanly (zero compile errors, artifact archived), but the review found the console streaming above never actually rendered on the Windows agent: the console layer drops partial-line writes, and the tail wrote raw chunks, so a 51-minute passing build still showed an effectively empty console. The tail now emits whole lines and holds an unfinished line until its newline arrives.
- The same log showed the Windows build agent gets nothing from the shared asset-import cache: the committed project settings point at a loopback address that only exists on the Linux build machine, so the build retried the connection the whole way through and reimported everything itself. Documented the per-node cache address override that fixes it and the log line that verifies the first connected build.
- The nightly server-report job can hold the only Windows build slot for hours, and overnight its timer queued a second copy behind a still-running first one, jamming a client build and a server build in between. The job no longer queues a duplicate while one is running.
- Headless server-report runs no longer pop editor dialogs. Every generator in the report suite ended by opening a Success or Failure dialog, and in a headless run each one logs a warning plus a stack trace. The dialogs now route through a helper that prints a single log line when no editor UI exists and behaves as before in the editor.
- Filed tracker follow-ups from the same review: the Unity licensing handshake race that now hits every build on the Windows agent (a scripted retry currently masks it), and a spot-check that spline event hooks authored in scenes still serialize under the new engine's stricter serialization rules.
- The nightly server-report job is live end to end (Gitea #140). Activating it meant standing up the build farm's seed machinery for real: the job-generator seed job and a build-tree update job now exist on the controller, with the seed reading the CI scripts from the controller's local clone after every git-checkout approach failed against a repository this size. Getting the first green run took four fixes found by supervised runs: the Windows node refuses bare batch names from the working directory (an environment setting), backslashes in the job scripts needed escaping, the editor-automation plugin had to be stashed for the batch run the same way client builds do (an outside tool could otherwise reach into the CI editor mid-generation, and its shutdown noise failed the log scan), and the log scan itself now fails only on real failure signatures instead of any exception text.
- Release wiring for the reports is in place on both paths: the release job copies the newest report artifact and hands it to the server deploy, and a new fetch script pulls the same artifact into a working copy for hand-run deploys. The deploy scripts accept an explicit reports directory all the way down.
- Validated the first Unity 6 report output against the June incident: the combined report carries 1,905 scenes and all 442 player-owned towns, including the town whose houses vanished on June 29 because the old reports had gone stale. The artifact-fetch path was verified end to end from a developer checkout; no deploy was performed.
- The release job's version gate tripped on its first real run: every client package's manifest recorded build number 0, even though the number was correctly stamped into the client itself. The stamp rewrites the version source file on disk mid-build, but the manifest writer read the already-compiled constant from before the rewrite. The manifest now reads the stamped number back from the same file the client was built from, so the two can no longer disagree.
- Provisioned the deploy machine for real releases: the interpreter the deploy scripts run under is installed, and the release job's service account now holds the credentials it needs to reach the test shard and the patch host, verified with a login to each.
- Rebuilt both release halves from current source so they ship as a matched pair (the previous server package predated this week's GM server changes), and the first rehearsal of the release job passed end to end: the version gate read the corrected manifest, and the deploy script resolved its keys and targets with no changes made.
- The first real run then stopped at the report-combining step: the tool that merges the per-scene server reports is a Windows executable that had never been staged on the deploy machine. Staged it with its runtime, taught the refresh script to run it on non-Windows hosts, and folded both into the machine's provisioning script.
- The release pipeline completed its first fully automated end-to-end run: fresh scene reports combined and validated, the server shipped to the test shard and confirmed ready, the client published to the patch channel with unchanged files deduplicated against the previous build, the channel pointer flipped, and the closing login smoke-test passed. The public channel now serves the new build.
- The server half of the release then went out for real, and every safety net held: fresh scene reports were combined and validated (1,905 scenes, all 442 player towns), the build shipped to the test shard, and the game service restarted and reported ready. The client half stopped in the publish script, whose transfer step assumed the Windows flavor of rsync when picking its login transport; it now uses the system one on Linux hosts.
- Caught the next missing dependency before it could fail a release: the client-publish step builds and runs a small .NET tool, and the deploy machine's system packages only offer a newer runtime that cannot run it. Installed the correct one from the official installer, verified the tool builds under the release job's account, and added it to the provisioning script.
Patch host
- Added automatic build retention on the patch host. A daily job keeps the newest three builds per platform, plus any build a channel pointer still references, and deletes the rest; old numbered launcher executables are pruned the same way, keeping the three newest per family and whatever the launcher pointer names. Deleting an old build cannot strand a player: the updater syncs a client against its own installed files, never against old builds on the host, so any prior version still converges to latest. The first run removed seven old builds and two old launcher executables, freeing about 75 GB.
- The publish script now applies the same keep-three rule to its local staging area after each successful push, and the retention policy is documented in the patcher notes and marked resolved in the replacement plan's open items.
Launcher
- Added a Graphics option to the launcher, next to the Play button: DirectX 12 (the default) or DirectX 11. The choice is saved with the launcher settings, remembered between runs, and applied to the game's command line every time it starts.
- Compiled DirectX 11 support back into the Windows client so the new option has something to switch to on machines where DirectX 12 misbehaves; DirectX 12 stays the default renderer.
NPC barks and voices
- Right-clicking an NPC that barks now offers a Mute option (and Unmute to undo it). Muting silences that character's ambient barks for you: the overhead bubble, the chat-log line, and the spoken bark all stop, while deliberate conversations still work and speak normally. The list is stored with your local client settings and covers every copy of a generic NPC, so muting one town crier mutes them all.
- Added three checkboxes to the Audio options tab: NPC Barks turns off all ambient barks, NPC Attack Barks turns off just the combat and aggro callouts, and NPC Spoken Dialog turns off all NPC voice audio (dialog text is unaffected, and a line already playing stops immediately). All of these filter only what your client shows and plays; bark behavior for other players and the servers is unchanged.
- The NPC voice cache is now player-managed. Voice clips already download once and replay from disk; a new Voice Cache Size dropdown in the Audio options caps that cache (256 MB to 8 GB or Unlimited, default 1 GB), and when the cache grows past the cap the client deletes the least recently played clips in the background.
- The Audio options also show the voice cache's current file count and size on disk, updated live, next to a Clear button. Clearing asks for confirmation first and explains that clips will download again as NPCs speak.
- The audio settings outgrew their window with these additions, so the Audio tab now clips and scrolls like the video and controls tabs, wheel and scroll bar included.
- Wrote up the full recipe for adding a client option (settings flag, options-screen binding, and the checkbox row in the options window) in the team docs, including the persisted-list pattern the mute feature uses, and extended it with the dropdown row, action-button-with-confirmation, and tab scroll view patterns from the cache manager work.
Options menu
- Went through the entire Options menu and gave every setting a hover tooltip. The Audio and Video tabs previously had almost none; their 41 sliders, checkboxes, and dropdowns now describe what they do, including the performance cost of the graphics settings.
- Rewrote the 65 existing tooltips from one-line labels into two or three sentences that cover the effect of turning each option on or off. Each keeps its translation id, so existing translations stay attached and only the new English needs a follow-up pass.
Creature audio
- Fixed a warning flood from the Redthorn flower: its shared plant sound controller only knew the man-eater skeleton's root bone, so every Redthorn spawn failed to attach its three audio sources and logged about 70 warnings per session on the test shard. The controller now falls back to the Redthorn's own root bone; the man-eater plants still attach as before.
GM tools
- Started a single in-game GM tool for the dev/GM client to replace the pile of memorized chat commands used during events. A /gm command (or a bindable key) opens a set of draggable, resizable windows; typing in a tool field no longer moves the character, and window positions are remembered per machine. Two windows shipped first; the players-online roster and the prize-mail window follow with their server-side operations.
- The main panel leads with a large two-state ghost-mode button that always shows the current state (it stays in sync with the chat command too), plus a fly-mode toggle and an info block with the current zone, position with a copy button, frame rate, time scale, game time, and account flags.
- The event panel covers the common event verbs: scene time dilation with a slider and reset, timed stat buffs for a single player or everyone in the scene (with a confirmation step), and a weather picker. Player, stat, and weather fields all use a new fuzzy-search autocomplete. Scene-wide time-of-day controls are visible but disabled until the server-side support lands.
- The tool only exists in GM builds, and the release-build hygiene check now also scans finished release output for the new tool's code and fails the build if it ever leaks in.
- Added three server operations for the tool, each re-checked server-side for GM access: an online-player roster (character, account, zone, time online), a prize-mail operation that creates reward items and gold directly in a recipient's mailbox (recipient can be offline, inbox limits honored, every granted item logged), and a character search that matches character or account names, offline players included.
- Shipped the Players Online window on top of the roster: a filterable list of everyone on the shard with per-row actions — teleport to the player (turning ghost mode on first so you arrive unseen, across zones too), send a customer-service message, mute or unmute their chat, and summon them to you.
- While wiring the client side, found that the client's copy of the generated network-interface file carries a few hand-added operations the server's generator no longer produces; merged the new operations in surgically instead of replacing the file, and noted the drift for a future reconciliation.
- Shipped the prize Mailbox window, completing the four-panel set: pick a recipient by character or account name (offline players included), stage up to twenty items from a fuzzy search over the full item catalog plus optional gold and a subject line, confirm, and the prizes land in the recipient's mailbox without ever touching the GM's inventory. Failures such as a full inbox are reported in plain words.
- The event panel's time-of-day controls went live: a GM can now set the hour for everyone in the scene and freeze or unfreeze the day/night cycle there, using the same scene-property mechanism as the weather override. The server only accepts the change from GM accounts, players arriving mid-event see the overridden sky immediately, and Clear returns the scene to the server clock.
- Fixed a crash lurking in the existing time-of-day console commands: they assumed the scene has a sky system, which the login/menu scenes do not.
- Wrote the team reference for the whole suite: every window, the server operations behind them, the deploy coupling, and the build-stripping guarantee, plus the sharp edges found along the way.
- First real-account test caught a lockout: the tool's open check required a legacy "GM power" character stat that nothing in the project ever sets, so even dev and admin accounts were refused. The check is now the account flag alone (the server still verifies every operation), and a refused /gm says why in chat instead of failing silently.
- Fixed the Players Online roster showing a stale zone after a player moved to a new scene. The roster read the zone from the player's cached login record, which only refreshes on a full reconnect; it now reads the room the player is actually in, so a Refresh reflects the current zone.
Release build hygiene
- The first release-configuration build since the hygiene work started stripping the developer define failed to compile: the test-harness auto-login referenced developer-only options without a guard, invisible in dev builds where the define is always on. Guarded it (and the harness's automation credential hook, which compiled in release but should never ship in one) to match their developer-only callers, and swept the codebase for other unguarded developer-only references - none remain.
- The rebuilt release client then passed compilation but tripped its own leak scanner on one finding, which turned out to be the scanner's bug: it matched forbidden developer symbols as substrings, so a legitimately shipping property whose name contains one (every player's client must handle a ghost-mode GM standing nearby) read as a leak. Both scanners (the in-build step and the standalone checker) now match whole names only, verified byte-level against the real build output; the actual developer-only symbols are confirmed absent, and the release build now passes hygiene end to end.
- Audited every app we build (the game client in all its variants, the server, the launcher, and the bundled companion) for developer and debug code left in release builds. A compiler-options file carried over from the old workspace was adding the developer/GM define to every script compilation, so the whole GM tooling surface (developer console, GM commands, the automation control bridge) was being compiled into every client build, release ones included.
- The release build now removes that file for the duration of the build and puts it back afterward, then scans its own finished output for GM symbols, developer-connection switches, and debug symbol files and fails the build if it finds any. Intent isn't trusted; the produced files are checked. Developer builds keep the tooling on purpose, which is also how the test-shard build now gets the control bridge without a special flag.
- A GM build can no longer be produced in the release configuration at all; the build refuses the combination, and the build record now reports the tooling state truthfully instead of from the requested flag.
- Every build now strips the build machine's own network addresses out of the client's startup config file. Unity writes one line per network interface into development builds, and those get published to the public patch host for the test shard, so they were shipping internal addresses to anyone who downloaded the client.
- Release client builds no longer ship the companion app's debug symbol files, and the publish step drops all symbol files and refuses to send anything but a clean release build to a player channel.
- Added a standalone build-hygiene scanner and wired the same checks into the pre-deploy security preflight, which now also reads what each live patch channel is actually serving. Documented the policy and the four enforcement points, and filed follow-ups for two existing leaks on the host (an old build that shipped generated source and symbols, and the current test-shard build's address lines) plus a server rebuilt in release mode.
Unity 6 migration
- The report job's first Unity 6 run caught a real engine-behavior change: asset enumeration in the editor now surfaces loaded prefab assets alongside scene objects, so the housing-plot validator saw the two "for sale" sign prefabs themselves (whose plot reference is empty by design at the asset level) and failed validation in all 69 housing-capable scenes. Worse, the non-validating path could have written scene plot data onto the prefab assets, and the plot-renaming utility could have renamed them. All four sweeps now consider scene instances only; the next report run came back with zero validation errors, including a duplicate-plot-name error that turned out to be the same artifact.
- Fixed the console warning "the tree must use the Nature/Soft Occlusion shader" that fired for four terrain tree types (two ficus variants, a bare trunk, and a thin tree) used as tree prototypes on more than 50 scene terrains across Novia, Hidden Vale, battlecamps, random encounters, and player villages. The engine only demands that shader when a tree prefab has no LOD group; the four prefabs now carry one, and the warning stops with no change to the trees' materials or appearance.
- Finding the affected terrains took a byte-level scan of the binary terrain data, since tree prototype references there are invisible to text search. The same tree prefabs are also placed directly in 46 scenes, so the new LOD group is set to never cull and those placements render exactly as before.
- Cape cloth had gone slow and choppy under the new engine. Cloth is simulated on the physics tick, and the client's physics tick runs at 10 per second; the old engine hid that with a cloth sub-stepping setting that the new engine dropped, so capes were suddenly updating only ten times a second. Raised the physics tick to 60 per second so cloth simulates smoothly again, and removed the now-defunct cloth setting from the equipment code.
Security
- Ran a security audit of how passwords, credentials, and access keys are stored and passed around across the project's tooling and infrastructure. Wrote up the findings and a hardening plan in the internal docs; no changes applied yet.
Localization
- Filled French with the same machine-translation pipeline that finished German and Portuguese, taking it from 53.0% to 99.6% coverage. All 55,668 missing lines were translated in a bulk pass, then re-checked line by line against the game's markup rules (keyword links, gender and plural selectors, formatting tags); anything that failed was re-translated automatically, and the last stubborn records were retried with their exact validation error fed back in. Every remaining gap is the separate book text, which ships on its own track.
- NPC dialogue, which was the largest hole, went from 55% to 100% translated: 45 conversation zones that had no French at all now have full French. The 43 conversation files that were previously untranslated shipped as new client language data.
- Ran a meaning-and-grammar review over the machine-translated lines. Heuristics flagged 1,710 candidates for a closer look; the review confirmed most as fine and applied 146 validated corrections, with 27 lower-confidence lines set aside for a native speaker.
- Serialized the finished French into the client language bundles and deployed to the test shard. The coverage snapshot doc now records French alongside German and Portuguese as complete for the in-game deploy set.
- Researched Japanese language support and wrote the plan. Most of the machinery is already in place: the machine-translation pipeline that filled French runs unchanged for Japanese, the UI already wraps Japanese text correctly, and chat, mail, and character names already accept it. The new engineering is a Japanese font (no font in the game carries the glyphs today) and live testing of Japanese text entry.
- The plan makes a politeness style guide and native-speaker review prerequisites for the Japanese translation pass, since machine translation is weakest at Japanese honorifics. Whether Japanese joins the October translation wave or lands later with the planned Asian shard is left as an open decision (Gitea #161).
In-game Oracle
- Hardened the Oracle assistant against answering from its language model's built-in memory of the game, which can be outdated or simply invented. Its instructions now require every game fact it states (numbers, names, places, mechanics) to come from the curated knowledge base or the recipe data it looked up during that conversation, with no borrowing from the older game it may "remember" or from the Ultima series.
- Added a server-side safety net behind those instructions: a gameplay answer produced without consulting the knowledge base or recipe data gets sent back once with an order to look it up or admit the gap, and every logged unanswered question now records whether the answer was grounded in real lookups.
- Fact-checked all 38 Oracle knowledge-base topics claim by claim against the game's data files, dialogue sources, and code: 1,762 claims checked, 1,229 confirmed, 497 corrected, 36 unsupportable details removed. Corrections ranged from combat math (Rapid Fire fires 8 arrows for 4.25 times weapon damage in a descending sequence) to lore (Arabella, wife of Lord British, is regent of New Britannia; the doc had named Ronin) to creature behavior (adult dragons have wing slap and wing gust attacks; the doc claimed they had none).
- Several magic guides described a smooth day-night power curve for Sun and Moon spells; the real bonus is simply on while the sun is up (or the moon in orbit) and off otherwise, and it works indoors. Attunement math, ward behavior, reagent consumption chances, the whole mounts guide, and the interact key (E; the docs said F) were corrected the same way.
- Every knowledge-base topic now has an internal source manifest recording which game files its claims were verified against and when, plus a full audit report, so future edits get re-checked against data instead of written from memory.
- Mapped the next round of Oracle topics: a ranked 15-entry gap list (death and resurrection, housing, travel, currencies, and pets lead it) went into the audit report, each screened against the content exclusions and paired with the game data it would be written from.
Client UI
- Nameplates over shrunken players no longer scale down without limit. The nameplate still shrinks along with the character, but it now stops at half its normal size, so names stay readable under shrink effects.
Agent tooling
- Rewrote the repo's AI-agent instructions (CLAUDE.md) and the agent's persistent working notes in a compact, directive-only format, roughly half the previous size, and added a standing style rule so future agent-facing notes stay that way. Team-facing docs are unchanged.
2026-07-01 · 38 items
Headlines
- The plan for replacing the game's licensed, Windows-only server networking stack (Photon) is now written end to end and approved. The direction changed from the old draft: instead of re-implementing Photon's wire format byte-for-byte, the servers and the client's transport move together to a clean new protocol underneath the game's existing message layer, with the servers hosted natively on Linux. The plan covers feasibility spikes, a parallel test universe on the test shard, a staged cutover with timed rollback drills, and area-of-interest filtering as the final scale phase; it was fact-checked against the code and hardened by several rounds of engineering review before approval. Phase 0 starts now.
- Bug reports through the in-game Oracle can now carry a picture. When the Oracle confirms a bug it asks whether to include a screenshot of what you're seeing; say yes and the game captures the screen and attaches it to the team's issue automatically. The Oracle window hides itself for the shot, so the picture shows the bug rather than the conversation about it.
- Vendor search in large player-owned towns now loads in about fifteen seconds, down from the two to three minutes it used to take. The client builds the results list once when the download finishes instead of rebuilding it on every item as they stream in, the server does its per-item database lookups once per vendor instead of once per item, and page sizing was fixed so a batch that lands on several large items can't exceed the per-message limit and fail the whole search. A separate experiment to send a slimmed-down item record was tried and dropped: it cost more server work than it saved on the wire.
- Switched the Windows client to DirectX 12 as its only graphics API, replacing DirectX 11. A measured in-world benchmark put DirectX 12 and DirectX 11 within a few percent on frame rate, so the move is for engine capabilities we want next rather than raw speed; DirectX 12 was excluded from being a plain speed play by the fact that the client is bound by the main thread and the GPU, not by render-thread draw submission. Vulkan was left out because it isn't stable on the current content.
- A years-old pet-data leak that could permanently lock a player out of the world is fixed for good. Records for tamed pets were never cleaned up when their taming necklace was destroyed, sold, or traded away; once enough piled up on an account, the server could no longer send that character's pet data at all, the world never finished loading, and the account needed hand-edits to the database every month. The server now removes a pet's record the moment its necklace leaves the account, heals already-bloated accounts automatically on their next load, and support gained report and cleanup commands that retire the monthly manual fix.
- The automated client-control harness graduated from watching the game to playing it. A test client can now inspect its surroundings, inventory, and skills; target, interact, use skills and items, and equip gear; run an approved set of developer-console setup commands; and craft a recipe at a station end to end, ingredients to finished item. Everything rides the same code paths a real player drives, so game rules and the server's permission checks stay in force.
- A new Recipe Studio editor window puts the whole crafting-recipe workflow in one place, replacing raw Inspector work spread across several disconnected menus. All four planned milestones landed today: browse and search the roughly 3,300 recipes (by name, ingredient, result, school, or skill level), error-check the whole library in the background with checks the old validator never had, edit recipes in-window with live validation and duplicate-ingredient warnings, and create new recipes through a guided wizard that clones an existing recipe, handles naming and recipe-book placement, and reminds you when the server's recipe database needs regenerating.
Server networking (Photon replacement)
- Rewrote the Photon-replacement migration plan against the post-Unity-6 reality and got it approved. The old draft assumed the shipped client could never change, which forced re-creating Photon's wire format byte-for-byte on the server; with the launcher now shipping client updates routinely, that premise no longer holds. The new plan moves server and client transport together to a clean new protocol beneath the game's existing message envelope, keeps the game code compiling unchanged behind a compatibility layer on each side, hosts the servers natively on Linux, and replaces the old weak wire encryption with a modern scheme. Cutover is staged: feasibility spikes first, then a parallel universe on the test shard, a low-risk pilot on the logging service, a full test-shard cutover with a timed rollback drill, and only then the live window.
- The plan went through several review rounds before approval: an adversarial pass that confirmed thirteen problems (among them two customer-service tools that also speak Photon and would have broken at cutover, and the fact that the live deployment spans multiple machines), and two rounds of engineer feedback that corrected the transport channel semantics, tightened the rollback data-safety story, upgraded the crypto section to a buildable spec, and fixed a wrong scheduling assumption about how this program competes with the render-pipeline work for the same client. All findings are folded into the plan with a ledger of what changed and why.
- Marked the old plan superseded, rewrote the roadmap epic to match, and extended the Photon API-surface reference with every consumer of the old networking stack beyond the game client (the bot-test client, the customer-service tools, a scheduled moderation job) so nothing breaks unnoticed when the wire changes.
Rendering
- Set the Windows player's graphics API to DirectX 12 only, dropping DirectX 11 and Vulkan. Benchmarked all three in-world first, driving a test client through the control harness onto the overworld and a town: DirectX 12 matched DirectX 11 on frame rate, because the client is limited by the main thread and the GPU rather than by render-thread draw submission, which is where DirectX 12 is cheaper (two to four times less render-thread cost here). Vulkan was excluded because it did not reach the world on the current content. DirectX 12 requires Windows 10 or newer, so this drops any players still on older Windows; worth checking against the player base.
- Raised the async texture-upload buffer from 9 to 64 MB across quality levels so a single large asset during a zone load can't overflow it under DirectX 12. This surfaced while benchmarking at very high frame rates, where the default buffer overflowed on a large asset; the bigger buffer clears it.
- Wrote up the DirectX 11 vs 12 vs Vulkan evaluation with the measured numbers (frame rate plus per-frame CPU and GPU timings on the overworld and a town) and the upload-buffer fix, and linked it from the docs index and the render-pipeline roadmap.
Unity 6 migration
- Fixed the recurring Ceto ocean teardown error (“Destroying object … is not allowed at this time”). On scene unload, play-stop, or quit, the ocean frees its wave render textures from inside
OnDestroy; Unity forbids bothObject.DestroyandObject.DestroyImmediatein that window and logs the error. The previous fix had switched the call from one to the other, which just moved the error — both are rejected there. The shared destroy helper now skips the managed destroy during teardown (the GPU surface is already released beforehand, and Unity reclaims the texture on its next unused-asset pass) and keeps the normal deferred destroy for mid-game buffer recreation. Also routed the overlay manager's one direct destroy through the same helper. Reproduced and verified in the editor: unloading a Ceto scene during play logged the error about ten times before and zero after, and a plain play-stop is clean. - Rewrote the root-cause section in the Unity 6 upgrade notes, replacing an earlier writeup that had the diagnosis backwards, so the next person doesn't try a fifth swap between the two destroy calls.
- Fixed a follow-on bug from that teardown change. Skipping the destroy during a scene unload was right for the ocean's render textures, but the same helper also destroyed the projected-grid meshes and the underwater bottom mask, which are hidden root GameObjects Unity does not reclaim on its own. Those leaked into the next scene, kept a render callback registered in a static list, and fired it every frame against the destroyed water component, throwing a null-reference each time; entering Silverdale Market logged the error continuously. Split the cleanup: render textures still skip destroy during teardown, but those GameObjects are now destroyed and their callback removed from the static list first, and the projection callback returns early instead of throwing when its component is gone. The reflection, depth, and mask cameras were leaking the same way on every zone change and are destroyed now too. Compiles clean; the in-editor scene-transition check still needs a run in a live editor.
Player vendors
- Vendor search in a large town rebuilt the entire results list once per item as listings streamed in, an O(n²) cost that dominated load time on a big town. The list now suppresses those per-item updates during the download and builds once when loading finishes, on a new completion event. This pairs with the earlier server-side fix that collapsed the per-item plot lookups.
- Tried, measured, and dropped a slimmed item record for the results list (effect data stripped, a pre-computed quality rating in its place) meant to shrink the transfer. End-to-end it was slower, not faster: effects aren't the bulk of the average town item, so the transfer barely shrank, while building the slim record for every enchanted item cost real server time. Backed it out; the speedup comes from the list-rebuild and per-vendor-lookup fixes instead.
- Fixed a bug that could fail a large-town vendor search outright. The listings were sent in fixed-count pages that ignored how big each item was, so a page that happened to hold several heavily-enchanted items could exceed the per-message size limit and error out the whole search. Paging is now sized by bytes, filling each page to a safe budget no matter how large the items are. It was data-dependent, which is why some searches went through and others didn't.
Pets
- Fixed the pet-data leak behind the scene-load “black hole” (Gitea #134). Taming a pet stores its custom name and behavior keyed to the taming-necklace item, but nothing removed that record when the necklace was destroyed, sold, or traded away, so the records accumulated forever. Once an account's pet data outgrew the per-message size limit, the server failed while sending it, the client waited on a reply that never came, and the scene never loaded. Affected accounts couldn't self-heal and were being fixed by hand-deleting database records every month.
- The server now drops a pet's record when its necklace is destroyed, fully sold to a merchant, or traded away, and reconciles oversized pet data against the items the account actually owns before sending or storing it. Accounts already past the limit heal on their next pet-data load with no manual intervention, and a record is kept whenever its necklace still exists anywhere the account holds items, including the bank, so nothing legitimate is lost. As a final backstop, a reply that is still too large after cleanup is trimmed to fit rather than allowed to fail, so this class of stuck login can't come back.
- The database maintenance tool gained two commands for support: a report ranking characters by pet-data size with warnings as they approach the limit, and a cleanup that removes orphaned records account-wide, dry-run by default. Together they replace the manual monthly query.
- Added regression coverage at both levels: unit tests for the reconciliation rules (orphan detection, spawner-reference cleanup, item-less pets never touched) and a new bot test that uploads deliberately bloated pet data over the wire and verifies the server prunes the orphans while keeping the real pets and their names. Verified end to end on a local server, including the cleanup commands against seeded data. (Gitea #134)
Client UI
- The in-game Oracle window (the one UI built on Unity's UI Toolkit rather than the game's NGUI) froze the avatar for as long as it was open. It claimed a dedicated input mode on show, and the player's movement reads only fire in the default control mode, so every movement key was dropped. It now claims that mode only while the question field has keyboard focus, mirroring how chat suppresses movement while you type: you can move with the window open, and typed w/a/s/d still won't drive the avatar while composing a question. Auto-focus on open is kept, and the window still releases the mode on close. Compiles clean; wants a play-mode check of the focus/movement handoff. (Gitea #147)
- Added a rebindable key for opening the in-game Oracle window, so players don't have to type
/oracleeach time. It appears on the Options key-bindings page as Toggle Oracle Window, unbound by default (like Toggle Task List) so players pick their own key, and toggles the window open and closed; the/oracleand/askchat commands still work. Compiles clean; wants a play-mode check of the binding. (Gitea #142)
In-game Oracle
- Added a Finding and Buying Recipes article to the in-game Oracle's knowledge base. It covers how recipes are obtained (known by default, trainers, quests, discovery at a station, drops, and purchase), which crafting merchant carries a given craft's recipes and roughly where to look, how recipe pricing behaves, and how to search other players' vendors. Written from verified game mechanics and cross-linked from the crafting guides, with the Oracle's search index rebuilt. The exact nearest-vendor-with-current-price lookup needs live game data and is left as a follow-up. (Gitea #146)
- Gave the Oracle a crafting-recipe lookup so it can answer material-requirement questions. A build step extracts about 3,100 recipes from the game's own data into a compact dataset (recipe, craft, skill level, ingredients and quantities, and what it makes); the Oracle searches it by name and answers from that data instead of guessing. Ingredient names resolve to their real in-game names, and disabled, GM-only, and unreleased recipes are excluded. (Gitea #144)
- Fixed a deploy bug in the Oracle's web service: redeploying could drop its HTTPS configuration and serve the wrong certificate, briefly making the in-game Oracle unreachable. The deploy now writes the full HTTPS config itself every time, so it can't silently regress.
- The Oracle now offers to attach a screenshot when it confirms a bug report (bugs only, never feature requests, and only if the player says yes). On consent the client hides the Oracle window, captures the frame, compresses the image under the tracker's attachment size cap, and uploads it; the server attaches it to the freshly filed issue as a native tracker attachment, falling back to a hosted image linked from an issue comment. The report still files even if the capture or upload fails, and the transcript reports the outcome either way. (Gitea #149)
- The screenshot upload is gated by a single-use, short-lived token the server mints only when a player consents on a report they just filed, so the endpoint cannot be used to push images onto other issues; validation runs before the token is consumed, so a malformed upload doesn't cost the player their one shot. Old clients never see the offer (the server makes it only to clients that declare screenshot support), which lets the service side ship ahead of the next client build.
Editor tooling
- Started Recipe Studio, a dedicated editor window for working with crafting recipes; until now, browsing meant a name filter in an old explorer tab and authoring meant raw Inspector work across several disconnected menus. The first milestone ships browsing: it indexes all recipe assets (about 3,300) with search by name or by ingredient/result item, filters for craft school, action type, enabled state, skill-level range, and whether a recipe is actually referenced by a recipe-book tab, and a detail pane showing ingredients, tools, results, and book membership, with a warning when a player-facing recipe is in no book and would be invisible in game. The index reads reference strings instead of loading the item assets they point to, so it stays fast on the full library. This is the project's first editor window built on Unity's UI Toolkit. Batch error checking and in-window recipe editing and creation land next.
- Recipe Studio milestone two: library-wide error checking. The recipe validator that used to dump every problem into one modal dialog was extracted into a shared engine; the old menu item and the build pipeline keep their exact behavior, while the window runs the same checks a few recipes per editor tick so the editor stays usable during a full scan. New checks the old validator lacked: player-facing recipes missing from every recipe-book tab (invisible in game), missing recipe-book icons, empty display names, and stale stored ingredient hashes (the crafting lookup key). Results land in a filterable issues panel; clicking an issue jumps to the recipe, and problem recipes get error/warning badges in the browser. Verified against the live library (clean, matching the old validator) and against an injected duplicate recipe, which was correctly flagged on both the original and the copy.
- Recipe Studio milestone three: editing without leaving the window. The detail pane now embeds the recipe's full inspector, so the existing edit behaviors (ingredient-hash refresh, metadata sync, only-one-primary-ingredient) carry over unchanged, with a live error strip above it that re-validates a moment after each edit and warns when the ingredient set collides with another recipe's. Recipe-book placement is managed in the same pane: add or remove the recipe from any book tab, with a warning when a player-facing recipe is in no book at all. Editing an ingredient quantity was verified to update the crafting lookup hash and clear its duplicate warning.
- Recipe Studio milestone four: creating recipes. A guided wizard clones an existing recipe (the common case, with a searchable picker that pre-fills the destination folder and naming) or starts blank, enforces the naming convention, checks for path collisions, and mints a fresh localization id for the display name so clones never share one. It also sets the new asset's archetype identity explicitly after finding that the existing import-time heal could apply out of order and leave a clone sharing its source's id. A banner in the window now counts recipes changed since the last server recipe-db export and regenerates it on click, closing the step that was easiest to forget when shipping a recipe.
- Recipe Studio finishing pass: the detail pane's ingredient, tool, and result lists are now directly editable rows (item picker with icon, classification, quantity, and the primary/always-consumed/apply-effects flags, with add/remove and the only-one-primary rule enforced) instead of requiring the embedded inspector, which remains available in a collapsed foldout for the long tail of fields. Item references in recipes serialize as id-plus-path strings rather than normal object references; a new picker control writes that format through the game's own path resolver and reports bad picks inline instead of the old modal dialog. Also wrote up the tool and the full recipe data pipeline in the docs tree and linked it from the docs index.
Server operations
- Triaged the chaos shard's error logs for the last 24 hours and traced the largest cluster to a false alarm. A burst of login-authorization rejections, connection-retry warnings, and an hourly stale-record sweep looked like a login-capacity outage, but the login-token store and its expiry were healthy and the master never lost its heartbeat during the window; the rejection flood was one client stuck reconnecting with a login token that had already aged out after an earlier vendor-search request errored. Added a case study to the capacity/outage runbook so the pattern is read correctly instead of chased as an outage.
Dev infrastructure
- Documented the client-control harness end to end now that its automated launch, log in, run a test, and exit flow works. The new writeup walks through how a freshly launched dev client is driven all the way to in-world, which is the involved part: a fresh client has to be taken through the main menu, credentials, the one-time agreement and notice dialogs, character selection, and the scene load. One dev-only setting carries the client from the menu straight into a chosen scene, and the agreement dialogs are auto-accepted only on that dev path, so nothing changes for real players. Also captured the one-time requirement that the test account already have a character, and the end-to-end result: the runner logs in by itself, lands in-world, runs its scripted steps with before and after screenshots, and exits clean.
- Small cleanup: the game writes a couple of runtime id files into whatever folder it starts from, which had been landing in the tools directory. The runner now launches the client from the client's own folder so those files stay out of the source tree, and added them to the ignore list. Noted the character and dev-build requirements in the tool's readme too.
- The client-control harness gained three read-only query verbs: nearby entities and interactable objects (names, kinds, dispositions, distances), the player's inventory (quantities, equipped slots, weight), and skill levels plus learned recipes. A test-driving agent can now find things by name instead of guessing at coordinates; results are filterable and capped so replies stay under the transport's frame limit.
- Added the harness's first real gameplay verbs: target a named entity, interact with objects and NPCs (the same E-to-use path a player triggers, with range checks), use a skill (returns the exact fail reason when it doesn't fire, e.g. not enough focus or no target), use an inventory item via the double-click path, equip gear, and follow or autorun for movement. All of them go through the same code the player drives, so combat rules and the server's account-permission checks still apply.
- The harness can now run a curated set of developer-console commands and read back their output. The console's execute logic had no callable entry point (it lived inside the on-screen console's input handler), so it was extracted into a shared method used by both. Only an approved list of test-setup commands is allowed (give item, learn recipes, set skill levels, crafting test mode, and similar); arbitrary code paths are refused, and the server still re-checks account permissions on anything privileged.
- Finished the harness's crafting support: a craft command finds a matching station, opens it, pulls the recipe's ingredients from the backpack onto the table (the same auto-fill the recipe list uses), presses Craft, and reports each item's result and experience before closing the window so products return to the backpack. It drives the real crafting window rather than calling the server directly, because the crafting table is a server-side temporary bag whose safe cleanup lives in the window's close path, and the station's crafting bonus only applies while the window is open. Missing ingredients come back as an exact shortfall list, and the crafting test mode command gives deterministic, timer-free results for repeatable tests.
- Wired the server-report regeneration job into the build farm end to end (Gitea #140). The per-scene reports the game servers read town and player-owned-town data from were last regenerated by a legacy build job that has been paused since last fall; that staleness is what made a newly created town's houses vanish in June. The new nightly job updates its build tree first so it always reports on current data, regenerates the shared data-contract code, runs the headless report build, sanity-checks the output, and archives it; the release job picks that archive up automatically, and a small fetch script gives hand-run deploys the same fresh data.
Game launcher
- Removed the launcher's “Connections” control, which let a player change how many files download in parallel. It's now fixed at 4 for everyone. The parallel-download engine is unchanged, so distant players keep the multi-stream speedup, and the setting no longer persists to the launcher config.
- Laid the groundwork for macOS and Linux launcher builds: added the extra build targets, guarded the Windows-only installer code (shortcuts, permissions, elevation) behind an OS check, and set the Unix executable bit when the launcher installs or self-updates. Windows behavior is unchanged (build clean, 43/43 tests, and the macOS/Linux binaries cross-publish). The macOS packaging and code-signing stage is deferred for now and tracked as a task.
2026-06-30 · 17 items
Headlines
- The client-control harness can now run a whole test from one command: it launches a dev client, logs it in, drives a scripted scenario or hands the live client to the assistant, and exits with a pass/fail code. Three new in-client controls (log in, change scene, quit) and a launcher script tie the lifecycle together.
- Cleared a Unity 6 deprecation warning across the entire art library by moving every 3D model off the engine's removed "External" material setting: 12,480 model importers switched to embedded materials with their existing material links preserved exactly. A new editor tool did it in about a minute by rewriting import settings instead of reimporting each model, and ran on a separate build machine so the working editor stayed free.
- Took apart the live deployment and automated-systems chain after the missing-houses incident and built permanent guardrails: the server now logs an error and alerts ops when a town's report data is missing instead of skipping the town, the deploy validates the report it ships and rejects a stale one, and a new reference maps every background job the running server depends on.
- New GM live-ops dashboard: a web page listing everyone online with account name, character, zone, time in zone, party, gold per hour and experience per hour, plus buttons to read a player's recent chat and to teleport to them with GM-ghost on by default. The roster is read from the game server; the teleport and ghost actions drive the GM's own client over the browser-control link.
- The in-game Oracle's skill help was describing the wrong effect for abilities across many combat and magic schools — it listed Thrust's bonus debuff as an armor cut when the skill actually reduces the target's healing. Re-checked every skill and spell against the game's own data, fixed the mislabeled effects and several wrong skill names across all the help guides, and switched the help text to the in-game term “Specialization” (formerly “Mastery”).
- Vendor search in large player-owned towns dropped from two to three minutes to seconds. Listing a town's vendors made the server repeat the same pair of database lookups for every item on a vendor; it now does them once per vendor and reuses the result, with identical output and no data changes. Deployed to the test shard.
Dev infrastructure
- Added an automated test runner for the client-control harness. One command launches the dev client, logs it in with test credentials from the environment, waits until it is in-world, then either runs a scripted scenario (a list of control steps with assertions, exiting with a pass/fail count and a saved report) or holds the running client for the assistant to drive live and quit when done. It is backed by three new in-client controls — programmatic login, scene change, and a clean quit — plus matching assistant tools and a shared connection module. Credentials are never stored in the scenario files. Compile-checked in the engine, the adapter's nine-tool test passes, and the runner's argument and exit-code handling is verified; a live run against a launched client is the remaining check.
- Finished that automated login and verified the whole cycle live. Getting a freshly launched client to a logged-in, in-world state meant driving the entire front-end sequence: past the main menu, through credentials, the one-time agreement and notice dialogs, character selection, and the scene load. The one-time agreement/notice dialogs are now auto-accepted only on the dev/automated login path (a flag real players never set, so normal logins are unchanged); a single setting then carries the client straight from the main menu into the chosen scene. End-to-end result against a launched dev client: it logs in by itself, lands in-world on the overworld, runs the scripted steps — read state, print location, read chat, turn, and before/after screenshots the assistant reads back — and exits zero. Added a short settle after reaching in-world so screenshots show the rendered world rather than the final load frame. (Gitea #135)
Unity 6 migration
- Migrated every model importer off Unity 6's removed "External" material location, which logged an obsolete-API warning on each affected model at import time. A text search badly undercounts the scope, so the real importer values were read instead: 12,480 of about 15,800 model importers were still on the legacy setting. All were switched to embedded materials, with each model's external material files first captured into an explicit remap so the materials are preserved exactly; animation and no-material models just flip. 12,457 metadata files changed here — source models were untouched, so there is no large-file churn. Added the MaterialLocationExternalSweep editor tool (dry-run and fix menu items plus a headless mode) that writes the corrected import settings without a per-model reimport, turning a serial multi-hour pass into about a minute; the actual reimport happens per machine on the next editor open. The sweep ran headless on a separate build machine and the changed metadata was brought back. The model preprocessor only sets defaults on first import and never touches this field, so the fix is one-time and will not reappear.
Server operations & reliability
- Wrote a single end-to-end reference for the live deployment and automated systems: how a server is built, deployed, and run; a dependency-and-failure matrix listing every artifact the running server reads that a separate background job produces (server reports, the pledge report, database and item-log backups, schema indexes) and how each one fails when that job is skipped or its output goes stale; the scheduled-job inventory; and a prioritized gap list. Most of these failures were invisible: the server kept running and served wrong or empty data with nothing logged.
- Hardened the player-owned-town report path against the staleness that caused the missing-houses incident. The server now logs an error and alerts ops when a town's report entry is absent instead of returning early, and records how many towns each report load covers (a number to watch against the known town count). The test-shard report refresh now validates the combined report (well-formed, non-empty, contains town entries) and fails the deploy if it is bad; the deploy also refuses to ship a report older than the town-ownership data unless explicitly told to. Verified the server build compiles and the deploy scripts parse.
- Corrected the build-server retirement guide: the metrics and notification scripts it listed as recoverable only from the old version-control server are already in this repository, so that part of the migration is not blocked on recovering them.
- Removed the temporary town-setup and house-load debug logging left in while chasing the incident, keeping the one real fix it proved out: a single bad lot marker no longer aborts a whole town's setup.
- Sped up vendor search in large player-owned towns, which could take two to three minutes. Listing a town's vendors pulls every item from every vendor, and for each item the server made two extra database lookups to resolve where that vendor sits — lookups that return the same answer for every item on the same vendor. Those are now computed once per vendor and cached for the listing, so a town with thousands of items no longer repeats thousands of identical lookups. The results are byte-for-byte the same as before and no stored data changes; verified the server build, then deployed to the test shard. The remaining client-side list-build cost and a real server-side search filter are noted as follow-ups.
- Added a scheduled build job that regenerates the server's per-scene reports headless, replacing a paused legacy job. Nothing was regenerating that source data, so it could drift and a deploy would ship outdated town data; the new job refreshes it nightly and archives it for the deploy to pick up. The first run still needs an on-machine check since it opens every scene.
Live-ops & GM tools
- Built a GM live-ops dashboard (gm-dashboard): a local web page that lists every online player with account name, character, zone, time in zone, party, gold per hour and experience per hour, a button to view a player's recent chat, and a button to teleport to them with GM-ghost mode on by default. The in-client browser-control link only sees the operator's own zone, so the shard-wide roster and totals are read from the game server's database and a player's chat from the log store, while the teleport and ghost actions ride the browser-control link to the GM's own client (those move the GM's avatar). Per-hour rates and time in zone are derived by sampling the roster between refreshes, so no database schema change was needed. The browser talks only to a small local backend that holds the server access and the control token. Backend, page and the rate math are smoke-tested with mock data, 14 checks pass.
- Supporting changes for the dashboard: the group server now logs every social-channel message through the existing log pipeline so a player's recent chat is queryable by sender, and the in-client control link gained a focused GM-ghost toggle (GM-ghost is a console-only command the chat passthrough can't reach). The server build compiles, and all of the dashboard's reads are pinned to the test shard.
- Live-tested the dashboard's roster read against the running test shard: it works end-to-end, and the first run found a bug — one fetch script's database parameter name collided with PowerShell's built-in debug switch, which had been aborting the query. Renamed it; the read now returns cleanly (empty right now because no character is logged in).
- Fixed two issues found on the dashboard's first live run. The Zone column showed a raw instance GUID; it now shows the readable scene name (for player-owned towns, the town name), with the raw id kept in a tooltip. The recent-chat popup was empty because it only queried the new server-side chat log that isn't deployed yet; it now also reads the existing client chat stream already in the log store, so a player's global and local chat show immediately. Both verified live against a logged-in player.
Documentation
- Corrected the in-game Oracle's skill and spell guides against the game's authoritative skill data. Most abilities had their secondary effect mislabeled: Thrust and Rend were described as cutting the target's armor when they actually reduce its healing, and polearm, bludgeon, ranged, shield, and many spells had the wrong effect type listed. Fixed across all 22 skill and spell guides — both the player-facing version the Oracle answers from and the engineering reference — along with several wrong skill names (Double Thrust is really Double Slash, Tumble is Evasion, Concentration is Tranquility).
- Renamed “Mastery” to “Specialization” across the skill docs to match what the game calls it, leaving the skills that are genuinely named “Mastery” (such as Blade Mastery) unchanged.
- Added a verifier that reads each ability's real effects from the game data and its in-game tooltip, so the skill guides can be checked against ground truth instead of by hand. It caught the original mismatch and confirmed every fix.
- Started a database migrations log: a running record of every change that touches the database — fields, collections, indexes, and one-off data fixes — so an existing data set can be carried forward into another test environment with a clear answer to what, if anything, must run against it. Added the first entry and linked it from the docs index.
2026-06-29 · 69 items
Headlines
- Portuguese is now fully translated in the game. Every NPC conversation line plus all the UI, item, skill, journal, help, title and script text was filled in, taking Portuguese from 28% to 99.7% coverage and dialogue from 9% to 100%. It is now the most complete language we ship, just ahead of German. The only text still in English is the in-world books, which go out on a separate track. A two-model meaning-and-grammar audit then reviewed all 85,783 new lines and applied 2,398 fixes for mistranslations, wrong terms, and grammar.
- Fireball traps stopped creating and destroying an explosion object on every hit: the explosion now comes from a reuse pool, and two long-standing bugs fell out with the change. Explosions had been cut off at a fixed one second instead of playing their full effect, and every blast was throwing a discarded error from a reference the trap never filled in.
- Mapped where the project could be knocked offline by a denial-of-service or UDP flood and wrote the plan to handle one. The public game shard is a single box that forwards its game traffic with no per-source rate limit, exposes connection and login limits that are per-connection rather than per-source, and carries some obsolete policy listeners worth retiring; the web portal answers on a directly reachable address with no global request limits. The honest limit, stated up front, is that a truly large flood can only be absorbed upstream at the hosting provider or a content-delivery network, or sidestepped by repointing players to clean capacity. Because a few hours of downtime is acceptable, the plan leans on cheap preparation rather than always-on defenses. Nothing was changed; this is the playbook to act on if it happens.
- Generic NPCs that come in both a male and a female version no longer share one voice. The voice tooling had collapsed each to a single voice, so a bearded guild registrar could speak with a young woman's voice; every such NPC now gets its own voice per gender, fixed across 135 shared-dialogue character types.
- Fixed a regression in the object-pool warmup that could briefly fire combat effects, particle bursts and audio at the world origin during a map load. The pools build their starting objects off the main thread, and the new copies were active for a few frames before being switched off; they now build hidden and only come alive as the pool hands them out.
- Audited every place the server stores player-sent data and found 11 ways a player could send data that, once saved, jams the game at the storage layer rather than just cheating. A single record pushed past the database's hard 16 MB size limit can no longer be read or written, stranding that character, party, plot, or vendor. The confirmed cases sit in plot and scene data, the knowledge lists, party storage, and a vendor sale that can leave shelf items locked; all are written up with a fix plan and filed as a tracking epic with 13 tasks. No fixes have shipped yet; this pass is the audit and the plan.
- The in-game chat moderator now runs on our own hardware instead of a paid API, so chat content stays in-house at no per-message cost. It reviews messages in the background against a written policy we control and can pull a flagged one back from everyone's chat. The policy is written for a game: it allows fantasy combat talk like "I'll kill you in the arena" but removes real-world threats, doxxing, real-money trading, and scams. It now covers all the social chat channels and is being tested on the test shard, off by default, in watch-and-report mode first.
Client
- Fixed a null-reference error logged at client shutdown. The options and login screens unsubscribed their save-to-file dialog handlers through a lazy accessor that recreates the dialog on demand; during teardown the dialog is already gone, so the accessor tried to rebuild it, failed to load, and the cleanup threw. Added a non-creating accessor that returns the existing dialog or nothing, and skip the unsubscribe when there is no dialog. (Gitea #136)
- Pooled the fireball trap's explosion effect. The trap created a new explosion object and destroyed it on every impact; it now draws from and returns to a shared reuse pool, with the timed destroy replaced by a release back to the pool and a self-release on scene change. Keeps a fallback to the old path if the pool is unavailable.
- Fixed the explosion length, which had been dead code: because a fresh object was created each time, the code that measured the effect's real duration ran a frame too late and never applied, so every explosion was cut to one second. The duration is now computed before first use, so longer explosions play in full. This is a visible timing change, pending an in-game check.
- Removed a back-reference on the explosion that the trap never set, which logged an error and threw a null-reference (caught and discarded) on every detonation. The projectile already resets itself, so the field was redundant.
- Fixed the async pool prewarm so warmup objects never run live. The pools warm their initial instances off the main thread with the engine's batched async instantiate, but those copies integrate active and were only switched off in one end-of-batch callback, so across the roughly 2,050 prewarmed objects they sat active at the world origin for several frames during a scene load. A combat-effect prefab runs a physics overlap query every frame, and others play particles or audio, so the warmup could play effects and apply spurious hits. The batch now builds under an inactive holder, so nothing ticks until each instance is reparented into the pool, sent its create hook, and switched off, matching the old synchronous warmup. Verified in the editor.
- Scoped the async-instantiate de-hitch work to the pool warmup and split the rest out. Its other named targets do not fit that engine API: NPC spawn waves go through the networked spawn path, and plot-decoration loading is tied to synchronous per-item setup. Both were filed as separate follow-up tasks.
- Added native-allocator metrics to the per-zone load logging. Each load already logged a timing breakdown but never recorded the engine's native memory use, which is what a native allocator change actually moves. It now also emits a companion line with native reserved, peak and allocated totals, the allocated growth across the whole load and across the scene-load and avatar phases, and the Mono heap in use.
- Reworked the load-timing line to measure the headline total from a monotonic clock and to break out the per-load garbage-collect time and any span the named phases do not account for. The existing fields and key=value format were left in place so the log dashboards keep parsing it.
- Added a per-build switch for the engine's new multithreaded native memory allocator, so its effect on multithreaded zone loads can be measured and kept only if it helps. The client reads which allocator a build was set to and labels each load with it, so on and off runs can be told apart in the logs.
- Stopped the companion app from reopening the in-game chat box. With the chat box closed, sending a message or switching tabs in the companion browser or desktop app used to force the in-game window back open; the two companion-only paths no longer call show. While a companion is connected, incoming messages no longer auto-open the in-game box either, since the player is reading chat in the companion. Sending and tab-routing still work with the window hidden, because a hidden chat window stays active and only has its panel alpha and colliders turned off.
- Left the in-game ways to open chat untouched: pressing Enter, Slash, or Shift+Alpha, or clicking the chat input, still opens the box as before, even while a companion is connected. When no companion is attached, behavior is unchanged. Compile-checked the client assembly; in-game verification pending.
- Fixed
/companion desktopfailing to launch the desktop companion in a real build. The launch code was the only place in the client that started a process with shell-execute turned off, which the player runtime's process API does not handle, so the spawn threw before the app ran. Switched it to the same shell-execute path every other launcher in the client uses and gave the process a working directory instead of an empty one. The failure also now logs the full error and the resolved exe path. The bundled app ships self-contained, so this was the spawn call, not a missing runtime on the player's machine. Compile-checked; in-game verification pending on the next build. - Fixed the in-game
/oraclecommand opening nothing: the command ran but no window appeared and no error was logged. The Oracle window's UI document had lost the panel-settings asset it needs, so the UI Toolkit panel never built and the window-show code hit a guard and returned silently. The asset had been added with the window, then an engine-upgrade asset-reserialize pass dropped the asset file while leaving its meta behind, and a later orphan-meta cleanup deleted the stranded meta, so both were gone even though the prefab still pointed at them. Restored the asset with its original id, so the prefab reference resolves again. Verified in the editor that the window's panel settings and theme now load; takes effect on the next client build. - Fixed a load-screen hang when entering a player-owned town. If the player's spawn point sat on a lot that had not finished initializing, the check that decides it is safe to place the player kept waiting on that lot's contents and never cleared, so the load stuck near the end and the player never entered the world. It now skips lots that were never initialized, since they have nothing to load. Pinned it with temporary diagnostics and confirmed live that the player spawns and the town loads.
Trust & safety
- Added an automated moderator for global chat (the Universe and Universe-Trade channels), which previously had no content moderation at all. Player messages send with no added delay; a small hosted language model reviews each one in the background, and a flagged message is pulled back from every client, including the sender's own copy, while the team is alerted. There is no pre-send gate, so chat speed is unchanged; the tradeoff is that a flagged line shows for the model's round-trip before it disappears. The prompt targets the abuse that generic safety filters miss: real-money gold/account selling, scams and impersonation, doxxing and threats.
- Built the unsend path to reuse what already exists rather than add new networking: each global message carries a client-generated id inside the normal chat payload, the redaction command is itself a message on the same channel (so it reaches players on other zone servers too), and removal reuses the existing chat-log rebuild. No new network message type or generated code was needed, and a message that has already scrolled off is simply left alone.
- Shipped behind an off-by-default switch with a two-phase rollout: phase one observes and alerts only, with no redaction, so the model's accuracy, latency and cost can be measured on real traffic; phase two flips one flag to enable the unsend once accuracy holds up. The model never blocks chat: any timeout or unparseable response leaves the message in place. Compile-checked both the server and the client.
- Reworked the moderator to run on our own hardware instead of a paid third-party API: the model now runs on a GPU workstation we already own, so chat content stays in-house and there is no per-message cost. It is an open-weight safety model that classifies each message against a written policy we control rather than a fixed built-in ruleset.
- Wrote the policy around the distinction that actually matters for a game: fantasy combat and PvP trash-talk are allowed, while real-world threats, doxxing, real-money trading, and scams are removed. The policy is a plain text file shipped with the server, so it can be tuned without rebuilding, and the model reasons about each message against it.
- Broadened coverage from the two global channels to all the social channels (universe, trade, guild, party, noble); party chat got its own unsend path. Private whispers are watched and reported but not yet unsent. Proximity chat is sent peer-to-peer with no server relay, so it can't be moderated this way. Targeting the test shard first; both server and client compile clean.
- Stood up the model end to end on our own GPU: installed the open-weight safety model on a workstation GPU we already own as a background service that keeps it loaded, and connected it to the test shard over a private encrypted tunnel so the game server can reach it without exposing anything to the public internet.
- Validated it against boundary cases on the real GPU: in-game combat threats ("kill you in the next siege") are allowed, while real-world threats, gold-for-cash sales, and fake-reward links are caught, and legitimate in-game trades and trash-talk pass. Classifying a message takes a couple of seconds once the model is warm, which is fine since moderation runs in the background and unsends after the fact.
- Deployed it to the test shard in watch-and-report mode: rebuilt and shipped the game server, pointed it at the model over the tunnel, and confirmed the server loads the moderation policy on startup. It now classifies real chat across the social channels in the background and logs what it would flag; nothing is unsent yet. The deploy was kept isolated from an unrelated in-progress fix on the same shard.
- Added a switch to also moderate GM/dev accounts (off by default so real staff announcements are never touched, on for the test shard) since otherwise a developer testing on their own GM account is skipped and never sees the moderator act. Switched the test shard to enforce mode so flagged messages are actually unsent, and kept the model loaded in memory so a check comes back in a second or two instead of the cold-start delay.
- Fixed a real miss found in testing: the model thinks before it answers, and on a longer message its reasoning ran into the output limit so the final verdict was cut off, which made the check fail safe and let the message through. Raised the output limit well past what any check needs, set the model's context window to its maximum (still fits entirely on the card), and added a fallback that reads the verdict from the model's reasoning if the final line is missing. Re-tested with several gold-for-cash and real-world-threat phrasings and all were flagged in one to three seconds.
- Tightened the policy to also remove strong profanity (f-bombs) and any racist or homophobic slur, on top of the existing real-world threats, gold-for-cash sales, scams, and doxxing. Mild trash-talk ("you're trash, uninstall") and in-game combat talk still pass. Since the policy is a plain text file the server reads, this went live by editing the file and reloading, no rebuild; verified the three new cases are caught and the allowed ones still pass.
Build & CI
- Fixed the macOS client black-screening on launch. The build's copy step put the asset bundles, license files, and certificates in a Windows-style
<Name>_Datafolder beside the app, but a Mac build is a.appbundle whose data lives inside it atContents/Resources/Data, so the running client found no bundles, hit a null reference in the asset-bundle loader, and never drew anything. The copy step now uses the.applayout for Mac builds and leaves Windows and Linux unchanged. Found by scanning the test-shard logs for the Mac tester's sessions, which showed four identical failed launches. Compile-checked the editor assembly; a rebuilt Mac build is still to be verified. - Noted from the same logs two more Mac-only gaps that do not block launch: the audio (FMOD) and Steam native libraries are Intel-only with no Apple Silicon build, so they fail to load on the tester's M-series Mac and the client runs without sound or Steam. Both errors are caught; shipping Apple Silicon libraries is filed as a follow-up.
Audio
- Gave per-gender voices to generic NPCs. Many of them (guards, merchants, bankers, registrars, trainers and others) ship as a male and a female prefab that share one dialogue file, so the offline voice casting treated them as one character and the opposite-gender version spoke with the wrong voice. The voice map now carries a separate entry per gender, and the client chooses by the speaking NPC's gender, falling back to the old single entry when no per-gender one exists.
- Kept the audio already generated and only added the missing voice. The casting step now preserves each NPC's existing voice rather than re-rolling it, so a re-cast never throws out or re-bills clips already made. Adding the second gender across 135 character types came to 6,568 new clips; nothing was removed from the voice host.
- Fixed the reported case: the Ardoris Guild Registrar, a bearded man who spoke with a young woman's voice, now uses a gruff male voice while the female registrar keeps hers. Documented the cause and how to re-cast a single NPC's voice.
- Corrected the recorded gender on six named Ardoris NPCs (Erin the Castle Guard, Cai Lau, Naoko Khan, Merrill, Parker, Sabine Eichner) whose prefab gender field disagreed with the voice a person had hand-picked for them. The voices already matched the characters, so the stale gender field was the bug; it also drives in-game pronouns, so fixing it corrects how the dialogue refers to them. No voices or audio changed.
- Restored cast sounds that went silent on a set of skills after the engine upgrade, where one skill (Light) made no sound while another (Heal) was fine. The two play their cast sound through different paths: the silent ones scheduled playback through an older delayed-play call that, on the new engine, no longer loads a sound clip unless it was preloaded, so the scheduled sound fired with no audio data. They now load the clip first and play it through the same call the working skills already use, keeping the short delay that lines the sound up with the cast.
- One change to the shared effect code covers all of the roughly two dozen affected effects, including partly-muted ones (Ice Fist layered a legacy sound on top of a working one); effects already on the newer path are untouched. Confirmed in the editor that the clip loads on demand and the play path is reached; the audible check rides the next build. Documented the two audio paths and the cause under docs/systems.
- Swept the rest of the code for the same delayed-play call and found one more use, the bird-flock scare sounds, where the same regression had silenced them (the flocks' idle and flight calls used the working path and were fine); fixed it the same way. The one other scheduled-sound spot, the player instruments, was checked and is safe because instrument notes are preloaded and decompressed up front, so their audio is ready the instant a note is scheduled.
Dev infrastructure
- Added prev/next open-issue navigation to the team's Gitea issue tracker, which has no such feature built in. Each issue page now shows Prev/Next buttons, n/p keyboard shortcuts, and a position count, so you can move through issues without going back to the list.
- By default the buttons follow the order of the issue list you came from (its current sort and filters, captured across all pages); opening an issue from a direct link instead steps through the open issues by number. It reads the list over the existing browser session, so no token is embedded in the page.
- Shipped as a small static script loaded through Gitea's custom-template hook, with no fork or rebuild of the server. The source is versioned in the repo and pushed by a new deploy script, with the mechanism and upgrade caveats documented under docs/operations.
- Built the first slice of the client-control harness from yesterday's design. A new component in the client, active only when the game is launched with a
-clientcontrolflag, opens a local-only, token-gated socket and answers four requests: read player state (scene, position, health and focus, frame rate, character name), take a screenshot, submit a chat or slash command, and a health check. It reuses the existing companion-app socket rather than adding a new in-client web server, and is off in a normal launch. Compile-checked in the engine; a live test against a launched build is still pending. (Gitea #135) - Added the out-of-process adapter that exposes those requests to the assistant over the Model Context Protocol, including a screenshot tool that returns the saved image for the assistant to look at and keeps the file on disk as bug evidence. Wired it into the project's assistant-tool config and verified its tool list end to end with no client attached. The reflection-driven and avatar-driving control verbs from the plan are deferred to later phases.
- Added an integration test for the adapter that runs it against a stand-in matching the in-client side's exact message format, exercising all four requests including the screenshot returning an actual image. It passes, which also confirms the two halves agree on the wire format. The in-client half is compile-checked only; a test against a real launched build is still pending.
- Restricted the client-control component to dev/GM client builds only (a compile-time switch), so it is absent from the retail client entirely; it still also requires the launch flag and a local token. Added two more requests: read recent chat, and turn the avatar to face a world point or compass heading (the first of the look/move family). Confirmed teleport already works through the existing command path (
/teleport x y z) given a dev account with GM power on, no new code. Screenshot, speak-into-chat, read-chat, teleport, and look are all in; "look at a named object" still needs a name-to-object lookup. Recompiled clean in the engine and the adapter's six-tool test passes. - Added a "GM client" checkbox (off by default) to the in-editor Build Chaos window, so a dev can build the GM/dev client themselves. The standard chaos build produces the normal client, which compiles the control component out; ticking the box compiles the player with the GM define on, pulling in the developer console, GM commands, and the control bridge. It is applied as a player-only define, so it never edits project settings or forces an editor reload, and the build is labeled GM in the dialog, the progress window, and its build record. Also wired the same option into the headless build command. Compiled clean.
- Restored HTTPS on the in-game Oracle assistant's API endpoint, which had taken the
/oraclecommand down. The endpoint was serving the wrong TLS certificate, so the game client's connection failed its certificate check before any request reached the service. The correct certificate was already present; the web server had stopped referencing it after a redeploy rewrote the site config without the HTTPS block. Re-attached the certificate and reloaded; the command works again, with no client update needed. - Made the Oracle service's provisioning script re-attach HTTPS itself on every redeploy, so rewriting the HTTP-only site config can no longer silently drop the certificate the way it did here. Updated the operations doc to match.
Localization
- Filled all 85,783 missing Portuguese lines with the same LLM pipeline used for German. A bulk machine-translation pass ran over 2,996 batches, then a stricter repair pass re-did the lines that failed the markup and format validation gate. The final 10 records, which had broken markup in the English source itself, were hand-fixed to match the existing German versions.
- NPC dialogue went from 9% to 100% translated. 79 conversation scenes that had no Portuguese text before now ship one, each with a generated Unity meta file, and the rebuilt client string tables are staged for the chaos shard.
- Ran a meaning-and-grammar audit over all 85,783 new Portuguese lines, the German-style pass that catches what the markup checker cannot: a fluent line that says the wrong thing. One model reviewed every line against its English source and flagged 7,233 as suspect; a second, stronger model independently re-checked the most serious flags and threw out about a quarter as false alarms. 2,398 fixes survived both judges and the markup checker and were written; the lower-confidence flags were set aside for a native-speaker pass.
- The fixes were real errors, not stylistic churn: surnames that had been translated as words (Captain Jack "Price" had become "Preço"), invented terms put back to the established name, a clickable in-dialogue keyword that had been duplicated, and infinitive verbs corrected to the imperative the English used. Coverage is unchanged at 99.7% because these change quality, not count. Re-mined the terminology glossary from the corrected text and rebuilt the client string tables.
- Hardened the translation workflow so it can take an explicit list of batches and caps how many sub-agents it launches at once, after the auto-discovery step over-counted at full scale and ran past the per-run agent limit. Updated the coverage snapshot and the per-language runbook.
Design tools
- Split the ConvoBuilder dialogue-authoring tool's 7,681-line MainForm, a single WinForms class that mixed the editor UI with database, Perforce, and file-export logic, into partial-class files by concern: one each for the database connect/load/delete code, the Perforce calls, the JSON/HTML/XML export and serialization, and the nested data classes. About 5,400 lines of UI handlers stayed in the main file. Part of the oversized-script (over 5,000 lines) paydown.
- The split relocated code verbatim with no edits, so behavior is unchanged. Confirmed by a before/after compile producing the same result (same single pre-existing warning, no new errors) and a line-by-line check that the trimmed main file equals the original minus the moved blocks.
Server
- Audited the server's handling of player-sent data for input that gets stored as-is and could break the game at the database layer: a document grown past the database's hard 16 MB limit (after which it can no longer be saved or loaded), an over-long value in an indexed field, an uncaught duplicate-key error, an unescaped player search compiled into a database query pattern, a player string used as a document field name, unbounded list growth, and integer overflow. The sweep produced 106 candidate findings; a second checking pass discarded 32 that were already handled or wrong, leaving 11 confirmed game-blockers.
- The confirmed cases sit in the plot and scene records that accept a client property bag with no size or key check, the per-character and per-account knowledge lists, party storage, and a player-vendor sale where an overflowing price throws partway through and leaves the shelf items locked. Recorded the findings, the failure-mode taxonomy, and the fix approach (reject bad edits with a clear error, clamp only where data loss is harmless, enforce at both the request handler and the database-write layer) under docs/security, and filed a tracking epic plus 13 fix tasks. This pass is the audit and plan; no fix code yet.
Test shard / data
- Reseeded the test shard from a sanitized copy of the live game database: 8,798,757 records restored with zero failures, giving the server production-scale data to test against. Sensitive collections (payments, cached credentials, anti-cheat and hardware-tracking data) are dropped before the data leaves the build machine and again on the target, and per-record personal data (last login IP, hardware-fingerprint history, and the private notes players leave about one another) is cleared after the restore. Snapshotted the box for rollback first and re-ran the security check after, which stayed green with no exposed database ports.
- Turned the run into a documented, repeatable procedure plus a separate live-safe restore runbook that covers any target up to production recovery, and committed the on-box worker script that performs it. Wrote down the traps that bit on the first attempt: the database restore tools the current server package no longer bundles and must be installed separately, the restore flag the newer tools require, rebuilding indexes from server code instead of trusting the dump, dropping the sensitive collections a restore won't remove on its own, and running the multi-step restore as a detached task so it survives a dropped admin session.
- Added a way to log into the test shard as a specific restored player's account, so we can inspect a real player's characters and inventory in the client: pointing a test login at a chosen account's id makes the game load that account's data, with no extra data copied. Documented the recipe and how to reverse it.
- Tracked down why a player-owned town on the test shard showed almost none of its houses. The lot, deed, and house data was all intact; the cause was a server-side report that records each town's owner and size. The copy on the test shard was an old snapshot from the data restore that predated the town, so the server had no record the scene was a player-owned town, its town setup returned early, and the houses never loaded through that path. A client-side load hang (fixed separately) had made it look fully empty.
- Wired that report's regeneration into the test-shard server deploy. The live environments rebuild it from current game data on every deploy, but the test-shard deploy had been shipping whatever stale copy was already in the build. The deploy now regenerates it from the current game assets when freshly built source reports are present, and warns with an age check when they are not, so a stale report can no longer ship unnoticed. The owner and size come from a committed game asset, so the test shard can build a correct report from its own checkout with no live account data. Added a standalone refresh script and a doc covering the generation pipeline; the scheduled job that rebuilds the source reports is currently paused, which was flagged rather than changed.
Patcher / Launcher
- Stopped the patch publish from shipping Unity's "do-not-ship" build output. Every deploy copied the whole build tree, including the IL2CPP
SotA_BackUpThisFolder_ButDontShipItWithYourGamefolder (generated C++ source and debug symbols) and the Burst debug folder, which players were then downloading. Added a publish-side ignore filter that drops them before staging, so they are never copied, listed in the manifest, or pushed; the local build keeps them for crash symbolication. - Kept the new ignore separate from the existing exclude list, which is client-side delete protection (it stops the updater removing player files but still ships the files). The ignore defaults to the two folders Unity marks do-not-ship, is wired through the publish CLI and the patch-publish script, and is covered by tests for copy and no-copy publishing.
Security / DDoS readiness
- Audited every spot exposed to a denial-of-service or UDP flood across the game shard and the web/portal host, and ranked them by live versus latent risk. The top live exposure is the game's forwarded UDP traffic, which has no per-source rate limit and no kernel-level flood tuning on the host. Behind that: connection and login caps are per-connection rather than per-source, so one source can still wedge logins; the legacy cross-domain policy listeners are an amplification vector and look obsolete; and the inter-server registration ports are bound wide-open but, on this deployment, are not actually forwarded to the internet, so they are a latent hardening item rather than a live hole. The web host answers on a directly reachable address with no global request limits.
- Turned it into a before/during/after plan weighted toward cheap preparation, since several hours of downtime is tolerable and repointing players to clean capacity is therefore a real response. The headline prep is switching the client from a hard-coded server address to a name, so a move becomes a quick name-record change instead of a full client rebuild and re-patch. The rest is staging the hosting provider's edge firewall rules and a content-delivery network in front of the web tier without turning them on, ready-to-apply host and server hardening, and capturing a normal-traffic baseline plus alerting so an attack is distinguishable from a launch surge. Nothing was applied; filed as a roadmap item and linked into the ops docs and the pre-deploy security check.
Planning / docs
- Evaluated moving the Unity client's asset loading to Unity Addressables. The client is already on a custom asset-bundle system, not the older Resources-based loading the original task assumed, so the real choice is Addressables versus the existing bundles. The advantages are developer-side only: getting off a deprecated build API, deleting hand-written GUID-catalog and reference-counting code, and gaining Unity's bundle authoring and analysis tooling. There is no player-facing gain, because the patcher already ships only the changed bytes and the loader is already asynchronous.
- Wrote a concrete prototype plan: a three-to-four-week spike behind the existing asset-load facade that builds one pilot category in parallel with the current bundles, behind a build switch and with a shippable fallback, and that must clear three gates — no regression in patch-download size, parity on load time and memory, and a demonstrated tooling win — before any larger migration. Left it deferred to run only on explicit go-ahead and filed it as a tracking task. (Gitea #137)
- Designed a way for an automated tester or operator to control a shipped game client from outside the process: run scenarios, read game state, and take screenshots. The plan reuses the existing loopback chat bridge, which already runs in the shipped build with a local-only socket and a per-session token, rather than adding a new in-client web server, and puts a small out-of-process adapter in front so an agent can drive it. Phased from read-state and screenshots first up to driving the avatar last; filed as a roadmap item and a tracking task. (Gitea #135)
- Settled the screenshot path, since seeing the client is the most useful part: a capture is written to disk as a file and the adapter hands the image to the agent to evaluate. The on-disk capture code already ships in the client, and saving every shot doubles as durable evidence to attach to a bug report. Noted it needs a windowed build with graphics, not a headless one.
- Evaluated what it would take to fully replace the legacy web stack — the public site, forum, store, real-money payments, and the account/login, entitlement, and purchase-delivery services the game server still calls at runtime. It is the one large system left with no replacement built. Produced a project-wide what's-left ledger and a deep dive on the money-and-account path: checkout, delivering bought items into the game, and granting monthly and subscription rewards.
- Recommended replacing it gradually rather than in one big-bang rewrite: stand up a new service that speaks the existing signed account and reward contracts in front of the current data, then retire the old surfaces one at a time, preserving years of purchase and account history in place. Decided to keep and modernize Stripe and PayPal and drop the two unused processors. Phased so each surface (login, purchase delivery, payments, store, account portal, forum) ships and is verifiable on its own; nothing built yet. Fleshed out the website-rebuild plan and updated the roadmap.