The dashboard shows worlds master has launched but that aren't connected
yet (Starting) and those shutting down, on the main page's world list
and in /api/worlds (state: starting|stopping), without a shutdown
button. Master's server list now carries each world's state (appended
after the UGC fields, one byte per world) and master pushes the list to
the dashboard whenever a world is launched, becomes ready, is told to
shut down or goes away, instead of the dashboard only seeing it on its
30 second poll. Starting worlds are kept apart from the running ones,
so counts, events and shutdown requests still only see running worlds.
prestart_worlds (masterconfig.ini, zone ids) lists the worlds master
starts when prestart_servers is on, instead of the hardcoded character
select (0) and Venture Explorer (1000), which stay the default when it
is missing or empty. It is on the Settings page as a zone list (restart
only), shown when prestart_servers is on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UGC Search (/ugc_search) finds creations by name, id or LOT with where each
one is. Property pages and character inventories show the icon the UGC server
made of each creation, its state and a link to /ugc?item=&kind=, for whoever
may view the page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Online state with the time it came up, down/up alerts, a UGC column in the
health history, /api/servers (every server with its process memory and CPU)
and /api/servers/ugc, and UGC gauges for Prometheus.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Servers now log to logs/<Server>/ (worlds to logs/WorldServer/<zone>/<clone>/) with the start time in the file name.
The dashboard and UGC server log the same way, and the System Log page and log search read the whole folder tree
instead of only the top of logs/.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bearer keys (dlk_...) are checked per request against their owner's
current account (ban, lock, demotion, sign out everywhere stop or narrow
them at once) and their scope: permission routes need the permission in
scope, read-only keys only read, level-only routes need an all-permission
key, and session-only paths (sign-in, password, 2FA, key management)
are never reachable with a key. Per-key rate limit and daily quota with
429 and X-RateLimit/X-Quota/Retry-After headers; usage is written in
batches every minute. WebSocket subscriptions honour the scope too.
Routes to list, make, rotate and revoke keys; staff with
api_keys_manage can see and revoke others' keys under the rank rules.
POST /api/auth/token now makes an all-permission key. Audit entries for
create/rotate/revoke/denied, and actions done with a key are attributed
to "user (key name)".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keeps every server's traffic reports (last hour at one second, per-minute
rows to server_traffic once a minute, pruned after traffic_days) and shows
packets, bytes, HTTP requests and latency per second, per server, with the
busiest message types and HTTP routes. Live over the traffic WebSocket topic;
1 hour, 24 hours and 7 days ranges. The same counters are in /metrics.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Counts, a paged list and failures of what the UGC server made of players'
models and modular builds, making one, the failed ones or everything again
(new ugc_manage permission), the UGC server's live status and icons from
ugc_public_url, and a 3D view of a model's LXFML. The UGC settings are in the
settings catalog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Off unless property_rent_enabled is on. Each property world's rent comes from its PropertyTemplate
row (minimumPrice every rentDuration x durationType; Block Yard is free), unless the new Property
Rent dashboard page sets another price or period (property_rent_manage). Rent is taken from the
owner's coins shortly after their character loads, with a mail receipt; unpaid rent is mailed and,
after property_rent_grace_days, makes the property private until it is paid, like live. The
property management component refuses public or best friends privacy while rent is overdue and a
property world that loads overdue makes itself private. Property game messages are unchanged.
Fixes#943
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Staff list contraband items on a new dashboard page (item search, reason, flag or flag and remove;
contraband_manage to edit, reports_view to see). World servers check every inventory when a
character loads and every item a player receives: each find is an economy flag of the new kind
Contraband, shown with the other flags and in the character's related data. Items marked for
removal are taken away, with a character snapshot kept first so they can be given back, an audit
entry and a mail or chat message telling the player why. Staff are skipped unless
contraband_ignore_staff is off. Worlds reload the list when it changes (RELOAD_CONTRABAND, added
at the end of ePlayerAction).
Fixes#1563
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening a zone the first time built its terrain, scene objects and manifests
and ran ImageMagick on the web thread, stalling the dashboard for seconds.
- Workers: the shared pool plus Workers::Reply (answer at once when built,
else from a worker via Web::Defer).
- terrain_chunks/terrain_layers/scene/paths/scenery/flairs (world3d, property
and showcase routes) and terrain textures go through it; results are built
once in OnceCaches, the .raw is read once per zone for chunks, layers and
flairs, deflated bodies are cached thread-safely.
- ImageMagick conversions are deduplicated and written under a temporary name.
- Workers don't query the CDClient, read settings or call mongoose: ZoneTable,
render components, flairs, object names, LOT kinds and terrain texture names
are read at startup; client_location is read once; base64 is plain C++.
- Logger writes one line at a time (mutex; localtime's buffer is shared).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every MASTER service packet is now an LUBitStream struct (docs/PacketArchitecture.md,
PR 14) and the master server's switch is a dispatch map (PacketDispatcher), as are the
master handlers of the world, chat and dashboard servers.
- dNet/MasterPackets.h: RequestZoneTransfer, RequestZoneTransferResponse, ServerInfo,
RequestSessionKey, SetSessionKey, SessionKeyResponse, NewSessionAlert, PlayerAdded /
PlayerRemoved, CreatePrivateZone, RequestPrivateZone (passwords still cut to 50
characters when read), WorldReady, WorldReadyInfo (WORLD_READY to the dashboard),
PrepZone, Shutdown, ShutdownResponse, WorldShutDown (SHUTDOWN_RESPONSE to the
dashboard), ShutdownUniverse, AffirmTransferRequest/Response, RequestServerList,
ServerListResponse, DashboardShutdown, ConfigReload, InstanceShutdown. The Send*
functions are gone; MasterPackets::SendToMaster(msg) and SendTo(sysAddr, msg) send a
struct.
- The dashboard and instance migration structs (PlayerAction, DataChanged, Dashboard
messages, MessageCapture, InstanceMigration) are LUBitStreams of the MASTER service now
and moved to dNet/master/, included by MasterPackets.h. Their payloads are unchanged;
master forwards them by re-serializing the struct instead of copying raw bytes.
- InstanceManager, ZoneInstanceManager, MigrationCoordinator, dServer (server info, zone
transfer response), auth (SET_SESSION_KEY), the world (session keys, player added and
removed, world ready, shutdown response, affirmations, prep zone, shutdown universe) and
the dashboard (server list, instance shutdown, config reload, announcements, player
actions, message capture) send and read structs.
- The login stamps are a `stamps` field of RequestZoneTransfer and
RequestZoneTransferResponse (read leniently as before: a message without them reads as
empty); master adds its stamps in the REQUEST_ZONE_TRANSFER handler and when it answers,
as it did.
- InstanceManager::GetInstanceBySysAddr takes a const address.
Verified: tests/dGameTests/dNetTests/Legacy/MasterPacketsLegacy.h is a verbatim copy of
the old writers and readers; MasterPacketsTests requires identical bytes for a grid of
inputs, checks the old readers read what the structs write, round trips and truncation,
hand written golden packets, that zone transfers without stamps still read, that the dashboard/migration structs write what
"header + Serialize" wrote, and that the dispatcher drops truncated packets. No wire
bytes changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dashboard's web server answers one request at a time, so converting a big
.nif (glom files up to tens of MB) held up every other request, flairs included.
- dWeb: Web::Defer hands a request to another thread; the reply is sent from the
web thread on its next poll (DeferredQueue). A client that leaves first cancels
it and the late reply is dropped. The synchronous route API is unchanged.
- Web::Shutdown closes connections while the state their close events touch is
still alive; the destructor no longer runs handlers during static destruction
(stopping the dashboard aborted in ~WSClient).
- WorkerPool: priority lanes, with one thread only for urgent work (flairs,
small models, textures), and limited background work.
- Scenery: mesh and texture routes (and the showcase's) convert on the pool;
thread-safe memory and disk caches, one conversion per model at a time with
waiters sharing it; zones are converted ahead onto the disk cache while viewed.
- Setting scenery_workers (0: half the cores, 2 to 4).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The NexusDashboard-parity dashboard (dDashboardServer) and everything built on it on the experimental branch:
accounts, characters, properties and moderation tools, permissions shared with in-game slash commands, economy
reports, World 3D and property 3D views with client scenery, scheduled events (features, vanity changes, live
events, announcements, restarts), vanity files and events, the CDClient browser, the message inspector with saved
captures, chat filter tools, community challenges, live ops, the AI moderator helper, and the server-side changes
they need.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>