A character's editor level is its account's GM level for accounts at editor_gm_level
or above, and 0 for everyone else (it was 1 or 0). It is written as the save's edit
attribute, sent with the character after the GM level (the editor flag, then the
level) and given in answer to the GetEditorLevel game message.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
/api/v1/zones/{zoneId}: send a zone's edited files (multipart), follow the reload,
list, restore and clear the zone's sets, and take, release and look at its lock.
Every request needs an API key that names the zones_publish permission, an owner at
zone_publish_gm_level, and HTTPS or the server's own machine; refusals are counted
per address and every use is in the audit log. A publish is checked at its headers
before any of its body is taken, the files are checked and written on a worker
thread, and the zone's instances are replaced through the world reload, held while
players are there unless told to move them.
The Worlds page lists each zone's editors and lock holder for an admin.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
World, master and chat servers read files under maps/ through the override folder
named by zone_override_dir (empty: off). The settings for who may send zone files
and how large a set may be are in sharedconfig.ini and the settings catalog.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The client takes a non-zero edit as the editor being enabled. A character whose
account GM level is at least editor_gm_level (worldconfig.ini, default 9) is
saved with edit="1"; everyone else keeps 0. A civilian never gets it, whatever
the setting. The setting is in the dashboard's settings catalog.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The settings list every key of the shipped config files, so the master
server's limit on world instances gets its entry too.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Live kept the total before the running session in <char time> and the
session's seconds in <char stime>; both changed at a login, together with
llog (time 262486 and stime 7344 became time 269830 at the next login).
The client reads both (LWOCharacterComponent::LoadFromSaveData 0x00ca4910)
and shows their sum (SendMessage 0x00d34330, GetTimePlayed). The server
wrote everything into time and no stime.
Now time + stime is read (a missing stime is 0, so a save from before
keeps its total) and time and stime are written as live had them. A
session starts when the character is picked on the character list:
charinfo.last_login is loaded with the character, a save whose llog is
another login has its stime moved into time, and llog is written as the
session's login. Without a known login the session goes on.
GetTotalTimePlayed is still every second played. The dashboard's save
check knows stime as a number.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
- Zone::LoadZoneIntoMemory takes the paths the zone file reader read (lost in the merge of the raw terrain loading)
- the terrain reader is main's unchanged: files from before version 32 keep their colour and texture maps as the file
has them, and the dashboard's terrain tests expect that (bounds by the span between the first and last point too)
- the model component test counts the model block only: the item block in front of it is the ItemComponent's
- scene file tests: a file from before chunks keeps only its header, editor settings are a count of colours, and the
tests that log have a logger
- the settings catalog lists export_terrain_to_obj
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's model behaviors (every rotation, DoDamage, OnTimer, OnStartup, MoveBackToStart, moves along the model's
local axes), raw terrain reading with scene lookups and scene tools, property model and brick by brick fixes and the
chat server's player count fix, merged onto this branch's structures:
- game messages stay structs; the angular velocity messages join them and the handler sets a message's target
- strips own their speed again and the block names, moves and rotations stay in the shared action table
(PropertyBehaviorActions, which the dashboard's behavior player reads; it now lists rotations too)
- the zone keeps reading its file through ZoneFile and takes the boundaries from it, then loads the raw terrain as main
does; the old dNavigation terrain reader is gone
- the show all response stays a struct and logs a player count mismatch as main does
- the property management component tells models who sent a chat message
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Totals and disk free, a size chart over a day, week or month, the
fastest-growing tables and every table with sorting. The time chart takes
a timeLabel for ranges longer than a day.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The database_usage task (hourly) measures on the background connection,
checks the free space on the database's disk (database_disk_path, else the
SQLite file or a local MySQL data directory) and alerts once when it drops
under database_disk_warn_percent. Log pruning keeps database_usage_days.
GET /api/database/usage?range=day|week|month. The settings are on the
Settings page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
About says what build is running (a release, or a local development or CI
build with its commit, branch and uncommitted changes), the newest release
with its notes, how many commits behind the build is with the compare link,
the last check and any error, and Check now. The home page's Server Status
card shows the build and an Update available badge.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A worker thread asks GitHub's API about update_check_repo: the latest release
and, for development and CI builds, how far the build's commit is behind its
branch (or main). Unauthenticated, with ETags, every update_check_interval_hours
(default 6); offline or rate limited, the last answer stays next to the error
and a used-up limit waits for its reset. The main loop logs results, sends
them to master (UPDATE_STATUS, again when the link comes back) and pushes
update_check to open pages. GET /api/update_check, and
POST /api/update_check/check (settings permission, once a minute). Nothing is
downloaded or installed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/api/world3d/property_models?zone=&clone= lists the saved property's models
(LOT, name, UGC ID, position, rotation), for replays of recorded positions,
which don't record models.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/api/inspector/sessions/:id/property serves CaptureProperty's worlds, built
once per loaded capture on a worker after the replica pass, with the zone's
and the models' names from the locale and the property saved now for each
zone and clone (read on the main thread) for links and UGC meshes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client's word check before sending is a light one; the server is the full filter. In normal (whitelist) chat, block list phrases are now stopped even when each word is allowed, and allowed phrases (from the dashboard or a line with spaces in the allowed words file) let their words through together. The dashboard can allow phrases and its message test explains both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The account page lists the account's reports as a table and the Client System Info page lists every report (or each account's newest), sortable and searchable by account name or video card; a row opens every field. Values that say little about the real system are marked, and every caveat is a tooltip on the value and column.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The filter stored and compared words by std::hash<std::string> in size_t,
which differs between standard libraries and platforms, so a .dcf made on
one system never matched on another and the block list never worked
there (issue 215).
- ChatFilterCore.h: 64-bit FNV-1a over the entry's bytes, ASCII lower
case, fixed-width uint64_t everywhere stored or compared.
- .dcf version 3: little-endian magic, version, longest entry in words,
uint64 count and sorted uint64 hashes. Version 2 files are refused:
the allowed words cache is rebuilt from its .txt, an old
blocklist.dcf is logged as unreadable.
- The servers build blocklist.dcf from a plain blocklist.txt next to
them (one word or phrase per line) when it is newer.
- Blocked entries can be phrases: runs of consecutive words up to the
longest entry, the whole run marked. Whitelist chat still checks one
word at a time, as the client does.
- Dashboard: the chat filter API reads blocklist.dcf the same way
(status, phrase length), accepts blocked phrases, refuses allowed
ones, and explains phrase matches in its message test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Links the game for its message structs, reads the components of every LOT at startup, and decodes a capture's
replica packets once, in timeline order, on a worker thread.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Busy property instances on changed files are kept (KEEP_UNTIL_EMPTY) instead
of saved, frozen and moved. Every instance acted on is marked outdated; the
files status carries the flag (bit 4).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/api/inspector/sessions/:id/worlds lists the captured characters' moves between
world servers with the zone's name from the locale; /positions also returns them,
takes ?zone=all and names each track's character and zone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Master hashes each reported file on a worker, polls size and mtime every
world_watch_seconds (a change must hold still for one poll), and replaces
stale instances with the instance migration (Mythran shift, or seamless
with world_reload_seamless=1); empty ones are stopped. Also on WORLD_RELOAD.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dashboard keeps master's server list endpoints and joins them into the
Network summary: its listening port and its machine as a token, since the
summary goes to every open page. The connection list maps the tokens to
addresses for viewers with network_ips. The dashboard reports its web port.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Master copies the fdb at startup, polls its size and mtime every
cdclient_watch_seconds, builds the new copy and CDServer.sqlite on a
worker, switches itself, writes the pointer file, tells every world and
keeps the last two copies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Packet Captures page (/inspector/packets, dev_message_inspector) arms captures with a
search picker (part of an account or character name, or a pasted account or object ID; online
ones marked; everything needs no pick), lists running and saved ones live (packet_capture
topic), and plays one back on its timeline: play, pause, seek and speed, packets appearing in
order with their decoded fields and bytes. World 3D takes ?capture=<id>&zone=<zone> and replays
the captured position updates in its replay mode; the capture page's playhead drives it over a
BroadcastChannel. Saved packet captures show in the Message Inspector's list and open there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Staff arm a packet capture on the dashboard; master passes MESSAGE_CAPTURE_CONTROL ARM to
every world, auth and chat and arms its own. Each server's PacketCapture tap (dServer receive,
and a send hook in RakPeer::Send so replica constructions are seen too) records into one
preallocated chunk per server and ships sealed chunks through master on the main loop when
capture_flush_bytes or capture_flush_interval_ms is reached; past capture_buffer_max_mb the
oldest chunks are dropped and the dashboard records a gap. Nothing is armed: one flag check.
- targets: an account (from its login; packets before the login are kept per connection
and added once auth or the world knows whose they are), a character (from when it is
picked), or everything; up to 8 at once (a bit each in the record mask)
- worlds and auth record their clients' packets and the master link messages of a captured
player (session keys by name, zone transfers by request, player added/removed, migration);
chat finds the player in each packet; master records server traffic for everything
- secrets are never recorded: structs that carry them (login request, login response user
key, world validation session key, session key messages between servers) are read,
blanked and written again before recording; auth keeps only the handshake and login
- PacketDecoder: a registry by service and message id names every packet and decodes the
registered structs; CaptureBundle is the file format (DLUBNDL1, metadata, records);
CaptureTools orders records on one timeline, pulls movement out, makes bundles portable
or anonymous and diffs replays
- the dashboard keeps packet captures in message_capture_sessions (capture_kind 1) and
their packets in a file under capture_dir, one write per batch; arming is audited
- MESSAGE_CAPTURE_CONTROL/DATA only gain appended enum values and trailing fields
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Web clients are one entry per address and signed-in account, with user and
account_id; the user is shown without network_ips too, the address then
masked as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dashboard's own loop is framed too, with a scope per module update. Frame time and stacked phase charts per server, the servers' loop summary, longest frames, packet handling times, the last 50 slow frames with a nested timeline, and profiling sessions (profiling_run, GM 8) drawn as a flame graph with folded stacks to download. PerfHistory keeps it in memory and is unit tested; the layouts are tested with node. Task 96.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dServer sends the frames with each traffic report, logs a frame over slow_frame_ms (default 250, reread on a settings reload) as one line with its heaviest path, and runs profiling sessions master forwards. Master routes the dashboard's requests to the named server, profiles itself and passes results on. Task 96.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings help names zones by locale key (expanded when served), the
Property Rent page and the Worlds table name zones in the viewer's
language, and the UGC page drops its own inventory name table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The routes' own zone name helpers and the CDClient's English zone and
leaderboard header copies give way to GameText, so every name comes from
the locale in the viewer's language; the Network page's world labels,
mail, missions, leaderboards, item info (cached per language) and the
settings/vanity help strings too. Reward code 4 and the plaque text name
their zone from the locale.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dashboard loads every language in the client's locale; each request
runs in the viewer's language (Workers::Reply carries it to the worker),
templates get game.* and phrase()/zone_name(), scripts get GameText.* on
<body data-game>, and the user menu picks the game text language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One helper for game text on the dashboard: zone names, locale phrases,
localized table columns, %[key] expansion and the game's currency words,
looked up in the viewer's language (a cookie pick, else Accept-Language),
falling back to en_US and then the key or id. Unit tested.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Account pages get a Client system info (as reported) card (client_sysinfo):
every description the account's client sent at login, newest first, with the
raw values, the memory text split into numbers and each field's caveat. The
address is only shown with logs_audit. The Client System Info page (Logs &
Health) shows the spread across players from each account's newest report:
Windows version, video card, memory buckets, processor count and client build,
marked as client-reported and approximate.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
client_sysinfo (GM 5, grantable) shows the client system info; the Log pruning
task deletes rows not seen for log_client_sysinfo_days (90).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After a successful login the auth server stores the system description the
client sent (memory text, video card, processor fields, Windows version fields)
exactly as sent, on the main thread with the login's other writes. The address
is only kept while log_login_addresses is on; log_client_sysinfo (on) turns the
whole thing off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dashboard's API covers what it offered (online players, teams, announcements) with API keys, so the
chat server no longer runs a web server; its settings show as unused on the settings page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A player on the servers' machine (or behind the same address as other players) was merged with the
servers' own links and everyone else there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Next to Diagnostics (health_view): game and web clients, auth, chat,
worlds per zone (expandable), other reporting servers, master, the
dashboard and the UGC server, with a lane each way per link sized by
bytes/s, animated by packets/s and coloured by load against its own
5 minute peak. Estimated links (servers without a split) are dashed.
Clicking a box shows its links, busiest message types and a sparkline,
with a link to Diagnostics (which now takes ?server=). A connections
table lists each remote address; single clients can be drawn too.
Narrow screens get a list. Drawing stops while the tab is hidden.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The traffic topic now carries each server's rates with its split by
peer (null for servers that report none), link statistics and gauges.
New routes: /api/diagnostics/network, /network/server (message types
and a 10 minute series) and /network/connections (remote ends grouped
by address). Addresses need the new network_ips permission; without it
each is a salted token. They stay in memory from the last report only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>