diff --git a/projects/SimpleFactions/README.md b/projects/SimpleFactions/README.md index 4e0d254..a435f61 100644 --- a/projects/SimpleFactions/README.md +++ b/projects/SimpleFactions/README.md @@ -9,6 +9,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) +- [Vehicle owners](docs/vehicle-owners.md) - [Installation trade and transport access](docs/installation-trade.md) - [Espionage and special positions](docs/espionage.md) diff --git a/projects/SimpleFactions/docs/treasuries.md b/projects/SimpleFactions/docs/treasuries.md index 7654b63..34a3c0c 100644 --- a/projects/SimpleFactions/docs/treasuries.md +++ b/projects/SimpleFactions/docs/treasuries.md @@ -78,13 +78,17 @@ exact retry after the profit has been settled does not tax it again. 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 +keeps PERSONAL records whose `playerUuid` is the payer character UUID and whose +`accountUuid` caches that character's account for slot limits; personal daily +upkeep and vehicle tax charge only that recorded payer, never the account's +current character. Upkeep covers every vehicle VehicleFramework names a +character or legacy account as owner of, minus faction vehicles; without a +readable VehicleFramework it covers the personal rows. [Vehicle owners](vehicle-owners.md) +covers how owners and rows are kept in step. + +On the first vehicle stage (see [Vehicle owners](vehicle-owners.md#startup)), +`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 @@ -115,6 +119,7 @@ Paths are relative to `plugins/SimpleFactions/`: | `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 | +| `vehicles/owner-migration.json` | One-time vehicle owner migration plan ([Vehicle owners](vehicle-owners.md#files)) | | `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 | diff --git a/projects/SimpleFactions/docs/vehicle-owners.md b/projects/SimpleFactions/docs/vehicle-owners.md new file mode 100644 index 0000000..8e6dc85 --- /dev/null +++ b/projects/SimpleFactions/docs/vehicle-owners.md @@ -0,0 +1,184 @@ +# Vehicle owners + +SimpleFactions decides who may change a VehicleFramework vehicle's owner and who +pays for it. VehicleFramework 2.13 stores owners as characters (`char_`); +SimpleFactions moves the old account-name owners across once, keeps faction +vehicles stamped for their leader, and keeps the payer registry in step. The +[VehicleFramework guides](../../VehicleFramework/README.md) describe the owner +values, the whitelist and the staff commands. + +## Startup + +Factions start without waiting for VehicleFramework. Vehicle work runs in a +separate stage once factions have started and VehicleFramework is enabled, +whichever happens last: SimpleFactions tries straight after it starts, and again +when VehicleFramework enables. The stage runs once per server start, on the main +thread, in this order: + +1. the stage-5 payer migration (`identity/vehicle-payers.json`, once); +2. the owner migration (`vehicles/owner-migration.json`, once); +3. the leader reconcile, which stamps every faction vehicle for its leader; +4. the upkeep reconcile, which also caches the account on older personal rows; +5. the vehicle listeners; +6. the `VehicleOwnershipAuthority` service. + +A failure in steps 1 or 2 disables SimpleFactions, as any other unfinished +financial state does; the next start resumes from the plan files. A failure in +steps 3 or 4 is logged and the stage stops there, so no listener or authority is +registered and VehicleFramework refuses every player ownership change until a +restart. `SimpleFactionsReadyEvent` says nothing about vehicles. + +Vehicle builds start only once the stage has finished in characters mode, +because a finished vehicle is stamped for its builder. If VehicleFramework never +enables, factions run without vehicles and the log says so once. If it is older than 2.13, or `ownership-identity` is not +`characters`, only step 1 and the listeners run: claims are refused for +non-staff, faction stamps are not written, and upkeep is still charged from the +personal registry rows. + +## Owner migration + +The first start in characters mode records a plan before it changes anything. +For each vehicle (stored rows except tombstones, plus loaded vehicles) it keeps +the original owner, whitelist and ticket fields and the decided new values: + +| Vehicle | New owner | +| --- | --- | +| Pool or installation row | The paying faction's leader character, else `char_none` | +| Personal row | The row's character, when the old name still belongs to that account and the account plays that character | +| No row | The reviewed heir of the account the name round-trips to | +| `none`, `char_*` | Unchanged | + +An old name belongs to an account when DenarEconomy resolves `player_` to it +and the name is still that account's latest name: the name Bukkit knows for the +account, or, when the server's user cache has forgotten it, the name DenarEconomy +remembered last for it in `plugins/DenarEconomy/Data/player-names.json`. That +index does not expire, so an old user cache does not quarantine vehicles, while +a name the account has since replaced still fails. + +Anything else is quarantined: it keeps its old `player_` value, which nobody +matches, and is listed by `/vf admin quarantine`. Whitelist entries move to the +named account's heir when the name round-trips and otherwise stay, greyed out. + +Before the first write the plan is synced to disk, then two fixed-name backups +are taken in `plugins/SimpleFactions/vehicles/`: `vehicles-6a-premigration.db` +(a `VACUUM INTO` copy of VehicleFramework's database, checked with +`quick_check`) and `vehicles_registry-6a-premigration.json`. They sit outside +VehicleFramework's rotating backups and are never retaken once the plan says +`backedUp`. + +Each vehicle is then written only if it still holds its planned original +values; one that already holds the new values is marked done, one that holds +anything else (for example a staff takeover since the plan) is quarantined, and +one that no longer exists is counted as gone. Afterwards every vehicle's stored +and loaded copies are read separately; copies that disagree fail the stage. The +plan is then marked `completed` and never runs again. + +## Ownership changes + +VehicleFramework asks SimpleFactions about every player change through +`VehicleOwnershipChangeEvent` and reports the result through +`VehicleOwnershipChangedEvent`, both carrying one operation ID: + +- Staff (`vf.admin`) are not refused, and their takeovers pay nothing; each + staff claim, takeover and clear is logged as `[vehicle audit]`. +- Any other change to a pool or installation vehicle is refused, including the + whitelist and ticket switches. The leader stamp only opens the ownership menu + and the seats. +- A claim needs the claimer's current character, ready vehicle payments and a + free personal slot, then pays the transfer fee when someone else owned the + vehicle last. The first click shows the fee and the second pays it under the + operation ID. +- After a successful claim or takeover the payer row names the new character + and caches its account. A failed claim refunds its fee. Clearing ownership + drops the payer row but keeps the last owner, so a friend who claims the + released vehicle still pays the transfer fee. + +SimpleFactions' own writes (construction, berthing, the pool, releases, +handovers, installation transfers and leader stamps) go through VehicleFramework's +`setOwnership`. The payer row is written first and put back if the owner write +fails. A constructed vehicle is stamped for the character that paid its +registration fee; if VFBuilders recorded a different constructor (or, for older +stations, the account's heir is someone else) it is stamped `char_none` and +listed for staff. If the stamp cannot be written the vehicle stays unowned, its +build receipt stays open and it is listed for staff. + +A vehicle on the staff list is held: players cannot claim or change it and +leader stamps leave it alone until staff give it an owner (for example with +`/vf takeover`), after which it drops off the list. + +Gift, berth and pool requests capture both characters when they are offered and +refuse acceptance from any other character. + +## Leader stamps + +A faction leader change restamps that faction's vehicles at once, stored or +loaded, keeping their whitelists. Deleting a faction stamps its remaining rows +`char_none`; annexed and untangled vehicles have already moved with their +installations. Leaders without a character (no leader, a staff `dummy_N`) stamp +`char_none`. A click on a faction vehicle whose stamp is stale is refused once +while the stamp is fixed on the next tick, and the ownership menu is hidden +until then. + +## Upkeep reconcile + +At startup and every 24 hours SimpleFactions compares VehicleFramework's owners +with the payer registry. A character-owned vehicle without a row gets a personal +row for that character. Everything else that disagrees is listed for staff in +`/vf admin quarantine` and left alone: a vehicle owned by a current faction +leader with no row, a personal row whose vehicle names someone else, a stale +faction stamp, a row without a vehicle, and a personal row whose account cannot +be worked out. + +## Slot limits + +Personal limits count personal registry rows per account. Rows cache the account +when they are written, and the vehicle stage fills it in on older rows from the +character, or from the account the owner or payer migration matched. A row with +no knowable account counts against every claimer until staff resolve it, and a +lookup error refuses the claim. + +## Rollback + +Roll back the whole stage 6a set together with the server stopped. For +vehicles, run the rollback tool from the server folder, first without `--apply` +to see what it would do. It needs Gson 2.10 or newer and sqlite-jdbc on the +classpath; a server's `libraries/` folder can hold several Gson versions +(Main's has 2.8.0, which is too old), so pick the newest of each: + +```sh +GSON=$(ls libraries/com/google/code/gson/gson/*/gson-*.jar | sort -V | tail -n 1) +SQLITE=$(ls libraries/org/xerial/sqlite-jdbc/*/sqlite-jdbc-*.jar | sort -V | tail -n 1) +java -cp "plugins/SimpleFactions-.jar:$GSON:$SQLITE" \ + net.tfminecraft.simplefactions.vehicles.owners.VehicleOwnerRollback . +``` + +Add `--apply` once the dry run looks right. With an older Gson or no sqlite-jdbc +the tool stops before reading anything and names the jar it needs. + +It reads `vehicles/owner-migration.json`, finished or interrupted, and for each +planned vehicle whose +SQLite row still holds exactly the migration's owner, whitelist and whitelist +switch, puts the original values back in the `owner` column and the payload's +`owner`, `whitelisted` and `whitelist` keys, in one transaction. Position, +health, fuel, cargo and tickets are never touched. Changed and missing vehicles +are listed and left alone. With `--apply` it also moves the plan aside +(`owner-migration.json.rolled-back-