diff --git a/projects/Games/README.md b/projects/Games/README.md index 0702631..aa124e0 100644 --- a/projects/Games/README.md +++ b/projects/Games/README.md @@ -10,6 +10,7 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. - [docs/FIVEDRAW.md](docs/FIVEDRAW.md) - [docs/GUILD_TABLES.md](docs/GUILD_TABLES.md) - [docs/HOLDEM.md](docs/HOLDEM.md) +- [docs/OWNERSHIP.md](docs/OWNERSHIP.md) - [docs/SYSTEM.md](docs/SYSTEM.md) - [docs/TEST_MATRIX.md](docs/TEST_MATRIX.md) - [docs/WAGER_ENGINE.md](docs/WAGER_ENGINE.md) diff --git a/projects/Games/docs/GUILD_TABLES.md b/projects/Games/docs/GUILD_TABLES.md index 5512caf..1a3ac45 100644 --- a/projects/Games/docs/GUILD_TABLES.md +++ b/projects/Games/docs/GUILD_TABLES.md @@ -26,10 +26,11 @@ Staff auto needs `games.autodealer.staff`. It is not the default for a normal pl On place, store: - `ownerPlayer` (placer UUID) +- `ownerCharacterId` (captured placer character, `ownerIdentityVersion: 2`) - `ownerGuildId` (guild id at place time, or null for a personal table) - `ownerGuildIncarnation` (the owning guild's permanent UUID) -Guild membership and leadership use the active character. Editing or picking up a guild table requires that character to lead the captured guild incarnation, or staff permission. The placer account alone gives no guild-table rights. The account remains the host of a personal table. +Guild membership and leadership use the active character. Editing or picking up a guild table requires that character to lead the captured guild incarnation, or staff permission. The placer account alone gives no guild-table rights. A personal non-mint table uses its full recorded owner character; a nonguild staff-mint table retains staff authority. Guild metadata takes precedence over mint/counting flags for ownership classification. Mode edits preserve the separate owner record. See [OWNERSHIP.md](OWNERSHIP.md) for migration, session guards and private-property inheritance. The captured incarnation selects the `guild:` Denars account. A deleted guild's later namesake cannot use its tables, bank or expansion slots. Missing guilds, unavailable faction services and unresolved incarnations refuse funding; tray settlement retains the table and chips for review instead of dropping money through a failure fallback. @@ -77,11 +78,11 @@ Not during a live round. Right-click shoe (existing claim path): - Already this player: **unset** dealer. -- Empty seat, human table: **set** if the player is in `ownerGuildId` (or staff). No guild on the table: placer / staff only. +- Empty seat, human table: **set** if the player is in `ownerGuildId` (or staff). No guild/non-mint table: recorded owner character / staff only. Staff-mint mode requires the existing staff permission. - Guild auto, between rounds: **takeover** - become human dealer, `autoDealer=false`, tray and guild ownership stay. - Staff auto: guild members **cannot** takeover. Staff can turn mint off from options (when not live) or pick up the table. -Leave-distance still does not clear dealer. Logout / pickup still does (existing). +Leave-distance still does not clear dealer. Logout, character session end and pickup clear it. Pending estate transfers prevent new claims while captured work settles. ## Options GUI diff --git a/projects/Games/docs/OWNERSHIP.md b/projects/Games/docs/OWNERSHIP.md new file mode 100644 index 0000000..f34334f --- /dev/null +++ b/projects/Games/docs/OWNERSHIP.md @@ -0,0 +1,69 @@ +# Table ownership and estates + +Private table authority follows a recorded character. Account UUIDs still route players, inventory and the existing placement limits; they do not grant another character on that account the recorded owner's rights. Wagers retain their separate captured identities and money recovery rules in [WAGER_ENGINE.md](WAGER_ENGINE.md). + +## Classification and authority + +| Stored mode | Owner authority | Property inheritance | +|---|---|---| +| Nonblank `ownerGuildId`, including a guild table with `staffMint` enabled | Existing guild membership/leadership, captured guild incarnation and staff override | Outside private property | +| No guild, `staffMint: true` | Existing staff permission and funding rules | Outside private property | +| No guild, `staffMint: false` | Recorded `ownerPlayer` plus `ownerCharacterId`, with a current live character session; existing staff override | Private table property | + +`autoDealer` and expansion-slot counting do not change this classification. The owner record is separate from guild and staff funding. Editing a table's mode preserves its recorded owner; turning off staff mint does not adopt the editor as the private owner. New placements, including saved settings used as a template, capture the new placer's current character. + +Explicit stored `staffMint` values take precedence even when `autoDealer` is null. Legacy missing/null mint fields retain the loader's old default: a missing/null automatic-dealer field uses that game's layout default, while an explicit automatic-dealer field defaults missing mint to false. Migration freezes the effective mint value in the same durable raw plan before property candidates are planned, including unloaded tables. Later layout changes do not reclassify that frozen record or recapture its owner. + +Owner editing, pickup and private dealing/hosting require the full recorded identity. Guild tables retain their guild rules. The character provider must confirm that a session belongs to the connected account, that its character is alive, and that the session is still current. Menus, placement arms and queued chat capture that session. Applying settings also checks the table's original owner and its runtime ownership generation, including staff edits; an old menu cannot restore an owner changed by an estate. + +Character session end clears placement, prompts, hands and dealer state through the existing disconnect cleanup. During that synchronous end event, the session is already cleared but RPCharacters still has the outgoing body loaded. Games can return its captured stakes and idle private dealer float into that body before RPCharacters stows it. Only the exact recorded wager character can receive the money; new source legs still require a current session. Another captured character, an already active successor, a dead body during death processing or insufficient inventory space leaves the obligation on the felt and holds the table for staff review. Refunds reserve room for every recipient leg before moving anything, including matching partial stacks; a payout that cannot fit stays held. An unexpected inventory backend disagreement logs a severe error and retains the existing at-feet drop backstop against deletion. Missed lifecycle events do not authorize old menu or chat callbacks. + +The ending-session scope closes when cleanup returns, including on an exception. Outcomes that settle later, such as a forfeited blackjack box, a live round's private float or a poker pot, require ordinary authority for the same captured character; they stay held otherwise. Item delivery and the table save are separate effects in the same tick, so a crash inside that tick remains a manual-review window. When character bodies are disabled, RPCharacters uses the account's shared inventory for all items. + +## Storage and migration + +All paths below are relative to the Games plugin data directory, usually `plugins/Games/`. + +Placed tables remain JSON files beneath `Data/tables/`. `ownerIdentityVersion: 2` adds nullable `ownerCharacterId` beside the existing `ownerPlayer`. Missing or version-1 identity metadata expands through the reviewed legacy-heir service, never the account's current, last-played or newest character. A missing reviewed heir leaves a null character slot; a later reviewed mapping can resolve it without erasing intervening table settings. A recorded dead character remains the owner until the estate decision changes property. + +Each migration archives exact original bytes under `migrations/table-ownership//.original` and stores a frozen `.json` plan beside them. Archive and plan are forced before publication. Plans distinguish owner-envelope changes from legacy-mode freezing; a mode-only plan preserves an existing owner unchanged. A restart uses that original mapping/mode, checks both digests and the exact permitted output, and refuses changed sources or damaged archives. Raw files are inventoried before world/card-set filtering, so unloaded tables participate too. + +Duplicate JSON fields, duplicate table IDs, unsupported or nonintegral identity versions, malformed owner/float/guild UUIDs and ambiguous guild-incarnation metadata remain untouched for review. Duplicate table IDs are checked across readable source files before owner or guild migration publishes anything. An unreadable inventory defers legacy mapping rather than guessing its missing rows; independently coherent, already resolved files can remain usable. Deferred loads retry every five seconds. + +Ordinary saves overlay known gameplay fields onto the original raw document. Unknown fields, explicit nulls, nested ledger metadata and unrecognized wager references survive saves and restarts. Consumed, valid wager bindings can be removed normally. Routine ownership health checks use in-memory holds. A funded money transaction checks full source content before posting or physical effects, and pickup checks before refunding or destroying the table. The five-second deferred pass checks loaded sources, including deleted files; saves and estate publication check full source content again before writing. Unexpected file deletion, replacement or content changes hold the affected table rather than recreating or overwriting the source, even when file size and modification time match. A reviewed repair requires a fresh load/restart; guards do not silently accept an external writer as a new baseline. + +## Estate settlement + +Games registers the `games` estate participant when the guarded estate API is present. Only the decision's **property** recipient can receive private tables. Guild and staff-mode tables remain outside this transfer. Money, loot, house floats, guild banks, stakes and captured payout identities are never inherited with the table. + +Settlement first requires reviewed legacy ownership and the exact current death, testator and decision revision. Unresolved private owner slots for the testator block settlement before an empty receipt can be written. `WAITING` and `FROZEN` property remain in place, with durable completions recording the full decision, retained table IDs/owner references and cumulative completed moves. Unsupported account/sender property routes and a self-recipient block rather than recording fictitious transfers. + +A completion refuses changed same-revision decisions or newly appearing/resolved testator ownership at that revision, including an empty completion. A genuinely newer guarded revision can consider new untouched property and the previously retained pool. Persisted future-revision evidence blocks decision rollback before any effects. Previously moved table IDs can never move again for that death: every later revision detects a restored source. Historical retained completions bind their cumulative IDs to earlier completed character snapshots, and reports count each moved table once. + +Plans under `estates/tables/-.json` capture candidate table IDs durably before waiting for activity to finish. Pending transfers prevent new sessions, dealer claims, stakes and free draws. Existing captured games, payouts and card recycling can finish under their existing identity and money guards. Live work returns `RETRY`; retained wager intents, unresolved money, floats, stakes or ambiguous saved activity return `BLOCKED` with the table ID. Ownership settlement does not replay or remove a wager intent. + +Once every candidate is idle, Games checkpoints loaded tables and freezes complete raw before/after documents. Publication changes only the owner account, owner character and identity version. It checks the guard again before each write, preserves unrelated raw fields and records exact completed IDs before releasing pending authority. Filesystem failures retry against the same plan, including a write whose bytes published before durability acknowledgement failed. Conflicting, stale or partial plans remain held. Historical receipts validate their death, full source/recipient, integral revision, filename, snapshots and moved IDs before contributing cumulative results. + +Startup, estate/revival wake-ups and periodic reconciliation recover pending intents. Provider-confirmed revival or a different death can obsolete an untouched intent and release its pending tables. Missing providers are not cancellation evidence. A partially published transfer remains held for review; revival does not reverse completed property. + +## Conservative rollback + +Use the source checkout's `tools/ownership-rollback.py` for a single migration whose table has no later progress or estate history. Keep a complete stopped-data backup and preserve all migration and wager journals. The helper defaults to preview: + +```sh +python3 tools/ownership-rollback.py /path/to/plugins/Games imported-table.json ORIGINAL_SHA256 +``` + +Use the full original SHA256 from that table's migration plan. The helper verifies the immutable original, frozen account/character mapping and exact current migrated bytes. It refuses altered settings, ownership changes, damaged evidence or any estate intent/receipt naming that table. A compatible binary and dependencies must accompany any rollback. + +After stopping Games cleanly, repeat with `--apply`. The plugin's `ownership-active` marker must be absent; it remains after an interrupted run or a shutdown with held ownership/money. Do not delete the marker to bypass recovery. Successful apply preserves the migrated file as `.pre-ownership-rollback` and restores the exact original bytes with atomic, forced writes. Previewing an already restored source is harmless. + +This helper is deliberately unable to undo ordinary gameplay or estate transfers. Review those against the stopped backup and complete receipts, preserving obligations; restoring an old owner after a completed estate is detected and held. + +## Compatibility and verification + +The build pins TLibs **2.6.0**. Without the required identity capability or reviewed provider, private migration and current owner authority defer; no account fallback grants ownership. Older TLibs may expose character sessions while lacking guarded estates, so those capabilities are probed separately. Presentation-name fallback is independent of owner authority. + +Run `tfmc-mvn -o -B clean verify` with Java 21. Automated scenarios cover account A-to-B switches and renewed sessions, missed lifecycle callbacks, staff stale snapshots, duplicate/malformed raw sources, null preservation, migration interruption, idle-only estates, receipt corruption, restored sources, revival and write acknowledgement failure. JaCoCo reports every production class and has no enforced threshold. + +`integration-tests/LegacyApiCheck.java` can run with the full test dependency classpath, compiled Games classes and an actual older TLibs jar. Its arguments are the Games classes/JAR, old TLibs JAR, expected character-capability boolean and an empty isolated fixture directory. It tests capability separation, missing provider authority and byte-preserving deferred private migration. This compatibility probe and the automated suite do not verify client packet rendering; retain the [manual matrix](TEST_MATRIX.md) for server/client behavior. diff --git a/projects/Games/docs/SYSTEM.md b/projects/Games/docs/SYSTEM.md index 9ab9b47..7067acf 100644 --- a/projects/Games/docs/SYSTEM.md +++ b/projects/Games/docs/SYSTEM.md @@ -17,6 +17,9 @@ All paths under `plugins/Games/` on the server. | `games.yml` | Per-game rules, layout, blackjack min/max / auto-dealer defaults, [hand card limits](#hand-card-limits). Live auto/mint/shuffle live on the table ([GUILD_TABLES.md](GUILD_TABLES.md)) | | `help.yml` | The rule books `/games help` opens, one section per book, pages written by hand | | `Data/tables/` | Gson for placed tables | +| `migrations/table-ownership/` | Immutable original bytes and reviewed legacy-owner plans | +| `estates/tables/` | Guarded private-table transfer intents and receipts | +| `ownership-active` | Running/recovery marker; clean, unheld shutdown removes it | ### Hand card limits @@ -36,6 +39,7 @@ games/ deck/ # shoe shuffle draw display/ # ProtocolLib ItemDisplay + motion layout/ # stack, fan, pot offsets + ownership/ # character authority, raw migration and estates table/ # placed deck, seats, persist select/ # closest token on look ray wager/ # denar and item transactions @@ -108,9 +112,9 @@ Ace file and IA id is `_1` or `1`, not 14. Ace-high ranking is a poker flag, not `TLibs`, `ItemsAdder`, `ProtocolLib` and `DenarEconomy` are required. RPCharacters softdepend (character names and voice). SimpleFactions softdepend (guild auto cap + bank; [GUILD_TABLES.md](GUILD_TABLES.md)). SF does not depend on Games. -The build pins TLibs **2.4.0**, DenarEconomy **0.3.0**, SimpleFactions **4.0.0** and RPCharacters **2.16.0**. Names use the shared +The build pins TLibs **2.6.0**, DenarEconomy **0.3.0**, SimpleFactions **4.0.0** and RPCharacters **2.16.0**. Names use the shared character identity service; voice still uses RPCharacters' chat API. Deploy the -pinned providers with Games and restart to activate them. +pinned dependencies and compatible character/estate providers with Games and restart to activate them. Private table authority, migration and property settlement are described in [OWNERSHIP.md](OWNERSHIP.md). ## Displayed names @@ -127,4 +131,4 @@ name. A null or blank display, an incompatible identity API, or a failing provider also falls back to the account name; only integration failures log a warning, once per server start. Offline players use their last known account name, or `-` when unavailable. Names are presentation only: player UUIDs, wagers, -payouts and table ownership do not depend on them. +payouts and table ownership do not depend on displayed names. Private owners and wagers use separate recorded character references. diff --git a/projects/Games/docs/TEST_MATRIX.md b/projects/Games/docs/TEST_MATRIX.md index d4e083d..a4dcc88 100644 --- a/projects/Games/docs/TEST_MATRIX.md +++ b/projects/Games/docs/TEST_MATRIX.md @@ -6,7 +6,7 @@ Run these checks on Minecraft **1.21.10** with the intended plugin dependencies ## Automated tests -`mvn clean verify` runs the JUnit suite (MockBukkit and Mockito) and writes JaCoCo reports to `target/site/jacoco/`. JaCoCo measures every production class with no exclusions; open `target/site/jacoco/index.html` to inspect uncovered behaviour. Coverage data is replaced on each run, so use the full suite when assessing repository-wide coverage. +`tfmc-mvn -o -B clean verify` runs the JUnit suite (MockBukkit and Mockito) and writes JaCoCo reports to `target/site/jacoco/`. JaCoCo measures every production class with no exclusions and enforces no threshold; open `target/site/jacoco/index.html` to inspect uncovered behaviour. Coverage data is replaced on each run, so use the full suite when assessing repository-wide coverage. Game scenarios drive public callbacks through complete Poker, Draw and Blackjack rounds across the real table, deck and money implementations, and assert game rules, money conservation or player-visible effects. Tests should protect supported behaviour, not create impossible internal states merely to execute a branch. Where no real caller can reach a branch, remove the branch rather than force it. @@ -110,9 +110,9 @@ See [BLACKJACK.md](BLACKJACK.md). - [ ] 3:2 natural, double, split, DAS - [ ] Auto: 10s after first min bet, spawn pays - [ ] Voice via RPCharacters `dispatch`, not `/rp` command -- [ ] With TLibs 2.4.0 and RPCharacters 2.16.0, table labels and game messages show the current mask/alias; changing it or switching characters updates the next rendered name without revealing a canonical name +- [ ] With TLibs 2.6.0 and a compatible RPCharacters provider, table labels and game messages show the current mask/alias; changing it or switching characters updates the next rendered name without revealing a canonical name - [ ] An online player with no active character shows the RPCharacters no-character label (`Unknown` by default), matching chat -- [ ] Without the character provider, online labels use account names; offline names use the last known account name or `-`; gameplay and wagers still work +- [ ] Without the character provider, online labels use account names and offline names use the last known account name or `-`; missing identity still refuses current owner authority, new character-bound sessions and unsafe wagers --- @@ -137,3 +137,21 @@ See [GUILD_TABLES.md](GUILD_TABLES.md). - [ ] Missing winning character, mid-round tax failure and partial payout retain the remaining obligation and prevent round clear/pickup. Run automated checks with `tfmc-mvn -o -B --no-transfer-progress clean verify`. Journal tests inject posting, storage and bookkeeping failures; they do not prove atomic Minecraft inventory persistence or replace server integration checks. + +## Private ownership and estates + +See [OWNERSHIP.md](OWNERSHIP.md) for source paths, compatibility probes and the conservative rollback procedure. + +- [ ] Character A owns a private table; same-account B cannot edit, deal, host or confirm A's old menu/placement arm, including a missed session-end event and an A-to-B-to-A session renewal. +- [ ] A's synchronous session-end cleanup refunds its captured stakes and idle private float into A's outgoing body, preserving total value and producing no world drops. A different recorded character, an active successor, a dead body or insufficient space holds all value for review; matching partial stacks count as available room. +- [ ] Ending-session authority cannot fund a new source leg or survive into a deferred payout. Ordinary quit cleanup followed by the end event does not pay twice. A captured taxed payout retains the full wager and tax envelope and conserves inventory plus ledger value. +- [ ] Staff edits opened before a table changes owner cannot apply or restore the old owner, including queued chat and an owner changing back. +- [ ] Guild-plus-mint, nonguild mint, human private and automatic private tables follow their separate authority and inheritance classification. +- [ ] Legacy missing/null mode fields and explicit mint/non-mint values use the same raw/runtime classification; frozen mode survives layout changes, loading and publication interruption without recapturing owner or changing money. +- [ ] An unready reviewed-heir service makes no migration or empty estate receipt; later readiness resolves raw unloaded rows without discarding settings or unknown nulls. +- [ ] Duplicate JSON fields/IDs and invalid owner/guild/float UUIDs stay untouched and held. External file replacement or deletion is detected before money effects, pickup or a save/estate publication and during deferred loading, including matching size and modification time; a held table refuses new money. +- [ ] A live private table receives a durable pending estate intent: new activity is refused, captured work can settle, and only idle ownership moves without touching floats, wagers or unknown fields. +- [ ] Restart resumes the exact current decision; changed historical envelope/receipt IDs, partial stale transfers and a restored completed source block every later recipient mode. +- [ ] Empty CHARACTER/WAITING/FROZEN completions record exact revision evidence; same-revision new ownership or changed decisions block, newer guarded revisions can consider untouched new property, and retained completions preserve cumulative moved IDs. +- [ ] Confirmed revival releases untouched pending plans; missing provider evidence and partial publication retain the hold. +- [ ] Rollback preview accepts only the exact migrated source; progress, active/recovery marker and estate history refuse apply. Successful apply preserves current bytes and restores the immutable original. diff --git a/projects/Games/docs/WAGER_ENGINE.md b/projects/Games/docs/WAGER_ENGINE.md index 5eefebe..d557fe0 100644 --- a/projects/Games/docs/WAGER_ENGINE.md +++ b/projects/Games/docs/WAGER_ENGINE.md @@ -71,8 +71,12 @@ Hold'em and Five-Draw pot payouts include the winner's own stake. `RoundMoney.ta ## Character ownership and recovery +Private table ownership uses a separate recorded owner character and the [idle-only property estate participant](OWNERSHIP.md). Transferring the table never changes its wager identities or money. Pending ownership prevents new stakes/sessions; existing captured payouts can finish subject to the original money and identity guards. Held raw ownership persistence refuses further money writes, and ownership recovery never replays or removes a wager intent. + A table stores `wagerCharacters`, mapping account UUIDs to the character captured when money enters play or the session begins. Tax quotes use that immutable character reference, including offline winners. A missing reference or another active character on the same account cannot collect a payout or refund. A settled round clears bindings when no player stakes remain, even if the house tray is nonempty; the next occupant can then bind their own character. +The synchronous character session-end listener can pay that exact captured character into its still-loaded outgoing body after the current session clears. Sources retain current-session checks. Every outgoing payout leg reserves storage space before effects; another captured character, a successor session, a dead body or insufficient room keeps the money on the felt with a review record. No ending-session authority survives into deferred callbacks. The inventory delivery and table save remain separate effects and require manual review after an interrupted tick; see [OWNERSHIP.md](OWNERSHIP.md). + Citizen tax is a pure `TaxPolicy` quote. Whole-chip rounding and source priority remain unchanged: net payout first, then whatever tax chips the sources can actually supply. The credited tax is exactly the chip value destroyed, in cents, even when the quote rounds or the payout is partial. Partial payments retain the paid amount, actual withholding and remaining obligation separately; they never retry the full gross amount automatically. Interrupted intents, unresolved identity/tax policy, partial winnings and failed guild tray settlements freeze further transfers, round clearing, editing and pickup. On restart the table and journal remain for review. Legacy saved player stakes without a recorded character also require review; loading never guesses from the account's current or last-played character.