Hashed API keys with scope, restrictions, limits, expiry and batched
usage counters, for MySQL and SQLite, with test stubs and parity tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One row per server and minute (packets, bytes, resends, HTTP requests, errors,
bytes and latency percentiles), written in batches and read summed into
buckets for the longer chart ranges.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client sends SetBBBAutosave (996) with the model being built every five
minutes, before an AFK kick and before quitting, and expects the server to
rebuild an unfinished model later (RebuildBBBAutosaveMsg). This keeps the
last one per character with the model items that were in the BBB inventory
at the time. MySQL 82 / SQLite 65; parity test included.
Refs #1632
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new server (dUgcServer, started by master with enable_ugc_server=1) that
takes unprocessed ugc and ugc_modular_build rows from the database and makes
what the client downloads with UGCUSE3DSERVICES: an optimized NIF (hidden
faces removed, ambient occlusion baked into vertex colors) and a 128px DDS
icon for player models, rendered by a software rasterizer from the client's
LDD brick primitives, and icons for cars and rockets assembled from their
modules per ModularBuildComponent/ModuleComponent. It serves them, with the
models' LXFML, over HTTP in the client's UGCC<dc>/3DOPTIMIZED and
IMAGE128DDS layout with .gz and .checksum files, and keeps its folder under
a size cap.
Processing state lives in ugc.is_optimized plus new processed_at,
process_attempts and process_error columns (and the same on
ugc_modular_build). ServiceType::UGC is appended. NifFile moves to dCommon
and records named node transforms for the modules' attach points.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pet_names gets a pet_lot column (mysql 80, sqlite 63). The world writes
it whenever it saves a pet name, from the pet entity's LOT, and fills it
in for older rows when the owner loads into a world (from the pets the
game loads for that character, only where it is still missing).
The dashboard's pet name tables read pet_lot instead of scanning the
owner's character XML; pets without it yet show as Unknown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Property worlds now give their property reputation for the time other people spend on it, which
fills the property lists and the news screen's Today's Top Properties. Visitors are counted per
account; the owner's account, accounts linked to it and staff don't count. A visit earns nothing
for the first property_reputation_min_visit seconds (WorldConfig's propertyReputationDelay), then
each active minute (the visitor moved) earns reputationPerMinute times a multiplier, for a capped
number of minutes per visit. Repeat visitors earn less the more recent days they already gave
reputation, and each visitor and each property have a daily cap. Every parameter is a setting;
what each account gave each property per day is kept in property_reputation_visits. The rules are
pure functions with unit tests.
Fixes#636Fixes#637
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>
Every character gets a save generation in charxml. A world bumps it when it loads the character for
play, and every save from that world only goes through while the stored generation is still the
one it loaded or last saved (and moves it on). Dashboard edits, restores and maintenance writes bump
it too. A world that lost the character to another world (a disconnect noticed late, a zone
transfer, an instance migration) or to a dashboard edit can no longer overwrite the newer data: the
save is refused, logged and audited as stale_save_refused, the world stops saving that character
and a player still connected to it is disconnected with the save failure reason so they reload.
Fixes#639
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its key starts with the zone, so the economy compaction read the whole table twice for every
day it merged into a month. With 900k old rows to merge on MariaDB the task went from 107 s to
14 s. The index holds only the day and the key, so the game's upsert that adds to a row's
events never updates it.
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>
* feat: re-write persistent object ID tracker
Features:
- Remove random objectIDs entirely
- Replace random objectIDs with persistentIDs
- Remove the need to contact the MASTER server for a persistent ID
- Add persistent ID logic to WorldServers that use transactions to guarantee unique IDs no matter when they are generated
- Default character xml version to be the most recent one
Fixes:
- Return optional from GetModel (and check for nullopt where it may exist)
- Regenerate inventory item ids on first login to be unique item IDs (fixes all those random IDs
Pet IDs and subkeys are left alone and are assumed to be reserved (checks are there to prevent this)
There is also duplicate check logic in place for properties and UGC/Models
* Update comment and log
* fix: sqlite transaction bug
* fix colliding temp item ids
temp items should not be saved. would cause issues between worlds as experienced before this commit
* feat: Remove PERSISTENT ObjectID bit because it's not an ObjectID bit
TODO: Need to add character save migration for the pet subkey in the inventory
Tested that the migrations work on mysql and sqlite and that properties have all their contents as before.
Need to test pets still
* fix: ugc, pet ids. remove persistent bit
* feat: convert character ids to 64 bits
remove all usages of the PERSISTENT bit with regards to storing of playerIDs on the server. the bit does not exist and was a phantom in the first place.
Tested that a full playthrough of ag, ns and gf was still doable. slash commands work, ugc works, friends works, ignore list works, properties work and have names, teaming works.
migrating an old mysql database works . need to test an old sqlite database
* fix sqlite migration
* remove nd specific column migration
Tested that models are migrated to the new format a-ok
Tested that the new logic works as expected.
Old code needs to be kept so that models in both states can be brought to modern standards
* Add utilities for formats
* Normalize model positions when placing in the world
Have tested that placing a small and very large model both place and are located at the correct position.
* add migration
* Update Logger.cpp
* add some notes and remove some logs
* change arguments and add eof check
Revert "fix: buff station cycling and dying too soon"
This reverts commit 1c6cb2921e10eb2000ac40007d0c2636ba2ac151.
fix: buff station cycling and dying too soon
Tested that the buff station now only cycles after it has been built and has been alive for 25 seconds.
* simplify leaderboard code, fully abstract database
* update exception catching
* update exception catching and sql references, remove ugc from gamemessages
fix deleting model
remove unrelated changes
Update GameMessages.cpp
* remove ugc from gamemessages
* Update GameMessages.cpp
* Update Leaderboard.cpp
* bug fixes
* fix racing leaderboard
* remove extra stuff
* update
* add sqlite
* use a default for optimizations
* update sqlite
* Fix limits on update and delete
* fix bugs
* use definition to switch between databases
* add switch for different backends
* fix include guard and includes
* always build both
* add mysql if block
* Update Database.cpp
* add new options and add check to prevent overriding mysql
* correct config names
* Update README.md
* Update README.md
* merge to 1 sql file for sqlite database
* move to sqlite folder
* add back mysql migrations
* Update README.md
* add migration to correct the folder name or mysql
* yes aron
* updates
* Update CMakeLists.txt
* dont use paths at all, add where check to only update if folder name still exist
check also doesnt check for slashes and assumes one will be there since it will be.
* default dont auto create account
for releases we can change this flag
* default 0
* add times played query
* fix leaderboard not incrementing on a not better score
* add env vars with defaults for docker
* use an "enum"
* default to mariadb
* Update .env.example