From d5f3e20e9f6ff0b3fe52b186dce76b0c954cacf0 Mon Sep 17 00:00:00 2001 From: Ryan Barlow <7389646+ryanbarlow97@users.noreply.github.com> Date: Mon, 12 Oct 2026 18:41:25 +0000 Subject: [PATCH 1/3] docs: describe character faction migration and public APIs --- projects/SimpleFactions/README.md | 1 + projects/SimpleFactions/docs/characters.md | 68 ++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 projects/SimpleFactions/docs/characters.md diff --git a/projects/SimpleFactions/README.md b/projects/SimpleFactions/README.md index 3f1f730..d4f8fe2 100644 --- a/projects/SimpleFactions/README.md +++ b/projects/SimpleFactions/README.md @@ -7,6 +7,7 @@ Technical documentation is maintained here. Run commands from the source checkou TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](../../PLATFORM.md) for runtime, build and validation conventions. - [Overview](overview.md) +- [Character membership and migration](docs/characters.md) - [Installation trade and transport access](docs/installation-trade.md) - [Espionage and special positions](docs/espionage.md) diff --git a/projects/SimpleFactions/docs/characters.md b/projects/SimpleFactions/docs/characters.md new file mode 100644 index 0000000..95265b7 --- /dev/null +++ b/projects/SimpleFactions/docs/characters.md @@ -0,0 +1,68 @@ +# Character membership + +Membership, leadership, councils, votes, mercenary enrollment and stored member +references use character UUIDs. Two characters played by one Minecraft account +can belong to different guilds, including rival realms. A player without a ready +character session has no membership. Menus capture the opening character session +and close when that session is stale. + +## Migration and readiness + +SimpleFactions waits for RPCharacters' reviewed legacy heir map before loading +faction state. During that wait commands report startup, timers and persistence +are inactive, and dependent plugins must defer loading or saving faction-owned +state. `SimpleFactionsApi.ready()` reports availability and +`SimpleFactionsReadyEvent` signals startup completion. + +`migrations/character-v1.json` records progress for each store. Originals are +backed up under `migrations/backup/`; interrupted writes restore their backup and +replay. Legacy aliases resolving to one heir are merged. Unresolved records keep +their original JSON, role and amounts in quarantine, with stable record IDs. +Unresolved leaders block succession and disbanding. + +Staff commands: + +```text +/f admin quarantine list +/f admin quarantine assign +``` + +Assignments take effect on the next startup. Back up faction data together with +RPCharacters and DenarEconomy data before a coordinated cutover or rollback. + +## Plugin APIs + +`FactionManager.getByMember`, `getByLeader`, `getGuildByMember` and +`getGuildByLeader` accept a `Player`, a character `UUID`, or a stored member-key +`String`. UUID arguments are character IDs. Nullable lookups cannot distinguish +startup unavailability from absence: check `SimpleFactionsApi.ready()` before +making persistence decisions. The same player and UUID lookups on +`SimpleFactionsApi` return `Optional` values and remain empty until ready. + +`Members.key(player)` resolves the active character. `Members.display(key)` is +its display name; `Members.online(key)` resolves only an online player currently +playing that character. Use those helpers instead of passing stored member keys +to Bukkit's account-name lookup methods. + +A guild's immutable `getIncarnationId()` identifies its treasury across renames. +`Bank.accountId()` exposes the ledger account name for transactional callers. +Disbanding and recreating a guild with the same name gives it another incarnation. + +## Public map data + +The nation export uses an explicit public projection. Leader display fields, +member display names and counts, provinces, banners, public relations and guild +branches remain available. Character member keys, account balances, loans, +ledgers, elections, espionage and other private saved state are excluded. + +See [the character identity API](../../TLibs/README.md) and +[DenarEconomy](../../DenarEconomy/README.md) for the shared providers. + +## Combat identity + +Warbands and battle roster IDs remain Minecraft account UUIDs for live combat. +Each enrollment also captures its character reference. Rejoining restores only +that character's former participation; switching characters does not inherit it. +`BattleStartedEvent.getParticipants()` and `BattleEndedEvent.getParticipants()` +return the captured character references, including participants who have since +disconnected. `getParticipantIds()` remains available for account-scoped callers. From 2c41e3eb7f539e4e58fd45c1cfbabe4c639e1b47 Mon Sep 17 00:00:00 2001 From: Ryan Barlow <7389646+ryanbarlow97@users.noreply.github.com> Date: Mon, 12 Oct 2026 19:38:54 +0000 Subject: [PATCH 2/3] docs: explain character treasury recovery and succession --- projects/SimpleFactions/README.md | 1 + projects/SimpleFactions/docs/characters.md | 37 +++++++- projects/SimpleFactions/docs/treasuries.md | 101 +++++++++++++++++++++ 3 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 projects/SimpleFactions/docs/treasuries.md diff --git a/projects/SimpleFactions/README.md b/projects/SimpleFactions/README.md index d4f8fe2..4e0d254 100644 --- a/projects/SimpleFactions/README.md +++ b/projects/SimpleFactions/README.md @@ -8,6 +8,7 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. - [Overview](overview.md) - [Character membership and migration](docs/characters.md) +- [Treasuries and financial recovery](docs/treasuries.md) - [Installation trade and transport access](docs/installation-trade.md) - [Espionage and special positions](docs/espionage.md) diff --git a/projects/SimpleFactions/docs/characters.md b/projects/SimpleFactions/docs/characters.md index 95265b7..e72a23f 100644 --- a/projects/SimpleFactions/docs/characters.md +++ b/projects/SimpleFactions/docs/characters.md @@ -8,8 +8,8 @@ and close when that session is stale. ## Migration and readiness -SimpleFactions waits for RPCharacters' reviewed legacy heir map before loading -faction state. During that wait commands report startup, timers and persistence +SimpleFactions waits for RPCharacters' reviewed legacy heir map and DenarEconomy's +ledger before loading faction state. During that wait commands report startup, timers and persistence are inactive, and dependent plugins must defer loading or saving faction-owned state. `SimpleFactionsApi.ready()` reports availability and `SimpleFactionsReadyEvent` signals startup completion. @@ -36,7 +36,8 @@ RPCharacters and DenarEconomy data before a coordinated cutover or rollback. `getGuildByLeader` accept a `Player`, a character `UUID`, or a stored member-key `String`. UUID arguments are character IDs. Nullable lookups cannot distinguish startup unavailability from absence: check `SimpleFactionsApi.ready()` before -making persistence decisions. The same player and UUID lookups on +making persistence decisions. Player and UUID lookups are unavailable until +startup recovery completes. The same player and UUID lookups on `SimpleFactionsApi` return `Optional` values and remain empty until ready. `Members.key(player)` resolves the active character. `Members.display(key)` is @@ -66,3 +67,33 @@ that character's former participation; switching characters does not inherit it. `BattleStartedEvent.getParticipants()` and `BattleEndedEvent.getParticipants()` return the captured character references, including participants who have since disconnected. `getParticipantIds()` remains available for account-scoped callers. + +## Death and succession + +Committed character deaths, startup reconciliation and hourly reconciliation +use the same durable death journal, keyed by RPCharacters' authoritative death +ID. Cleanup removes the deceased character's membership, offices, council and +voting rights, movement participation and captured warband participation. It +also ends a council session hosted by that character. Another character on the +same Minecraft account keeps its own rights. + +Cleanup saves each affected faction or warband before acknowledging that step. +Fixed wages and the captured per-member dividend amount become payable claims +only after the original payout rights are durably removed. They survive guild +or faction dissolution and remain payable to the deceased character's bank. +Daily settlement records actual cents paid, including partial payments. + +An ordinary guild leader's successor is the first living remaining member in +its original join order. Empty ordinary guilds disband. A completely empty +realm is removed while its ledger treasury remains. If the ruling guild empties +but another guild still has members, the realm and its treasuries remain: +elective governments start the normal election; a nonelective ruler remains +vacant for staff resolution. Unresolved quarantined leaders block succession +and disbanding. A later staff appointment is preserved during replay. + +Dead warband leaders pass leadership to the captured first living participant, +or leave a vacant seat when none is available. Pending rejoin records still +identify the deceased character if its session ended before the death event. + +See [treasury recovery](treasuries.md) for journal paths and coordinated backup +requirements. diff --git a/projects/SimpleFactions/docs/treasuries.md b/projects/SimpleFactions/docs/treasuries.md new file mode 100644 index 0000000..d93461e --- /dev/null +++ b/projects/SimpleFactions/docs/treasuries.md @@ -0,0 +1,101 @@ +# Treasuries and financial recovery + +SimpleFactions uses DenarEconomy's ledger for guild money. Each guild incarnation +has a `guild:` account; its location and display name do not +identify its money. Recreating a disbanded guild gives it a different account. +`Bank.getWealth()` is a read-only denar view. Guild debt remains signed, and +moving or removing a bank does not transfer its balance. + +Legacy treasury balances and accumulated citizen tax are imported once using +`import-guild:` and `import-tax:` operation IDs. +Citizen tax is withheld inside the earning transaction and credited to +`guildtax:`. SimpleFactions registers its tax policy only after +startup recovery completes. Consumers must defer taxable transactions while +that policy is unavailable. + +## Daily settlement + +The day counter tracks server uptime, rather than calendar dates. One balanced +posting, `sf:settle:`, covers every realm's daily money movements. The +stored plan includes all cents, account IDs and resulting financial state; +recovery never recalculates a published plan. + +Preparation records the calculation timestamp, upkeep decisions and the selected +province losses before applying nonfinancial changes. It then records exact +movement organization and offline-day values. Recovery finishes this same +prepared day before allowing new gameplay or death reconciliation. + +The settlement preserves the order of army and installation upkeep, trade and +other income, inter-guild transfers, liquidation, dividends and wages, loan +and contract updates, and finally vehicle upkeep. Army upkeep can preserve debt. +Unaffordable installation upkeep downgrades or removes the installation without +charging it. Vehicle upkeep requires the full projected amount, including any +tax; insufficient funds leave the balance intact and record unpaid maintenance. +Personal vehicle payers are captured character IDs. + +Available payout funds are shared between dividends and wages. Unpaid wages and +fixed claims owed to deceased characters remain owed. Death dividend claims +reserve part of the dividend pool before living members receive their shares; +they do not add another copy of that payout. Claims against a disbanded guild +can still use its preserved incarnation treasury. + +All monetary legs post together. Only an `APPLIED` result permits the recorded +branch levels, loan balances, histories, maintenance state and payment receipts +to be saved. A failed save stops faction gameplay until recovery. The completed +day clock is persisted with these effects; a restart cannot pay that day twice. + +## Other financial actions + +Deposits, withdrawals, staff `addguildbank`, branch changes, loan acceptance and +repayment, mercenary formation, capital changes, relocation and pillage use +durable operation intents. They capture the actor's character session and the +participating guild incarnations. Confirmations cannot act for a new character +or a replacement guild. + +Loan offer IDs are paid once. Principal acceptance and its debt record recover +together. Manual repayments credit the lender directly and persist exact debt +and credit-score changes; they do not create another daily receipt. Pillage +uses the war identity and attacker incarnation to recover the same loot and +penalties. Capital and relocation recovery also repairs an interrupted change +across faction files before those files are loaded into domain objects. + +Material deposits credit the captured character's bank and actual citizen tax +holding account in one posting. Materials are removed only after `APPLIED`. +Staff physical coin withdrawals record the proposed inventory change before +posting. An interrupted material removal or coin delivery requires staff +reconciliation: recovery does not repeat physical delivery automatically. + +Games casino profit bookkeeping has its own durable intent. The money has +already reached the guild treasury; this records its taxable income once. +Startup finishes an incomplete bookkeeping save before daily settlement. An +exact retry after the profit has been settled does not tax it again. + +## Saved recovery state + +Paths are relative to `plugins/SimpleFactions/`: + +| Path | Purpose | +| --- | --- | +| `day-preparation/.json` | Captured timestamp and nonfinancial day preparation | +| `settlements/.json` | Immutable realm posting and exact financial after-values | +| `economy/callers/` | Interactive treasury and topology operation intents | +| `external-income/` | Idempotent external casino income bookkeeping | +| `deaths/` | Death cleanup, succession and outstanding character claims | +| `deaths/payments/` | Settlement acknowledgments for those claims | + +An intent is forced to storage before its dependent action. Completed intents +and receipts are financial history, not disposable retry files. Restoring a +completed receipt without its corresponding DenarEconomy posting is rejected. + +For an unresolved operation, retain its file, operation ID and matching ledger +status. For physical items, also inspect the captured character, inventory +snapshot, item bytes and amount. There is no command to discard an ambiguous +physical intent. Correct the underlying persistence or data problem, reconcile +physical evidence where necessary, and restart to resume the saved intent. + +Back up or roll back SimpleFactions, RPCharacters and DenarEconomy together using +a consistent stopped-server copy. Do not combine an old ledger with newer +faction journals or delete receipts to force another payment. + +See [character membership](characters.md) and the shared +[DenarEconomy guide](../../DenarEconomy/README.md). From f9e550b1dce81c1c177b24a9943512deaa305fe8 Mon Sep 17 00:00:00 2001 From: Ryan Barlow <7389646+ryanbarlow97@users.noreply.github.com> Date: Mon, 12 Oct 2026 20:22:59 +0000 Subject: [PATCH 3/3] docs: describe vehicle finance and bound loan offers --- projects/SimpleFactions/docs/treasuries.md | 38 +++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/projects/SimpleFactions/docs/treasuries.md b/projects/SimpleFactions/docs/treasuries.md index d93461e..7654b63 100644 --- a/projects/SimpleFactions/docs/treasuries.md +++ b/projects/SimpleFactions/docs/treasuries.md @@ -52,7 +52,10 @@ durable operation intents. They capture the actor's character session and the participating guild incarnations. Confirmations cannot act for a new character or a replacement guild. -Loan offer IDs are paid once. Principal acceptance and its debt record recover +Loan offer IDs are paid once. An offer book records the lending guild's +incarnation; books written before that binding existed, or whose lending guild +has been deleted and recreated under the same ID, are rejected as out of date +and the lender must issue a new agreement. Principal acceptance and its debt record recover together. Manual repayments credit the lender directly and persist exact debt and credit-score changes; they do not create another daily receipt. Pillage uses the war identity and attacker incarnation to recover the same loot and @@ -70,6 +73,34 @@ already reached the guild treasury; this records its taxable income once. Startup finishes an incomplete bookkeeping save before daily settlement. An exact retry after the profit has been settled does not tax it again. +## Vehicle finance + +Vehicle payments use the selected living character. Construction and +maintenance capture a character session; handovers capture both participants, +so switching characters cannot redirect an armed payment. `Cache/vehicles_registry.json` +keeps PERSONAL records whose `playerUuid` is the payer character UUID; personal +daily upkeep and vehicle tax charge only that recorded payer, never the +account's current character. VehicleFramework owner tags still describe +physical ownership and are not migrated to characters. + +On the first startup with VehicleFramework enabled, `identity/vehicle-payers.json` +records every player-owned vehicle from VehicleFramework's persisted +repository, mapping the account to its legacy heir character. Existing registry +records win, the plan is saved before the registry changes, and it never +reinterprets later owner-tag changes. Unresolved owners keep a null payer and +stay unpaid; there is no in-game command to reassign them, and the completed +plan must not be deleted to force a new mapping. + +Fees, refunds and maintenance use durable intents in `vehicle-payments/`, +recovered at startup before realm settlement. `Cache/vehicle_fees.json` holds +outstanding build, claim and handover receipts; an unfinished receipt blocks +new ownership payments for its vehicle and leaves its daily upkeep unpaid, +rather than charging the previous payer. A construction refund pays the +originally captured character from the original guild incarnation, limited to +the treasury's positive balance. Maintenance explicitly selects bank or pouch +and never falls back to the other. Daily vehicle charges run last inside the +single `sf:settle:` posting, as described above. + ## Saved recovery state Paths are relative to `plugins/SimpleFactions/`: @@ -82,6 +113,11 @@ Paths are relative to `plugins/SimpleFactions/`: | `external-income/` | Idempotent external casino income bookkeeping | | `deaths/` | Death cleanup, succession and outstanding character claims | | `deaths/payments/` | Settlement acknowledgments for those claims | +| `vehicle-payments/` | Vehicle fee, refund and maintenance intents | +| `identity/vehicle-payers.json` | One-time legacy vehicle payer migration plan | +| `Cache/vehicles_registry.json` | Vehicle ownership scopes and personal payer characters | +| `Cache/vehicle_fees.json` | Outstanding vehicle payment receipts and owner history | +| `Cache/vehicle_maintenance.json` | Vehicle upkeep and unpaid maintenance state | An intent is forced to storage before its dependent action. Completed intents and receipts are financial history, not disposable retry files. Restoring a