diff --git a/PLATFORM.md b/PLATFORM.md index 20211bf..0d1cb74 100644 --- a/PLATFORM.md +++ b/PLATFORM.md @@ -2,7 +2,9 @@ TFMC plugins target **Java 21 / Minecraft 1.21.10**, using `io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT` with `provided` scope. -Plugin descriptors declare API 1.21.10. +Plugin descriptors declare API 1.21.10 except TLibs and VehicleFramework, which +retain `api-version: '1.21.4'` loader metadata. Both still compile against Paper +1.21.10 with Java 21; their descriptor values do not change the TFMC runtime target. ## Runtime and toolchain diff --git a/README.md b/README.md index 7d8707a..91c1355 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ guides live here. TFMC runs **Minecraft 1.21.10 on Java 21**. The [shared platform and build baseline](PLATFORM.md) defines common runtime, toolchain and validation conventions, with the current build declarations for each plugin. +## Features + +- **Project guides** — Setup, gameplay, configuration, architecture, and operations for each active project. +- **Shared conventions** — Platform targets, build pipelines, and documentation rules maintained in one place. +- **Link validation** — Automated checks for local links, section anchors, and project navigation. + ## Start here | Looking for… | Go to… | @@ -98,6 +104,23 @@ This repository is public. Private source repositories and licensed binaries retain their existing access restrictions. Licenses, contribution policies, and code/API comments remain with their source projects. +## Tests + +Run the same checks as CI with Python 3.10 and the pinned documentation dependencies: + +```sh +python -m pip install -r requirements.txt +python -m unittest discover -s scripts -p 'test_*.py' +python scripts/check-indexes.py +python -m mkdocs build --strict --config-file projects/CoreProtect/mkdocs.yml --site-dir /tmp/tfmc-coreprotect-site +``` + +The unittest suite exercises the Markdown link checker; the index check validates +local links and project navigation across the documentation. Results are printed +to the terminal, and the MkDocs output goes to `/tmp/tfmc-coreprotect-site`. +There is no coverage gate. These checks do not verify remote URLs, source-code +behavior, or live Minecraft servers. + ## License Copyright (c) 2026 TF-Minecraft contributors. diff --git a/projects/ActivityTF/README.md b/projects/ActivityTF/README.md index 34c0690..8b3cd0d 100644 --- a/projects/ActivityTF/README.md +++ b/projects/ActivityTF/README.md @@ -10,7 +10,7 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. Build with JDK 21 and `mvn clean verify`. The POM uses compiler release 21 and Paper API `1.21.10-R0.1-SNAPSHOT`; the loader API is `1.21.10`. -Install the shared Maven plugin dependencies selected by the POM, then supply the four private `libs/` dependencies (VotingPlugin, MMOCore, MMOItems and MythicLib). `.github/scripts/prepare-release.sh` retrieves pinned ServerAssets files and checks `.github/dependencies.sha256`; it needs read access through `GH_TOKEN` (the CI `DEPS_TOKEN` secret). Output is `target/activity-.jar`; CI uses a dated development version. +Install the shared Maven plugin dependencies selected by the POM, then supply the four private `libs/` dependencies (VotingPlugin, MMOCore, MMOItems and MythicLib). `.github/scripts/prepare-release.sh` retrieves pinned ServerAssets files and checks `.github/dependencies.sha256`; it needs read access through `GH_TOKEN` (the CI `DEPS_TOKEN` secret). Output is `target/activity-.jar`; pull request builds publish a dated development version. Optional runtime integrations are declared as `softdepend` in `src/main/resources/plugin.yml`; those declarations do not remove their compile-time JAR requirements. `ActivityPlugin` registers hooks for installed plugins, `ActivityManager` owns task progress and rewards, and `/activity` opens the GUI or runs permitted admin actions. diff --git a/projects/ActivityTF/REWARD-POOLS.md b/projects/ActivityTF/REWARD-POOLS.md index 1677bb3..fbd00c8 100644 --- a/projects/ActivityTF/REWARD-POOLS.md +++ b/projects/ActivityTF/REWARD-POOLS.md @@ -1,6 +1,11 @@ # Named reward pools -Configuration changes can be applied with `/activity reload`. +Configuration reloads stage reward changes for the next weekly reset. The current +week keeps the reward selection recorded in `reward-lock.yml`; `/activity rewards` +shows the locked week and whether an update is pending. The lock includes reward +pools, multipliers, milestone drops, `bar.milestones` and daily reward groups. +To deliberately replace the current week's rewards, run `/activity rewards apply`. +These commands require `activity.admin` (operators by default). Example (replace the sample rewards with your own): @@ -42,8 +47,8 @@ Whitelist only material pools, such as `pool_prologue`, and leave skin/scroll pools out. At multiplier 2, this gives two independent material draws per material milestone while a `pool_skin` milestone still awards one scroll. Existing configurations must add the whitelist before extra pool draws apply; -`/activity reload` picks up changes. Fixed-item rewards retain their existing -amount multiplier. +`/activity reload` stages those reward changes for the next week unless staff +apply them immediately. Fixed-item rewards retain their existing amount multiplier. `drop_N` selects the Nth configured milestone, so only define drops that exist. Named pools use the `pool_` prefix and are case-insensitive. Lists directly under diff --git a/projects/AdvancedCrafting/configuration.md b/projects/AdvancedCrafting/configuration.md index ff22379..fb0d4fa 100644 --- a/projects/AdvancedCrafting/configuration.md +++ b/projects/AdvancedCrafting/configuration.md @@ -33,12 +33,34 @@ Recipe/profession permissions use the configured `permission-prefix` and namespa | `/ac sync recipes` | Inspect stored alloy recipe synchronisation | | `/ac sync recipes repair` | Repair stored alloy recipe synchronisation; back up data first | | `/ac give alloy [player]` | Give an alloy item | +| `/ac give equipment [player] [quality]` | Give finished equipment using the separate staff permission described below | | `/ac alloy info ` | Show alloy information | | `/ac craft ` | Arm the player's next station craft for 30 seconds, clamping quality to 0–100 | | `/alloy name ` | Name an alloy through the existing player workflow | -All `/ac` actions above require the admin permission. `/alloy name` follows its -own alloy manager checks. Consult +All `/ac` actions above except `give equipment` require the admin permission. +`give equipment` uses its configured permission; `/alloy name` follows its own +alloy manager checks. Consult [CommandManager](https://github.com/TF-Minecraft/AdvancedCrafting/blob/main/src/main/java/net/tfminecraft/advancedcrafting/managers/CommandManager.java) for parsing and player/console restrictions. Use a restart when changing plugin JARs; configuration reload does not replace loaded classes. + +## Staff equipment grants + +`/ac give equipment [player] [quality]` +gives one finished item using a configured recipe and compatible main material. +For example, `/ac give equipment heavy_chestplate ingredient.steel_ingot Alex 100`. +Tab completion lists loaded recipes, compatible ingredients/alloys and online players. +Omit the player to give to yourself; console must specify an online player. +Quality defaults to 100 and accepts finite values from 0 to 100 (specify the player +before quality). Alloy IDs are the loaded discovery IDs, not display names. + +Secondary recipe ingredients use the lowest configured tier, then ingredient ID +alphabetically to break ties. Their stats and appearance participate normally. +The item retains normal stats, quality sockets, appearance, tier and crafting +provenance; no materials are consumed and no XP or activity rewards are granted. +Full inventories drop the item at the recipient's location. + +Set `give-equipment-permission` in `config.yml` and run `/ac reload` to change +access. Missing or blank settings default to `advancedcrafting.admin` (operators). +Grant a custom permission only to staff; it does not grant other admin commands. diff --git a/projects/AdvancedCrafting/setup.md b/projects/AdvancedCrafting/setup.md index 50b6a95..01d6245 100644 --- a/projects/AdvancedCrafting/setup.md +++ b/projects/AdvancedCrafting/setup.md @@ -13,8 +13,8 @@ For a new server, while stopped, create `plugins/AdvancedCrafting/` and copy the `recipes/`, `colour-schemes/`, `model-schemes/` and `naming-schemes/` directories from the source's `src/main/resources/` into it. Adapt the examples to the server's MMOItems templates and item paths. The current bootstrap loads these directories -but does not extract their contents automatically; it also does not create the -model-schemes directory. Top-level default YAML files are copied only when missing. +and creates them when missing, but does not extract their contents automatically. +Top-level default YAML files are copied only when missing. Do not overwrite existing production configuration or data during this step. Place one AdvancedCrafting release JAR in `plugins/`, start the server, and check @@ -28,9 +28,9 @@ ServerAssets. Build from `main`. Clone TLibs `main` next to the source checkout as `../tlibs`: ```sh -python3 ../tlibs/tools/install-plugins.py --pom pom.xml --mode pinned -GH_TOKEN="$(gh auth token)" bash .github/scripts/prepare-release.sh -mvn clean verify +python3 ../tlibs/tools/install-plugins.py --pom pom.xml --mode pinned && + GH_TOKEN="$(gh auth token)" bash .github/scripts/prepare-release.sh && + mvn clean verify ``` Pinned mode installs the versions declared in the POM. The @@ -42,9 +42,10 @@ in the private preparation script. Licensed JARs are not bundled with the releas ## Releases -PR and main builds verify the source and upload a `DEV-YYYYMMDD-HHmm` JAR plus -exact shared dependency metadata. Numeric tags must match the -committed Maven version. The tag workflow creates a draft release with the JAR, -`SHA256SUMS`, and `build.json` containing source and resolved dependency provenance. +PRs and pushes to `main` verify the source. PRs upload a `DEV-YYYYMMDD-HHmm` JAR +plus exact shared dependency metadata; main builds do not publish development JARs. +The release workflow sets Maven's version from the numeric tag and creates a draft +release with the JAR, `SHA256SUMS`, and `build.json` containing source and resolved +dependency provenance. Inspect the draft before publication. Use a new version for corrections; never replace an existing published version's bytes. diff --git a/projects/AdvancedCrafting/verification.md b/projects/AdvancedCrafting/verification.md index eec7e1e..dcd17fe 100644 --- a/projects/AdvancedCrafting/verification.md +++ b/projects/AdvancedCrafting/verification.md @@ -4,14 +4,20 @@ ## Automated evidence -The source contains no unit tests. `mvn clean verify` checks source -compilation and packaging; a successful build must not be described as gameplay -coverage. CI uses Java 21 and publishes test reports if tests are added later. - -Release checks verify that the tag matches Maven, the JAR has a plugin descriptor, -and the release checksum matches its bytes. The descriptor receives the same -version as the JAR through Maven resource filtering. Compile ActivityTF, TFMCCore, Thievery and Recycler and run their available -tests against the matching API version. +With Java 21 and the [build dependencies](setup.md#build-from-source) prepared, +run `mvn clean verify`. JUnit 5, MockBukkit and Mockito exercise crafting, alloys, +commands, persistence and plugin boundaries. JaCoCo checks every production class +and requires 100% instruction, branch and line coverage without exclusions. +Surefire reports are in `target/surefire-reports/`; coverage HTML, XML and CSV +are in `target/site/jacoco/`. Build CI uploads test and coverage reports; release +CI uploads coverage reports. Available reports are uploaded after failures too. +These tests do not start a live Paper server or the external plugin set. + +Release CI sets Maven's version from the tag, checks the packaged plugin descriptor +against that version, and verifies the release checksum against the JAR bytes. +The descriptor receives the same version as the JAR through Maven resource +filtering. Compile ActivityTF, TFMCCore, Thievery and Recycler and run their +available tests against the matching API version. ## Server smoke checklist diff --git a/projects/Archaeo/README.md b/projects/Archaeo/README.md index 75e887b..66e267f 100644 --- a/projects/Archaeo/README.md +++ b/projects/Archaeo/README.md @@ -17,8 +17,7 @@ From the source checkout, run `mvn clean verify`. The JAR is written to - [Gameplay guide and default tools](docs/gameplay.md) - [Custom-pack player guide](docs/pack-gameplay.md) - [Lore catalog editing](docs/lore-catalogs.md) -- [Original design reference (Spanish)](docs/concepto.md) — includes proposals; - it does not establish implemented behavior. +- [Tests, coverage and optional API contracts](docs/testing.md) Technical guides live here. Plugin code, bundled YAML defaults, and optional pack assets stay in [Archaeo](https://github.com/TF-Minecraft/Archaeo). TFMC-specific @@ -27,4 +26,4 @@ settings and lore catalogs live in private ## Builds and releases -See the [shared pipeline guide](../../PIPELINES.md). Pull requests and pushes to `main` build dated development JARs; matching numeric `v*` tags create draft releases with checksums. +See the [shared pipeline guide](../../PIPELINES.md). Pull requests and pushes to `main` verify the source; only pull requests publish dated development JARs. Numeric `v*` tags supply the release version and create draft releases with checksums. diff --git a/projects/Archaeo/docs/testing.md b/projects/Archaeo/docs/testing.md new file mode 100644 index 0000000..9e0ca97 --- /dev/null +++ b/projects/Archaeo/docs/testing.md @@ -0,0 +1,31 @@ +# Testing + +[Project index](../README.md) + +With Java 21 and Maven installed, run `mvn clean verify`. Tests exercise domain +rules, YAML persistence, and plugin workflows using JUnit 4, Mockito, and MockBukkit. +Surefire test results are in `target/surefire-reports/`. +JaCoCo measures all production classes and writes its HTML report to +`target/site/jacoco/index.html` and machine-readable results to +`target/site/jacoco/jacoco.xml`. No minimum coverage gate is enforced. The Build +workflow uploads the test and coverage reports. + +Tests should assert gameplay behavior, data preservation, or failure handling. +Do not add tests solely to execute a line or manufacture unreachable states to +increase coverage. MockBukkit tests do not replace testing on a real Paper server +with the optional ItemsAdder and MMOItems integrations installed. + +Adapter contract tests can also run against locally supplied plugin jars: + +```sh +mvn -Ppack-api-tests clean verify \ + -Ditemsadder.jar=/path/to/ItemsAdder.jar \ + -Dfastnbt.jar=/path/to/FastNbt-jar.jar \ + -Dmmoitems.jar=/path/to/MMOItems.jar \ + -Dmythiclib.jar=/path/to/MythicLib.jar +``` + +These tests bind the actual API classes and mock their external operations; they +do not start those plugins. The jars remain outside this repository and are used +only on the test runtime classpath. Use the FastNbt version declared by your +ItemsAdder jar. The ordinary test suite needs none of these files. diff --git a/projects/ArmourShop/README.md b/projects/ArmourShop/README.md index bd98727..d8257e4 100644 --- a/projects/ArmourShop/README.md +++ b/projects/ArmourShop/README.md @@ -20,7 +20,7 @@ Run `mvn clean verify` to build and run the available tests; use `mvn clean inst Required plugins declared by the manifest: TLibs, ItemsAdder, TFMCWeb. -Startup loads `config.yml`, `categories.yml`, `base-sets.yml`, `permission-groups.yml` and `Categories/`, then starts pack pulling and catalog sync. `/armourshop` provides the shop and admin subcommands. `armourshop.admin` defaults to false, so grant it explicitly to administrators. Follow the integration guides below for the gateway, approval and ItemsAdder pack workflow. +Startup loads `config.yml`, `categories.yml`, `base-sets.yml`, `permission-groups.yml` and `Categories/`, then starts pack pulling and catalog sync. `/armourshop` provides the shop and admin subcommands. `armourshop.admin` defaults to operators; grant it explicitly to other administrators. Follow the integration guides below for the gateway, approval and ItemsAdder pack workflow. ## Guides diff --git a/projects/BarterShops/README.md b/projects/BarterShops/README.md index f0c0fa5..0dbadc4 100644 --- a/projects/BarterShops/README.md +++ b/projects/BarterShops/README.md @@ -2,7 +2,7 @@ [Source repository](https://github.com/TF-Minecraft/BarterShops) · [All projects](../../README.md) -Sign-based item shops with barter, DenarEconomy payments and faction embargo checks. +Sign-based item shops with DenarEconomy payments and faction embargo checks. TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](../../PLATFORM.md) for runtime, build and validation conventions. @@ -14,7 +14,23 @@ Install these matching Java 21 TFMC artifacts into local Maven before building: Populate `libs/` with the exact files and hashes listed in `.github/dependencies.sha256`. `.github/scripts/prepare-release.sh` downloads those pinned private assets when supplied with the approved dependency token. -Run `mvn clean verify` to build and run the available tests; use `mvn clean install` when another plugin needs the result as a Maven dependency. The plugin JAR is written under `target/`. Gameplay and web integration checks on the Minecraft 1.21.10 server remain separate from build verification. +Install the pinned shared plugin dependencies, download the private build +inputs, then run the build with Java 21. In Bash: + +```bash +python3 path/to/TLibs/tools/install-plugins.py --pom pom.xml --mode pinned && + (read -rsp 'ServerAssets token: ' GH_TOKEN && echo && export GH_TOKEN && + bash .github/scripts/prepare-release.sh) && + mvn clean verify +``` + +The installer comes from a separate TLibs checkout +(`git clone https://github.com/TF-Minecraft/TLibs.git`); point `path/to/TLibs` at it. The token needs Contents read access +to TF-Minecraft/ServerAssets. The prompt keeps it out of shell history, the +subshell keeps it out of your session and Maven, and Maven only runs if both +preparation steps succeed. CI supplies it from `DEPS_TOKEN`. + +Use `mvn clean install` when another plugin needs the result as a Maven dependency. The plugin JAR is written under `target/`. Gameplay and web integration checks on the Minecraft 1.21.10 server remain separate from build verification. ## Runtime and configuration diff --git a/projects/BirdMessenger/README.md b/projects/BirdMessenger/README.md index 476856f..29a7d67 100644 --- a/projects/BirdMessenger/README.md +++ b/projects/BirdMessenger/README.md @@ -14,7 +14,23 @@ Install these matching Java 21 TFMC artifacts into local Maven before building: Populate `libs/` with the exact files and hashes listed in `.github/dependencies.sha256`. `.github/scripts/prepare-release.sh` downloads those pinned private assets when supplied with the approved dependency token. -Run `mvn clean verify` to build and run the available tests; use `mvn clean install` when another plugin needs the result as a Maven dependency. The plugin JAR is written under `target/`. Gameplay and web integration checks on the Minecraft 1.21.10 server remain separate from build verification. +Install the pinned shared plugin dependencies, download the private build +inputs, then run the build with Java 21. In Bash: + +```bash +python3 path/to/TLibs/tools/install-plugins.py --pom pom.xml --mode pinned && + (read -rsp 'ServerAssets token: ' GH_TOKEN && echo && export GH_TOKEN && + bash .github/scripts/prepare-release.sh) && + mvn clean verify +``` + +The installer comes from a separate TLibs checkout +(`git clone https://github.com/TF-Minecraft/TLibs.git`); point `path/to/TLibs` at it. The token needs Contents read access +to TF-Minecraft/ServerAssets. The prompt keeps it out of shell history, the +subshell keeps it out of your session and Maven, and Maven only runs if both +preparation steps succeed. CI supplies it from `DEPS_TOKEN`. + +Use `mvn clean install` when another plugin needs the result as a Maven dependency. The plugin JAR is written under `target/`. Gameplay and web integration checks on the Minecraft 1.21.10 server remain separate from build verification. ## Runtime and configuration diff --git a/projects/BirdMessenger/overview.md b/projects/BirdMessenger/overview.md index 94a5dfa..f1f6ec5 100644 --- a/projects/BirdMessenger/overview.md +++ b/projects/BirdMessenger/overview.md @@ -1,5 +1,6 @@ # BirdMessenger +[Project index](README.md) Right-click the mailbox to send letters. Ordinary left-clicks are protected; sneak-break to remove the mailbox deliberately. @@ -15,12 +16,28 @@ letters: - ia.iasurvival:letter_open_letter ``` -Build with Maven and **JDK 21**, using the Minecraft **1.21.10** API. Install -the matching TLibs and RPCharacters Maven dependencies (see the -[shared installer guide](../TLibs/README.md)), populate `libs/ItemsAdder.jar` -with the checksum-pinned dependency, then run `mvn clean verify`. See -[build and dependencies](README.md#build-and-dependencies) for the current coordinates -and dependency preparation workflow. +## Recipient opt-out + +Players can use `/rpcharacter mail off` in RPCharacters to hide their active +character from the recipient list, or `/rpcharacter mail on` to restore it. If a +recipient opts out before a sender confirms, the letter is returned; already-sent +mail still arrives. + +## Mail recovery + +Mail is stored in `plugins/BirdMessenger/in_flight_mail.yml` and +`plugins/BirdMessenger/pending_mail.yml`. + +If a mail file or one of its entries cannot be decoded, BirdMessenger preserves +the original file beside it as `.corrupt-` before later saves can +replace it. These recovery copies include unreadable entries and should be kept +until the affected mail has been recovered. If the copy cannot be created, +startup fails and the affected file cannot be overwritten by the store. + +## Build and verification + +Follow [build and dependencies](README.md#build-and-dependencies) to prepare the +pinned shared and private dependencies, then run `mvn clean verify` with Java 21. Before deployment, verify on a test server with ItemsAdder: diff --git a/projects/CompanionPets/README.md b/projects/CompanionPets/README.md index 0738260..7251b1e 100644 --- a/projects/CompanionPets/README.md +++ b/projects/CompanionPets/README.md @@ -20,6 +20,7 @@ for runtime, build, and validation conventions. - [Staff commands](docs/staff.md) — commands, permissions, and audit - [Saved data and recovery](docs/saved-data.md) — persistence files, backups, and recovery after a failed save +- [Testing](docs/testing.md) — automated coverage and the dev-only Paper helper - [Default configuration](https://github.com/TF-Minecraft/CompanionPets/blob/main/src/main/resources/config.yml) - [Plugin commands and permissions](https://github.com/TF-Minecraft/CompanionPets/blob/main/src/main/resources/plugin.yml) @@ -28,7 +29,7 @@ for runtime, build, and validation conventions. Build the source repository with Java **21** and Maven: ```sh -mvn -Pcoverage clean install -DskipTests=false -Dmaven.test.skip=false +mvn clean verify python3 .github/scripts/plugin-artifact.py --jar target/companionpets-main-SNAPSHOT.jar --version main-SNAPSHOT ``` @@ -43,28 +44,19 @@ and models (`integration/`), and MMOItems and ItemsAdder supply interaction item and eggs (`item/ItemBridge.java`). MythicLib is only a load-order dependency. CompanionPets has no runtime dependency on Archaeo, Cooking, or MCPets. -The build command runs the unit tests under `src/test/java`; the `coverage` -profile writes a JaCoCo report to `target/site/jacoco/`. +The build command runs the tests under `src/test/java` and enforces 100% +production line coverage. Surefire reports are in `target/surefire-reports/` and +JaCoCo reports are in `target/site/jacoco/`. Coverage runs in every normal +verification; the `coverage` profile is only a compatibility alias. ## Testing on a Paper server The source repository includes a dev-only -[integration helper](https://github.com/TF-Minecraft/CompanionPets/blob/main/integration-tests/README.md) +[integration helper](docs/testing.md#paper-integration-helper) that runs once against a real Paper server with the configured providers and reports `COMPANIONPETS_INTEGRATION PASS` or `FAIL` in the log. It does not establish correct rendering in a Minecraft client. -## Design notes - -The original design notes are preserved in Spanish. They predate the -implementation and do not establish current behaviour: - -- [Pet identity, entities, and movement](docs/design/nucleo-mascota.md) -- [Care and needs](docs/design/cuidado.md) -- [Training and tricks](docs/design/entrenamiento.md) -- [Play and toys](docs/design/juego.md) -- [Hatching, ownership, and management](docs/design/gestion.md) - ## Builds and releases See the [shared pipeline guide](../../PIPELINES.md). Pull requests build diff --git a/projects/CompanionPets/docs/testing.md b/projects/CompanionPets/docs/testing.md new file mode 100644 index 0000000..ebe9eea --- /dev/null +++ b/projects/CompanionPets/docs/testing.md @@ -0,0 +1,67 @@ +# Testing CompanionPets + +Run commands from the [CompanionPets source checkout](https://github.com/TF-Minecraft/CompanionPets) +with Java 21 and Maven. See the [project index](../README.md) for dependencies. + +## Automated tests + +```sh +mvn clean verify +``` + +JUnit, MockBukkit, and Mockito exercise plugin logic, configuration, persistence, +and simulated Bukkit/provider interactions. Surefire reports are in +`target/surefire-reports/`; JaCoCo HTML, XML, and CSV reports are in +`target/site/jacoco/`. Verification requires 100% production line coverage with +no class or package exclusions, and checks that `target/jacoco.exec` and the XML +report exist. CI uploads test and coverage reports. The `coverage` profile is a +compatibility alias; ordinary verification already enforces the same gate. + +These tests do not establish live Paper behaviour or correct client rendering. + +## Paper integration helper + +The [helper source](https://github.com/TF-Minecraft/CompanionPets/tree/main/integration-tests) +runs once, starting 240 server ticks after enabling, then disables itself when +the checks finish. It uses its own `plugins/CompanionPetsSmoke/` persistence files +and namespace, leaving live CompanionPets records alone. + +Build the plugin into the local Maven repository, then compile the helper: + +```sh +mvn clean install +mvn -f integration-tests/pom.xml clean package +``` + +If the plugin Maven version differs from `main-SNAPSHOT`, supply +`-Dcompanionpets.version=` to the second command. Copy +`integration-tests/target/companionpets-smoke-1.0.0.jar` to the dev server +alongside the matching CompanionPets JAR and its configured provider plugins, +then restart dev. The server process must be able to create or write +`plugins/CompanionPetsSmoke/`; prepare this isolated directory with the server +account's ownership if the plugins directory is not writable. + +The helper spawns temporary entities near the first world's spawn, loads and +temporarily force-loads chunks, and places stone test platforms in air for native +movement checks. Its disable handler removes test entities and isolated records, +restores the platforms' original blocks, and releases the chunks it force-loaded. +Cleanup failures are reported as failures. + +Look for `COMPANIONPETS_INTEGRATION PASS checks=...` or +`COMPANIONPETS_INTEGRATION FAIL` in the server log. PASS is emitted only after +the checks and cleanup succeed. Checks use the active configuration: + +- Bodies, egg identity, model attachment, and available mapped/custom clips. +- Native AI pause/resume, Follow navigation, frozen needs, and postures held + across 180 native ticks. +- Adoption and body restoration without losing learning, default learned Follow, + and modeled wolves starting shake with the native shake clock. +- Duplicate removal, owner/staff Pet House actions, confirmed release and heal, + and removal of model registrations and modeled owners. +- Toy landing, offline return with original data, stale projectile rejection, + and configured provider IDs and required animations. + +Keep the helper's staff audit and deletion journal with the log as test evidence. +Remove the helper JAR after running; an installed helper runs again at the next +server restart. CI compiles the harness but does not execute these Paper checks. +Client rendering still needs a separate in-game check. diff --git a/projects/CoreProtect/README.md b/projects/CoreProtect/README.md index 7bbd927..fe3b62a 100644 --- a/projects/CoreProtect/README.md +++ b/projects/CoreProtect/README.md @@ -8,11 +8,12 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. ## TFMC build -Use JDK 21 and `mvn clean verify` from the source checkout. The POM compiles against Paper API `1.21.10-R0.1-SNAPSHOT` with compiler release 21; the loader API is `1.21.10`. Output is `target/coreprotect-.jar`. Dependencies resolve from the POM repositories. +Use JDK 21 and `mvn clean verify` from the source checkout's `master` branch. The POM compiles against Paper API `1.21.10-R0.1-SNAPSHOT` with compiler release 21; the loader API is `1.21.10`. Output is `target/coreprotect-.jar`. Dependencies resolve from the POM repositories. ## Guides - [Project overview](overview.md) +- [Custom item and mob labels](overview.md#custom-item-and-mob-labels) - [docs/api/index.md](docs/api/index.md) - [docs/api/networking.md](docs/api/networking.md) - [docs/api/version/v12.md](docs/api/version/v12.md) diff --git a/projects/CoreProtect/overview.md b/projects/CoreProtect/overview.md index 61e57d7..ac66475 100644 --- a/projects/CoreProtect/overview.md +++ b/projects/CoreProtect/overview.md @@ -110,6 +110,19 @@ The fork is not published to a Maven repository. Consumers pin `coreprotect.vers * WorldEdit changes and supported FAWE clipboard pastes. * *...and more!* +## Custom item and mob labels + +Item and container lookups show saved MMOItems names, types, and IDs. Kill lookups +show recorded mob names and, for deaths recorded with the MythicMobs integration, +the internal mob ID. Ordinary named items are marked as renamed; a display name +alone does not establish MMOItems or MythicMobs identity. + +Historical records receive these labels when the required metadata was saved. +MythicLib and MythicMobs are optional integrations, with vanilla types as the +fallback. Material filters, aggregate counts, and rollback payloads retain their +existing meaning. Custom-ID filtering and restoration of custom mob identities +are not supported. + ## How to use the inspector Once you have the inspector enabled with `/core inspect` or `/co i`, you can do the following: diff --git a/projects/Dowsing/README.md b/projects/Dowsing/README.md index 55b73c5..eb44c64 100644 --- a/projects/Dowsing/README.md +++ b/projects/Dowsing/README.md @@ -10,11 +10,13 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. Dowsing builds with Java **21** against `io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT`. Install the pinned TLibs -`2.0.0`, SimpleFactions `3.0.1`, and Magic `0.2.0` release artifacts with -the shared dependency installer. The build also prepares checksum-verified +`2.0.0`, SimpleFactions `3.0.1`, DenarEconomy `0.2.4`, and Magic `0.2.0` release +artifacts with the shared dependency installer. The build also prepares checksum-verified ItemsAdder, MMOItems, MythicLib, and json-simple inputs from the private ServerAssets repository. +DenarEconomy supplies API types exposed by SimpleFactions bank signatures. + At runtime, `plugin.yml` requires TLibs, SimpleFactions, MMOItems, MythicLib, and ItemsAdder. Magic is optional; configured Magic artifact paths are only resolved when that plugin is present. diff --git a/projects/Gathering/README.md b/projects/Gathering/README.md index 6ae5087..d640557 100644 --- a/projects/Gathering/README.md +++ b/projects/Gathering/README.md @@ -11,7 +11,7 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. Gathering builds with Java **21** against `io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT`. Install the pinned TLibs `2.0.1` and RPCharacters `2.0.2` release artifacts with the shared dependency -installer. The build also prepares checksum-verified MMOCore `1.13.1` and +installer. The build also prepares checksum-verified MMOCore `1.13.1-SNAPSHOT` and MythicLib `1.7.1-SNAPSHOT` inputs from the private ServerAssets repository. At runtime, `plugin.yml` requires TLibs and RPCharacters. MMOCore is optional; diff --git a/projects/GemInfusion/README.md b/projects/GemInfusion/README.md index 77a878b..5c6a623 100644 --- a/projects/GemInfusion/README.md +++ b/projects/GemInfusion/README.md @@ -24,9 +24,24 @@ Gameplay validation remains separate from compilation; follow the ## Runtime and configuration -Required plugins declared by the manifest: TLibs, MMOItems, MythicLib. +Required plugins declared by the manifest: TLibs, MMOItems, MythicLib. MMOCore +is declared optional. -`InfusionMain` loads `config.yml`, `goldsmithing.yml` and the `goldsmithing/` definitions for hits, materials, tiers, qualities and projects. It loads persisted stations, flushes them periodically and on shutdown, and registers socket/unsocket listeners. `/geminfusion reload` reloads definitions and stations; `/geminfusion select ` selects a project. Admin access uses `geminfusion.admin`; goldsmithing gameplay uses `professions.goldsmith` (false by default). +`InfusionMain` loads `config.yml`, `goldsmithing.yml` and the `goldsmithing/` +definitions for hits, materials, tiers, qualities and projects. It loads +persisted stations, flushes them periodically and on shutdown, and registers +socket/unsocket listeners. + +`/geminfusion reload` reloads definitions and stations. +`/geminfusion select ` selects a project on an empty table the player +is looking at within six blocks. Both commands require `geminfusion.admin`. Goldsmithing gameplay uses +the permission configured in `goldsmithing.yml`, defaulting to +`professions.goldsmith` (false by default); administrators bypass that gameplay +check. + +## Guides + +- [Goldsmithing and finishing rules](goldsmithing.md) ## Builds and releases diff --git a/projects/GemInfusion/goldsmithing.md b/projects/GemInfusion/goldsmithing.md new file mode 100644 index 0000000..e25694f --- /dev/null +++ b/projects/GemInfusion/goldsmithing.md @@ -0,0 +1,35 @@ +# Goldsmithing and finishing rules + +[GemInfusion documentation](README.md) · [All projects](../../README.md) + +Project definitions live in `goldsmithing/projects.yml` inside the plugin's data +directory. See the [bundled definitions](https://github.com/TF-Minecraft/GemInfusion/blob/main/src/main/resources/goldsmithing/projects.yml) +for the available projects and their metal recipes. + +## Gem projects + +Projects with `gem: 1` need an infused gem before work begins. Recipe accuracy, +tool work, project tier, finishing quality, and the crafter's configured attribute +influence determine how much of the gem's stat reaches the finished jewellery. +The required tool hits come from the materials actually deposited. + +## Gem-free projects + +Projects with `gem: 0`, such as the bundled Golden Key, need only the configured +metals. They return the configured item without adding a gem stat or quality. + +Finishing before the `min-hit-percent` threshold in `goldsmithing.yml` asks the +player to keep working and leaves the project intact. Once that threshold is +met, a gem-free project succeeds only with the exact recipe and required hits. +Missing hits, extra hits, or hits with unneeded tools ruin the piece and consume +the deposited metals without producing an item. + +## Feedback and material recovery + +The branding tool's status reports material counts and whether a required gem is +present. It does not reveal recipe or hit percentages while the project is in +progress. Those percentages appear after a piece is completed or ruined. + +Every finished piece records the metal materials actually used. Compatible +recycling uses those recorded inputs instead of assuming the listed recipe was +followed. This applies to both jewellery and gem-free items. diff --git a/projects/GunsAndGadgets/README.md b/projects/GunsAndGadgets/README.md index ac15bce..efeb4af 100644 --- a/projects/GunsAndGadgets/README.md +++ b/projects/GunsAndGadgets/README.md @@ -8,7 +8,8 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. See the [shared API versions](../../PLATFORM.md#shared-api-versions) for the matching provider dependency set. -- [docs/REVISION_SYSTEM.md](docs/REVISION_SYSTEM.md) +- [Commands and ammunition handling](operations.md) +- [Revision and craft provenance](docs/REVISION_SYSTEM.md) ## Builds and releases diff --git a/projects/GunsAndGadgets/docs/REVISION_SYSTEM.md b/projects/GunsAndGadgets/docs/REVISION_SYSTEM.md index 04734a2..8275be5 100644 --- a/projects/GunsAndGadgets/docs/REVISION_SYSTEM.md +++ b/projects/GunsAndGadgets/docs/REVISION_SYSTEM.md @@ -5,7 +5,8 @@ When a stamped part revision is behind the live `parts.yml` revision: - `GunStatRefresher.refresh()` rebuilds the gun from stamped part ids via `InventoryManager.rebuildFromParts()`. -- Preserves runtime identity: `gun_id`, `accuracy_salt`, loaded ammo (`bullets_loaded`, `ammo_loaded`), mid-reload state (`reload_ammo`, `reload_amount`), and `last_fire`. +- Preserves runtime identity: `gun_id`, `accuracy_salt`, loaded ammo (`bullets_loaded`, `ammo_loaded`), selected ammo (`ammo_selected`), mid-reload state (`reload_ammo`, `reload_amount`), and `last_fire`. +- Preserves the recorded crafting inputs used by recycling. - Syncs provenance revisions and re-applies `gg_craft_parts` / `gg_parts_revision`. - Does **not** refresh broken guns (`GunBrokenMarker.isBroken`) or guns with missing stamped ids (failed refresh; `GunManager` still marks broken on use). @@ -16,6 +17,9 @@ When a stamped part revision is behind the live `parts.yml` revision: - Hotbar slot change (`PlayerItemHeldEvent`) - Inventory click (clicked slot + cursor) - Item drop (`PlayerDropItemEvent`) +- Player join (carried inventory and ender chest) +- Opening world storage (block inventories, double chests, and storage entities; + plugin menus and player-owned menus are excluded) Managed = has `gun_id` PDC and readable `gg_craft_parts`. @@ -49,6 +53,10 @@ When `InventoryManager.createOutputItem(..., gui=false)` completes a real craft: Revisions come from `GunPart.getRevision()` assigned by `RevisionTracker` on load. +`CraftingManager` separately records the materials actually consumed in +`gg_craft_inputs`. Staff-given weapons record an empty set of inputs; see +[staff gun commands](../operations.md#staff-gun-commands). + ## Provenance and revisions ### RevisionTracker @@ -79,9 +87,11 @@ Crafted guns use these runtime keys plus provenance when crafted: | `stat_value_*` / `stat_index_*` | Aggregated stat totals + lore indices | | `gg_craft_parts` | Stamped part list (craft only) | | `gg_parts_revision` | Max stamped part revision | +| `gg_craft_inputs` | Actual consumed materials; empty for staff-given guns | | `gg_majority_tier` | Majority part tier (preview and craft) | | `gg_tier_lore_start` | Lore index of the `Tier II` line | | `bullets_loaded` / `ammo_loaded` | Runtime ammo (preserved on refresh) | +| `ammo_selected` | Preferred ammunition for the next reload | | `reload_ammo` / `reload_amount` | Mid-reload cancel state | | `last_fire` | Last fire timestamp | @@ -130,7 +140,10 @@ Wired on gun use and hotbar switch (next tick). ### Why not only `skin_id`? -`SkinResolver` picks a skin from weighted votes across parts. Multiple part combinations can share a skin. Recycling must use **actual part costs** from `GunPart.getCost()`, not skin heuristics. +`SkinResolver` picks a skin from weighted votes across parts. Multiple part +combinations can share a skin. Recycler reads the actual consumed materials from +`GunCraftInputs`; it does not infer costs from a skin or today's part definitions. +Guns without recorded inputs are not handled by that provider. ## Source files diff --git a/projects/GunsAndGadgets/operations.md b/projects/GunsAndGadgets/operations.md new file mode 100644 index 0000000..3e9e5f3 --- /dev/null +++ b/projects/GunsAndGadgets/operations.md @@ -0,0 +1,38 @@ +# Commands and ammunition handling + +[GunsAndGadgets documentation](README.md) · [All projects](../../README.md) + +## Staff gun commands + +`/gg give [part...]` + +Gives one completed, unloaded gun to an online player. Use part IDs from +`parts.yml` and supply exactly one enabled, compatible part for every category in +`required-parts` in `config.yml`. The normal assembly builder applies stats, +skins, and part provenance. Conflicting class requirements and invalid designs +are rejected. The recipient needs an empty inventory slot. + +No crafting materials are charged. The item explicitly records an empty set of +consumed crafting inputs, so compatible recycling cannot return unpaid materials. +Load ammunition normally. + +`give-permission` in `config.yml` defaults to `gunsandgadgets.give` (operators). +Set it to your staff permission; a blank value disables giving. This permission +is independent of `gunsandgadgets.reload`, which gates `/gg reload` and +`/gg refresh`. Reload configuration with `/gg reload`. Tab completion suggests +recipients, weapon types, and configured part IDs. + +`/gg refresh` is player-only and force-refreshes the gun in the main hand. See +[revision and craft provenance](docs/REVISION_SYSTEM.md) for automatic refresh +triggers and the data preserved when stats change. + +## Ammunition selection + +Crouch and right-click with a gun to cycle through compatible ammunition carried +in the inventory. The next reload uses the selected calibre, and the choice is +saved on that gun. Already loaded ammunition stays loaded. Without a selection, +reloads use the first compatible ammunition carried. + +The action bar confirms the selection and carried amount, or reports that no +compatible ammunition is available. Selection is blocked while the gun is +reloading. diff --git a/projects/InteractibleFurniture/README.md b/projects/InteractibleFurniture/README.md index 88ef1ae..432d126 100644 --- a/projects/InteractibleFurniture/README.md +++ b/projects/InteractibleFurniture/README.md @@ -17,8 +17,23 @@ mode. Prepare the private inputs in `libs/` with The POM uses the Paper 1.21.10 API and writes the JAR under `target/` using its declared version. Validate server behavior against the intended dependencies. -The plugin manifest requires TLibs; resolve the exact runtime integration -versions with the shared baseline. +For a local Bash session, the following prompts for the ServerAssets token and +builds only after both dependency steps succeed: + +```bash +python3 path/to/TLibs/tools/install-plugins.py --pom pom.xml --mode pinned && + (read -rsp 'ServerAssets token: ' GH_TOKEN && echo && export GH_TOKEN && + bash .github/scripts/prepare-release.sh) && + mvn clean verify +``` + +Use a separate [TLibs checkout](https://github.com/TF-Minecraft/TLibs) for +`path/to/TLibs`. The token needs Contents read access to TF-Minecraft/ServerAssets; +the prompt keeps it out of shell history and the subshell limits its lifetime. +CI supplies the token from `DEPS_TOKEN`. + +The plugin manifest requires TLibs and optionally integrates with WorldGuard. +Resolve the exact runtime integration versions with the shared baseline. ## Configuration and operations diff --git a/projects/Magic/README.md b/projects/Magic/README.md index cd84e81..04d04be 100644 --- a/projects/Magic/README.md +++ b/projects/Magic/README.md @@ -10,6 +10,21 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. - [docs/SYSTEM.md](docs/SYSTEM.md) - [docs/TEST_MATRIX.md](docs/TEST_MATRIX.md) +## Build and dependencies + +Build from `main` with Java 21. Install the TLibs, RPCharacters, and +InteractibleFurniture releases pinned in the +[POM](https://github.com/TF-Minecraft/Magic/blob/main/pom.xml) using the +[shared dependency installer](../../PIPELINES.md#build-dependencies), then run +`bash .github/scripts/prepare-release.sh` with ServerAssets access to install +the checksum-verified private inputs. Run `mvn clean verify`; the output is +`target/magic-.jar`. + +The plugin descriptor requires TLibs, ItemsAdder, RPCharacters, and +InteractibleFurniture. MMOItems, MythicLib, and MMOCore are optional integrations +in the descriptor; the corresponding gear and casting features need their +providers. The build compiles against Paper API `1.21.10-R0.1-SNAPSHOT`. + ## Builds and releases See the [shared pipeline guide](../../PIPELINES.md) for development artifacts, release tags, and dependency access. diff --git a/projects/Magic/docs/MAGE_GEAR.md b/projects/Magic/docs/MAGE_GEAR.md index a6adaef..d0bcfe1 100644 --- a/projects/Magic/docs/MAGE_GEAR.md +++ b/projects/Magic/docs/MAGE_GEAR.md @@ -34,13 +34,35 @@ They are excluded from meditation and artifact care/muffle handling. above the player's resonance band requires confirmation. 4. Hit the station's good orbs and avoid bad ones. The charge is consumed when the run begins; the captured result is written when the run ends. -5. Sneak-right-click with an empty hand to eject the weapon. Ejection is blocked +5. Right-click with an empty hand to eject the weapon. Ejection is blocked while a run is active. +To recharge an existing mage weapon, right-click an empty station while holding +it without sneaking. One weapon moves from the hand onto the station and retains +its state. Apply a charge as above, or take it back with an empty-hand click. + The station does not need to be at a shrine. Difficulty uses the charge item's tier, not its displayed aura band. A disconnect or shutdown finishes with the captured result rather than refunding the charge. +## Staff weapon commands + +`/magic weapon give [part...]` + +Gives one completed mage weapon to an online player. Use part IDs from +`gear/parts.yml` and an enabled attunement element from the loaded configuration. +Supply exactly one part for each required category, respecting the core's part +limit. Aura is a finite positive raw attunement amount and must reach a configured +tier band. The command applies attunement, finalizes sockets, and records no +material cost. It does not change the recipient's resonance. The recipient needs +an empty inventory slot. + +`give-permission` in `config.yml` defaults to `magic.weapon.give` (operators). +Set it to the chosen staff permission; a blank value disables giving. This +permission is independent of `magic.admin`. Reload configuration with +`/magic reload`. Tab completion suggests recipients, archetypes, elements, and +enabled part IDs. + ## Orb results and weapon state Capture is clamped to 0–1 from good hits divided by the tier's `good_target`, diff --git a/projects/MarketBlock/README.md b/projects/MarketBlock/README.md index f80a7f9..af6ae60 100644 --- a/projects/MarketBlock/README.md +++ b/projects/MarketBlock/README.md @@ -8,6 +8,19 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. See the [shared API versions](../../PLATFORM.md#shared-api-versions) for the matching provider dependency set. +## Build and dependencies + +From the MarketBlock source checkout, install the shared plugin versions pinned +in `pom.xml`, then verify with Java 21: + +```sh +python3 path/to/TLibs/tools/install-plugins.py --pom pom.xml --mode pinned && + mvn clean verify +``` + +Point the installer at a separate TLibs checkout. See +[build dependencies](../../PIPELINES.md#build-dependencies) for access and checksum verification. + ## Builds and releases See the [shared pipeline guide](../../PIPELINES.md) for development artifacts, release tags, and dependency access. diff --git a/projects/PermCleaner/README.md b/projects/PermCleaner/README.md index 5d9f7a7..26855d7 100644 --- a/projects/PermCleaner/README.md +++ b/projects/PermCleaner/README.md @@ -10,11 +10,11 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. Run Maven with **JDK 21** from the source checkout. The POM sets `maven.compiler.release=21` and resolves `io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT` with `provided` scope from the PaperMC Maven repository. The plugin descriptor declares `api-version: 1.21.10`. -Install these matching Java 21 TFMC artifacts into local Maven before building: `me.plugins:tlibs:2.0.0`. Use the [shared installer](../TLibs/README.md) for published dependencies, or `mvn clean install` from matching source versions. CI uses the shared dependency setup actions. +Install the pinned `me.plugins:tlibs:2.0.0` artifact into local Maven with the [shared installer](../../PIPELINES.md#build-dependencies). LuckPerms API `5.4` resolves from Maven; the running server requires both TLibs and LuckPerms. CI uses the shared dependency setup action. No private `libs/` JARs are required by this POM. -Run `mvn clean verify` to build and run the available tests; use `mvn clean install` when another plugin needs the result as a Maven dependency. The plugin JAR is written under `target/`. Gameplay and web integration checks on the Minecraft 1.21.10 server remain separate from build verification. +Run `mvn clean verify` to build and run the tests; use `mvn clean install` when another plugin needs the result as a Maven dependency. The plugin JAR is written under `target/`. Live LuckPerms cleanup and storage checks on the Minecraft 1.21.10 server remain separate from build verification. ## Builds and releases diff --git a/projects/ProvinceSystem/README.md b/projects/ProvinceSystem/README.md index 325b1da..388ade0 100644 --- a/projects/ProvinceSystem/README.md +++ b/projects/ProvinceSystem/README.md @@ -6,49 +6,20 @@ 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.md](overview.md) -- [DEPLOY.md](DEPLOY.md) -- [STAGING.md](STAGING.md) -- [UPDATE.md](UPDATE.md) -- [backend/assets/kit_skins/README.md](backend/assets/kit_skins/README.md) -- [backend/benchmarks/regen/README.md](backend/benchmarks/regen/README.md) -- [backend/render/README.md](backend/render/README.md) -- [docs/README.md](docs/README.md) -- [docs/architecture.md](docs/architecture.md) -- [docs/characters/creator.md](docs/characters/creator.md) -- [docs/cosmetics/drinks.md](docs/cosmetics/drinks.md) -- [docs/cosmetics/naming.md](docs/cosmetics/naming.md) -- [docs/cosmetics/skins.md](docs/cosmetics/skins.md) -- [docs/flows/journeys.md](docs/flows/journeys.md) -- [docs/identity/auth-security.md](docs/identity/auth-security.md) -- [docs/identity/tfmcweb.md](docs/identity/tfmcweb.md) -- [docs/integrations/armourshop.md](docs/integrations/armourshop.md) -- [docs/integrations/discord-bot.md](docs/integrations/discord-bot.md) -- [docs/integrations/precedent.md](docs/integrations/precedent.md) -- [docs/integrations/simplefactions.md](docs/integrations/simplefactions.md) -- [docs/map/generation.md](docs/map/generation.md) -- [docs/map/ledger.md](docs/map/ledger.md) -- [docs/map/overview.md](docs/map/overview.md) -- [docs/map/title-editor.md](docs/map/title-editor.md) -- [docs/map/viewer.md](docs/map/viewer.md) -- [docs/map/wars-on-map.md](docs/map/wars-on-map.md) -- [docs/ops/dev-config.md](docs/ops/dev-config.md) -- [docs/ops/local-dev.md](docs/ops/local-dev.md) -- [docs/ops/sheet-render.md](docs/ops/sheet-render.md) -- [docs/wiki-research/a-magic-knowledge.md](docs/wiki-research/a-magic-knowledge.md) -- [docs/wiki-research/a2-gems-dowsing-archaeo.md](docs/wiki-research/a2-gems-dowsing-archaeo.md) -- [docs/wiki-research/animal-husbandry.md](docs/wiki-research/animal-husbandry.md) -- [docs/wiki-research/b-crafting-economy.md](docs/wiki-research/b-crafting-economy.md) -- [docs/wiki-research/c-food-farming.md](docs/wiki-research/c-food-farming.md) -- [docs/wiki-research/class-skill-descriptions.md](docs/wiki-research/class-skill-descriptions.md) -- [docs/wiki-research/d-identity-social.md](docs/wiki-research/d-identity-social.md) -- [docs/wiki-research/e-adventure-mythic.md](docs/wiki-research/e-adventure-mythic.md) -- [docs/wiki-research/e-adventure.md](docs/wiki-research/e-adventure.md) -- [docs/wiki-research/f-rpg-stack.md](docs/wiki-research/f-rpg-stack.md) -- [docs/wiki-research/g-gadgets.md](docs/wiki-research/g-gadgets.md) -- [docs/wiki-research/herb-material-acquisition.md](docs/wiki-research/herb-material-acquisition.md) -- [docs/wiki-research/material-acquisition.md](docs/wiki-research/material-acquisition.md) -- [frontend/app/wiki/README.md](frontend/app/wiki/README.md) +- [Product and technical reference](docs/README.md) +- [Overview and setup](overview.md) +- [Previews and deploys](DEPLOY.md) +- [Staging checks](STAGING.md) and [update reference](UPDATE.md) +- [Architecture](docs/architecture.md) +- [Accounts, authentication and security](docs/identity/auth-security.md) +- [CoreProtect activity and movement](docs/integrations/coreprotect.md) +- [LuckPerms staff panel](docs/integrations/luckperms.md) +- [Rail network](docs/integrations/rail.md) +- [Patreon integration](docs/integrations/patreon.md) and [backend protocol](docs/integrations/patreon-backend.md) +- [Kit default skins](backend/assets/kit_skins/README.md) +- [Map regeneration benchmarks](backend/benchmarks/regen/README.md) +- [Review-sheet renderer](backend/render/README.md) +- [Gameplay guide authoring](frontend/app/wiki/README.md) and [research references](docs/wiki-research/) ## Builds and releases diff --git a/projects/ProvinceSystem/backend/assets/kit_skins/README.md b/projects/ProvinceSystem/backend/assets/kit_skins/README.md index 030ce32..45c75ed 100644 --- a/projects/ProvinceSystem/backend/assets/kit_skins/README.md +++ b/projects/ProvinceSystem/backend/assets/kit_skins/README.md @@ -1,6 +1,6 @@ # Kit default skins -Default editable-kit PNGs live here as `{skin_png}.png` (for example `knife_skin.png`). +Default editable-kit PNGs live in `backend/assets/kit_skins/` in the ProvinceSystem checkout as `{skin_png}.png` (for example `knife_skin.png`). **Production:** RPCharacters uploads these on creation-catalog sync via `PUT /characters/plugin/kit-skins/{name}` from `plugins/RPCharacters/assets/`. diff --git a/projects/ProvinceSystem/backend/benchmarks/regen/README.md b/projects/ProvinceSystem/backend/benchmarks/regen/README.md index fecac59..0f294ac 100644 --- a/projects/ProvinceSystem/backend/benchmarks/regen/README.md +++ b/projects/ProvinceSystem/backend/benchmarks/regen/README.md @@ -4,9 +4,10 @@ Compare `fullregen` output and timings between labelled snapshots. ## Layout +Generated artifacts live under `backend/benchmarks/regen/` in the ProvinceSystem checkout: + ``` regen/ - README.md # this file snapshots/ # gitignored — full PNG trees per label {label}/ timings/ # gitignored — JSON from _RegenTimings @@ -18,7 +19,7 @@ regen/ ## Commands ```bash -cd ProvinceSystem/backend/src +cd backend/src export PYTHONIOENCODING=utf-8 # Snapshot only — use when output/{map}/ is already fresh diff --git a/projects/ProvinceSystem/backend/render/README.md b/projects/ProvinceSystem/backend/render/README.md index 94dc6ad..5222177 100644 --- a/projects/ProvinceSystem/backend/render/README.md +++ b/projects/ProvinceSystem/backend/render/README.md @@ -6,15 +6,15 @@ Headless Chromium (Playwright) + Three.js. Python [`review_sheet.py`](https://gi ```bash cd backend/render -npm install +npm ci npx playwright install chromium ``` -`npm install` runs `prebuild`, which copies [`frontend/lib/skins`](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/frontend/lib/skins) into `src/skins/` so esbuild can resolve `three` from this package’s `node_modules`. +`npm ci` runs `postinstall`, which runs the build and its `prebuild` step, which copies [`frontend/lib/skins`](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/frontend/lib/skins) into `src/skins/` so esbuild can resolve `three` from this package’s `node_modules`. ## Production -The backend Docker image runs `npm ci`, installs Playwright Chromium, and `install-deps` during build ([`backend/Dockerfile`](https://github.com/TF-Minecraft/ProvinceSystem/blob/main/backend/Dockerfile)). The image copies the same skin helpers to `backend/render/src/skins` before `npm ci`. Rebuild the backend image after any change under `backend/render/` or those shared skin modules. +The backend Docker image runs `npm ci --ignore-scripts`, installs Playwright Chromium and its system dependencies, then copies the renderer and shared skin helpers and runs `npm run build` ([`backend/Dockerfile`](https://github.com/TF-Minecraft/ProvinceSystem/blob/main/backend/Dockerfile)). Rebuild the backend image after any change under `backend/render/` or those shared skin modules. Non-Docker hosts: run the local setup commands above on the API machine; ensure `node` is on `PATH` for the uvicorn process. diff --git a/projects/ProvinceSystem/docs/README.md b/projects/ProvinceSystem/docs/README.md index 9468456..7c0639b 100644 --- a/projects/ProvinceSystem/docs/README.md +++ b/projects/ProvinceSystem/docs/README.md @@ -1,6 +1,6 @@ # ProvinceSystem documentation -**tfminecraft.net** is the TFMC web hub: interactive political maps, donator cosmetics (skins and drinks), character creation, and identity services backed by a FastAPI backend and Next.js frontend. +**tfminecraft.net** is the TFMC web hub: interactive political maps, donator cosmetics (skins and drinks), character creation, Discord accounts, and staff tools backed by a FastAPI backend and Next.js frontend. The game integration target is Minecraft **1.21.10**; see the [shared platform baseline](../../../PLATFORM.md). ProvinceSystem itself uses the web runtimes described in [architecture.md](architecture.md). @@ -16,6 +16,10 @@ This section is the product and technical reference for **ProvinceSystem**. Tech - [characters/creator.md](characters/creator.md) - web character creator - [identity/tfmcweb.md](identity/tfmcweb.md) - Discord link, tokens, gate - [integrations/patreon.md](integrations/patreon.md) - Patreon supporter linking, entitlement and operations + - [identity/auth-security.md](identity/auth-security.md) - Discord sign-in, feature codes and staff access + - [integrations/coreprotect.md](integrations/coreprotect.md) - staff player activity and movement + - [integrations/rail.md](integrations/rail.md) - staff rail network + - [integrations/luckperms.md](integrations/luckperms.md) - staff permission policy and plugin protocol 3. [flows/journeys.md](flows/journeys.md) - end-to-end player and staff journeys 4. [ops/local-dev.md](ops/local-dev.md) - run the site locally 5. [ops/sheet-render.md](ops/sheet-render.md) - 3D review-sheet renderer (prod deploy + smoke) @@ -28,7 +32,7 @@ Schema assets: [assets/map-export-schema.json](assets/map-export-schema.json) (S | Component | Path | Role | Docs | |-----------|------|------|------| | **ProvinceSystem** | `ProvinceSystem/` | Website + FastAPI: maps, skins, drinks, characters, identity | This folder | -| **TFMCWeb** | `tfmcweb/` | MC ↔ web gate: Discord link, scoped tokens, Survival Discord freeze, warn/ban mirror, Patreon rank writer | [identity/tfmcweb.md](identity/tfmcweb.md), [integrations/patreon.md](integrations/patreon.md) | +| **TFMCWeb** | `tfmcweb/` | MC ↔ web gate: Discord link, scoped tokens, Survival Discord freeze, warn/ban mirror, Patreon ranks, staff LuckPerms bridge | [identity/tfmcweb.md](identity/tfmcweb.md), [integrations/patreon.md](integrations/patreon.md) | | **SimpleFactions** | `simplefactions/` | Map bridge: nation JSON upload, queue, regen, province lookup | [integrations/simplefactions.md](integrations/simplefactions.md) | | **ArmourShop** | `armourshop/` | Skins pack writer + apply | [integrations/armourshop.md](integrations/armourshop.md) | | **DrinkBuilder** | `drinkbuilder/` | Donator BreweryX drinks + `tfmc_drinks` IA | [cosmetics/drinks.md](cosmetics/drinks.md) | @@ -36,10 +40,10 @@ Schema assets: [assets/map-export-schema.json](assets/map-export-schema.json) (S | **ItemsAdder** | Server `plugins/ItemsAdder/` | Resource packs: `tfmc_submissions`, `tfmc_armorshop`, `tfmc_drinks` | [integrations/armourshop.md](integrations/armourshop.md) | | **tfmc_bot** | `tfmc_bot/` | Red-DiscordBot: skins/drinks review, link, ban/warn DMs, Patreon supporter roles | [integrations/discord-bot.md](integrations/discord-bot.md), [integrations/patreon.md](integrations/patreon.md) | -## Locked platform decisions +## Shared platform behavior - **Name:** TFMC = TF Minecraft. "TF" has no expansion. -- **No site logins** - skins, drinks, and characters use TFMCWeb-issued UUID-bound codes; redeem → API session (8h default; character Remember me = 30d). +- **Website sign-in** - Discord OAuth powers account and staff pages. Skins, drinks, and characters also use TFMCWeb-issued UUID-bound codes; redeem → feature API session (8h default; character Remember me = 30d). - **Shared cosmetic mint cooldown** - skin + drink share one clock on **TFMCWeb** (not ProvinceSystem). - **Discord link** - in-game `/linkdiscord` + Discord `/linkdiscord `; required before upload. - **SQLite + disk** for skins/drinks metadata and pending files on the API. @@ -48,4 +52,5 @@ Schema assets: [assets/map-export-schema.json](assets/map-export-schema.json) (S ## Ops references - Deployment and QA checklists: [STAGING.md](../STAGING.md) -- Production deploy guide: [UPDATE.md](../UPDATE.md) +- Preview and deployment workflow: [DEPLOY.md](../DEPLOY.md) +- Update reference: [UPDATE.md](../UPDATE.md) diff --git a/projects/ProvinceSystem/docs/architecture.md b/projects/ProvinceSystem/docs/architecture.md index 671b777..a607d4d 100644 --- a/projects/ProvinceSystem/docs/architecture.md +++ b/projects/ProvinceSystem/docs/architecture.md @@ -36,6 +36,8 @@ tfminecraft.net/ /skins Redeem code + upload + status /drinks Brew form + status /character Character creator, kits, wardrobe + /account Discord sign-in and connected accounts + /admin Staff player, activity, rail and permission tools /map/editor Staff map title editor (?map= required) ``` @@ -160,6 +162,7 @@ Map assets remain under `backend/src/output/{map}/…`. **Why not store PNGs in | Surface | Mechanism | |---------|-----------| +| Account and staff panel | Discord OAuth and HttpOnly browser session; website staff roles | | Map plugin regen / queue | Shared secret in path (prefer env) | | Skins/drinks/character player actions | Redeem **code** → short-lived Bearer session tied to issuer UUID | | Skins staff (Discord) | Server-side staff API key; never `NEXT_PUBLIC_*` | @@ -183,7 +186,6 @@ No website passwords. Codes are **not shareable by design**: cosmetics are grant ## Non-goals - Rewriting mapgen in another language -- User accounts / OAuth on the site - Putting Discord or Java plugins inside ProvinceSystem git (document contracts only) - Manual `tfmc_pack` CMD overrides for new submissions - Bot executing in-game bans diff --git a/projects/ProvinceSystem/docs/identity/auth-security.md b/projects/ProvinceSystem/docs/identity/auth-security.md index b461f5f..1419382 100644 --- a/projects/ProvinceSystem/docs/identity/auth-security.md +++ b/projects/ProvinceSystem/docs/identity/auth-security.md @@ -10,6 +10,27 @@ Public map data is low sensitivity. Still validate uploads, hash codes, and keep Cosmetics and identity are higher sensitivity: UUID-bound codes, opaque Bearer sessions, and server-side staff keys. +## Discord website sign-in + +The website supports Discord OAuth sign-in for account pages and the staff panel, +separately from the UUID-bound feature codes below. Enable it with +`DISCORD_AUTH_ENABLED=1`; configure `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET`, +`DISCORD_GUILD_ID` and `SITE_PUBLIC_URL`. `DISCORD_REDIRECT_URI` defaults to the +site URL plus `/api/auth/discord/callback` and must return to that site. + +[`auth_routes.py`](https://github.com/TF-Minecraft/ProvinceSystem/blob/main/backend/src/api/auth_routes.py) +starts sign-in at `/auth/discord/start`, checks the OAuth state against a browser +cookie, and sets an HttpOnly, SameSite=Lax session cookie. HTTPS uses the Secure +`__Host-tfmc_session` cookie; plain HTTP local development uses `tfmc_session`. +Cookie-authenticated writes enforce an origin check. `/account` exposes the +signed-in account and supports Minecraft linking and Patreon authorization. + +Website roles (`mod`, `admin`, `root`) control staff capabilities independently +of feature-code scopes. See [CoreProtect data](../integrations/coreprotect.md), +[rail data](../integrations/rail.md), and [LuckPerms policy](../integrations/luckperms.md) +for the individual staff panels. Configuration validation is in +[`auth/config.py`](https://github.com/TF-Minecraft/ProvinceSystem/blob/main/backend/src/auth/config.py). + ## Opaque Bearer sessions | Surface | Mechanism | diff --git a/projects/ProvinceSystem/docs/integrations/coreprotect.md b/projects/ProvinceSystem/docs/integrations/coreprotect.md new file mode 100644 index 0000000..1695f63 --- /dev/null +++ b/projects/ProvinceSystem/docs/integrations/coreprotect.md @@ -0,0 +1,143 @@ +# CoreProtect reader + +[Project index](../../README.md) · [Source module](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/backend/src/coreprotect) + +Bare filenames below refer to `backend/src/coreprotect/` in the ProvinceSystem checkout; paths beginning with `src/` are relative to `backend/`. + +The staff panel's player pages read the Minecraft server's live CoreProtect +SQLite database: who has played, their sessions, and their recent actions. +`src/auth/players.py` joins that with `discord_links`, `users` and +`character_roster`; the routes are `GET /admin/players`, +`/admin/players/{uuid}`, `/sessions` and `/activity` (`view_players`, mod and +above). Movement, `GET /admin/players/{uuid}/movement` and `/admin/movement` +for everyone, is for admins and the owner (`view_player_movement`). + +## Settings + +| Variable | Meaning | +| --- | --- | +| `COREPROTECT_DB` | Path to `database.db` inside the backend container. Unset: the directory still lists linked players and characters, and CoreProtect sections say they are unavailable. | +| `COREPROTECT_SERVER` | Short id that scopes caches and page cursors (default `main`). | +| `COREPROTECT_SERVER_LABEL` | World name shown with the data, for example `Vardera`. | +| `COREPROTECT_PING_SECONDS` | The server's `player-pings` interval (default 60; `0` if pings are off). Used to guess whether someone is online, to end crashed sessions and to tell a gap in a movement path. | +| `COREPROTECT_MAP_WORLD` | The CoreProtect world the site's map shows: the server's `level-name` (default `TFMC_Map`). Movement in other worlds is summarised, not drawn. | + +Mount the CoreProtect **directory** read-only, so SQLite can see a hot +`-journal`. Set `disable-wal: true` in CoreProtect's `config.yml` (then restart the Minecraft application through the deployment procedure): our fork uses WAL by default, and through a read-only mount +SQLite can open a WAL database only while its `-wal` and `-shm` files already +exist. CoreProtect closes its connections between batches, which removes +them, so in WAL mode the site mostly reports `cannot_open`. The option is +appended to `config.yml` once and applied on every start or reload; CoreProtect +only ever appends missing options to that file, so updates keep it. The paths are host-specific, so they belong in the host's +`docker-compose.override.yml`, not the repository's compose files: + +```yaml +services: + backend: + volumes: + - /home/amp/.ampdata/instances/TFMCMain01/Minecraft/plugins/CoreProtect:/coreprotect:ro + environment: + - COREPROTECT_DB=/coreprotect/database.db + - COREPROTECT_SERVER=main + - COREPROTECT_SERVER_LABEL=Vardera +``` + +Docker resolves the bind as root, so the AMP tree needs no permission +changes. The mount also exposes CoreProtect's `config.yml`, which holds +connection settings for database engines the server does not use. + +## Keeping CoreProtect's writer unblocked + +With `disable-wal: true` the database uses a rollback journal, so a +reader's lock holds off CoreProtect's commits. `reader.py` explains the guards: autocommit, one +reader per process, a 1.5 s budget per request (waiting for the lock +included), and every statement fully fetched before the next. The backend +runs as one uvicorn process; running more would need the reader limit +shared between them. Never open the file with `immutable=1`: on Main that +read torn pages while CoreProtect wrote. + +Candidate scans are indexed seeks by player; final-page metadata reads use +row IDs. The only whole-table reads are the small tables holding +CoreProtect names, including `co_user` and `co_username_log` (a few hundred +to a few thousand rows). Activity pages examine at most `SCAN_LIMIT` rows per +table, so a sparse filter stops early and returns a cursor to continue +(`searched_to`). + +## What is shown + +For moderators, chat is never read and commands are cut to their first word +inside SQL. Admins and the owner (`view_player_messages`) also get chat and +whole commands, each cut to 512 characters; every page that shows any is +recorded in `admin_audit` as `player.messages.view` (which rows, never the +text) before it is returned, and the page is refused if that record cannot +be written. Sign text is never selected. After selecting the final activity page, +point reads +fetch at most 64 KiB per item, entity, or identity blob for displayed rows. After closing the +reader, a bounded, read-only Java-stream/NBT decoder extracts only custom names +and MMOItems type/ID; raw metadata, lore, and other tags never enter API responses. +Decompression is capped at 256 KiB, with depth and node limits. Malformed, +oversized, or unsupported metadata retains the vanilla label. + +Item and container labels include the recorded MMOItems identity beside the +saved name (including alloys). Named vanilla items are explicitly marked renamed. +Historical mob kills show their saved name as a name, not a proven mob type. New +kills from the custom-identity CoreProtect build additionally show the MythicMobs +ID from the `coreprotect:mythic` block-metadata marker. No history rewrite is +needed. Custom-ID search and ItemsAdder block identity are not supported. + +Each activity entry also carries `target_info`: the name the panel shows +(`Short Grass`, not `short_grass`), where it came from (vanilla, MMOItems, +MythicMobs, a player) and the ids behind it. Vanilla names come from +Minecraft's own `en_us.json`, which is Mojang's to publish, not ours: the +backend image runs `scripts/build_minecraft_names.py` to fetch the pinned +client jar, check its SHA-1 and keep the block, item and entity names +(about 94 KB) in `data/minecraft_names.json`, which git ignores. Bump the +pinned version there when the server updates. Without the file, ids are +tidied instead. Block changes use block names (`wheat` is "Wheat Crops"), +items use item names ("Wheat"); only `minecraft:` ids are looked up. +Activity responses also return `server_label` and `map_world`, so the panel +calls the map's world by its name (Vardera). The panel merges consecutive +changes to the same block within a minute into one row; the API does not. + +RPCharacters chat sent by command (`/looc hi`, `/fooc`, `/me`, `/shout`…; +the aliases are `CHANNELS` in `activity.py`, from Main's `chat.yml`) is chat: +for admins it comes back as kind `chat` with its `channel` and the text after +the command, under the Chat filter and audited like chat; moderators still get +only the command word, with the `channel` named. Plain chat goes to the +player's current channel, which CoreProtect does not record, so it is worked +out (`channel_inferred`): RPCharacters keeps the channel picked with +`/channel ` in memory until the player quits, so a line's channel is +the last valid switch since their latest login, else RP. That looks back at +most 5,000 commands; further than that, or with no login recorded, the +channel is left unknown. Character-creation answers typed in chat are not +channel chat but are labelled as if they were. + +Sessions are rebuilt from login, logout and ping rows; see `sessions.py` for how crashed sessions end. + +Movement is a player's session rows in a window (up to 7 days for one +player, 24 hours for everyone): logins, logouts and a position ping once a +minute, so the path between pings is a guess. Every request is recorded as +`player.movement.view` (who, which player or everyone, the window and how +many rows, never positions) before it is returned, and refused if that +record cannot be written. Rows are read newest first in batches of 5,000, +one statement each; a window holding more than the limit keeps the newest +rows and reports `complete_from`. + +## Deployment gate + +Run these commands from `backend/` in the source checkout. + +`backend/benchmarks/coreprotect_reader.py` measures both sides: + +```sh +# Lock hold time per request against a live database (opens it immutable, benchmark only) +python3 benchmarks/coreprotect_reader.py hold --no-lock /path/to/database.db +# A disposable database at Main's size, then writer impact on it (contend writes) +python3 benchmarks/coreprotect_reader.py synth /tmp/main-synth.db +python3 benchmarks/coreprotect_reader.py contend /tmp/main-synth.db --readers 1 +``` + +Acceptance: `hold` p99 ≤ 100 ms and max ≤ 500 ms; `contend` with no writer +failures and writer transaction p99/max up by no more than 100/500 ms. + +Do not time live reads through `~/work/amp-readonly`: this FUSE mount does not pass file locks through, so SQLite can report a hot journal and fail with `attempt to write a readonly database`. Use the backend container's direct read-only bind mount. Record timings and source revisions in the deployment PR or external test lab. diff --git a/projects/ProvinceSystem/docs/integrations/luckperms.md b/projects/ProvinceSystem/docs/integrations/luckperms.md new file mode 100644 index 0000000..6f703f1 --- /dev/null +++ b/projects/ProvinceSystem/docs/integrations/luckperms.md @@ -0,0 +1,137 @@ +# LuckPerms in the staff panel + +[Project index](../../README.md) · [Source module](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/backend/src/luckperms) + +Bare filenames below refer to `backend/src/luckperms/` in the ProvinceSystem checkout; paths beginning with `src/` are relative to `backend/`. + +The website shows and edits LuckPerms without touching its database. One +server's TFMCWeb (Main's) is the bridge: it publishes a snapshot of LuckPerms +to the site and applies changes that staff queue on the site, through the +LuckPerms API. Dev and Main share one LuckPerms MariaDB, so exactly one server +may apply changes. Another server may publish to its own site (Dev publishes to +dev.tfminecraft.net) without applying, which makes that site read-only. + +## Rights + +| Who | May | +| --- | --- | +| mod | view everything: groups, tracks, every player's groups and permissions, change history | +| admin | add or remove the groups in `policy.yaml` `admin_groups` on players, promote/demote players along tracks between those groups, set or unset player permissions matching `admin_permissions` but not `root_only_permissions` (such as `armourshop.admin`) | +| root | everything, including staff groups, other permissions, meta (prefix/suffix/weight) on players, group definitions and tracks | + +A listed group counts as an admin group only while everything it inherits, in +any context, is listed too, so a root edit to a group definition cannot hand +admins a staff group by inheritance. Unknown permissions, wildcards and regex +nodes are root-only. + +Admins may not change anything on their own Minecraft account, on a player +whose linked website account is admin or root, or on a player who holds or +inherits a root-only group (in-game staff, linked or not). Rights are checked +when a change is queued and again when the bridge collects it, so a demotion +in between cancels it (`no_longer_allowed`). Every change carries a +reason and is written to `admin_audit`, and LuckPerms logs it (`/lp log`) with +the source `web:`. + +## Plugin protocol + +All routes need the primary `X-Plugin-Key`; secondary keys are refused. + +### Snapshot + +`PUT /luckperms/plugin/snapshot` (JSON, at most 16 MiB): + +```json +{ + "server": "main", + "generated_at": 1791321779, + "revision": 1791321779123, + "hash": "", + "groups": [{"name": "staff", "display_name": null, "weight": 200, "nodes": [NODE]}], + "tracks": [{"name": "staff", "groups": ["staff_player", "staff_inactive", "staff"]}], + "users": [{"uuid": "…", "name": "drefvelin", "nodes": [NODE]}] +} +``` + +`NODE` is `{"key": "group.staff", "value": true, "contexts": {"server": ["main"]}, "expiry": 0}`; +`expiry` is unix seconds, 0 for permanent; `contexts` maps each key to its values (empty object for global). +`users` holds every user with at least one stored node (`UserManager.searchAll` with an empty key prefix). +The site replaces its whole mirror in one transaction and answers `{"ok": true}`. + +`revision` orders everything the bridge reports: it strictly increases across snapshots and +results, also across restarts (the bridge uses `max(last + 1, current time in ms)`). The site +ignores a snapshot or result state whose revision is not above the newest it has stored +(answering `{"ok": true, "stale": true}`), so a request delayed past a newer one cannot undo it. + +`POST /luckperms/plugin/snapshot/unchanged` `{"hash": "…", "revision": …}` answers `{"ok": true}` and +marks the mirror current when the hash matches the stored one, else `{"ok": false, "need_full": true}`. + +The bridge builds a snapshot every `snapshot-seconds` (default 30) and right after applying changes, +on the same single worker thread that applies changes, so a snapshot never predates an applied change. + +### Changes + +`GET /luckperms/plugin/changes` returns up to 20 changes in id order and records the poll time +(the site shows the bridge as connected while polls are recent): + +```json +{"changes": [{ + "id": 12, + "target_type": "user", // user | group | track + "target": "5b0c…-uuid", // user UUID, group name or track name + "target_name": "drefvelin", + "actor_name": "web:w.o.n", // LuckPerms action source name + "actor_uuid": null, // the actor's linked Minecraft UUID, if any + "description": "parent add staff", // LuckPerms action log text + "ops": [OP], + "guard": {"admin_groups": ["default", "commoner"]} // admin changes only +}]} +``` + +A group or track change is handed out on its own, and nothing else is handed out until its +result arrives, so later changes are checked against the definitions it leaves. + +`guard` is on changes an admin made. The mirror can lag in-game edits, so the bridge checks live +LuckPerms before mutating: the player must hold no group (any context) outside `admin_groups` +(else `target_is_staff`), and every group an `add_node` grants must inherit only groups in +`admin_groups` (else `root_only`). + +Ops, applied in order to one holder and saved once: + +| target | op | fields | precondition | +| --- | --- | --- | --- | +| user, group | `add_node` | `node` | the exact node (key, value, contexts, expiry) is absent | +| user, group | `remove_node` | `node` | the exact node is present | +| group | `create_group` | | the group does not exist | +| group | `delete_group` | | the group exists | +| track | `create_track` | | the track does not exist | +| track | `delete_track` | | the track exists | +| track | `set_groups` | `groups` (ordered) | the track exists and every group exists | + +The bridge checks every precondition in op order against a staged copy of the live LuckPerms data +(so create-then-add and remove-then-add work) before mutating anything; if one fails, nothing is +mutated or saved and the result is `{"ok": false, "error": ""}` with codes +`node_exists`, `node_missing`, `group_exists`, `group_missing`, `track_exists`, `track_missing`, +`bad_op`, `bad_target`, `save_failed`. A `group.` node whose group does not exist is `group_missing`. +If saving fails after mutating a loaded holder, the bridge reloads that holder from storage so the +server does not keep an unsaved change in memory. After saving a user it pushes a user update; after +a group or track change it pushes a full update. It submits one LuckPerms action per change +(source UUID: the actor's linked Minecraft UUID, else the nil UUID). + +`POST /luckperms/plugin/changes/results`: + +```json +{"results": [{"id": 12, "ok": true, "error": null, "revision": 1791321780456, "state": STATE}]} +``` + +`STATE` (optional) is the target after the change: a user `{"uuid", "name", "nodes"}`, a group +`{"name", "display_name", "weight", "nodes"}` or `null` (deleted, or tracks). The site updates its +mirror for that target at once. The bridge keeps results it could not post and posts them again +on later ticks until the site answers `ok`. + +Site side, a change is `pending` until fetched, then `sent`, and the site never offers it again: a +change is applied at most once. A sent change with no result after 300 s becomes `unknown` (staff +are told to check the player); a late result still settles it. A change not fetched within +10 minutes `expired`. Both timeouts are settled on staff reads too, so they happen even when the +bridge is gone. New changes are refused with `bridge_offline` unless the bridge polled in the +last 60 s, and always on a site with `LUCKPERMS_READ_ONLY=1` (dev.tfminecraft.net, whose server +shares LuckPerms with Main). diff --git a/projects/ProvinceSystem/docs/integrations/patreon-backend.md b/projects/ProvinceSystem/docs/integrations/patreon-backend.md new file mode 100644 index 0000000..e82ea15 --- /dev/null +++ b/projects/ProvinceSystem/docs/integrations/patreon-backend.md @@ -0,0 +1,147 @@ +# Patreon backend + +[Project index](../../README.md) · [Source module](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/backend/src/patreon) + +Bare filenames below refer to `backend/src/patreon/` in the ProvinceSystem checkout; paths beginning with `src/` are relative to `backend/`. + +The tier mapping is `tiers.yaml`. Tables are in `src/skins/schema.sql`, +created by the existing `src.skins.db.migrate()` in the app lifespan. They +share `province.db` with `discord_links`. Patron linking lives in `linking.py`: +hashed single-use OAuth state and confirmation tokens, `link/start`, +`oauth/callback`, `link/pending`, `link/confirm`, `link/cancel`, and `webhook`. + +## Explicit OAuth link confirmation + +`POST /patreon/link/start` keeps the existing staff (Discord), plugin +(Minecraft), and profile-session entry points. It stores the target kind and +the supplied `discord_username` or `minecraft_name` with the subject and +hashed state. Profile sessions always use the session UUID and ignore supplied +names: resolve the Minecraft name from `discord_links`, then the most recent +`discord_link_codes` record. If no name is available, display the UUID (or the +Discord id for a nameless Discord request) so the target remains explicit. + +Consent alone does not create a link or grant perks. The callback consumes +the state, exchanges the code, reads the Patreon id and full name, and discards +the patron tokens. It stores the identity and target in `patreon_pending_links` +under a fresh random token, stored only as a SHA-256 hash, valid for 10 minutes. +It redirects to `{PATREON_PUBLIC_SITE_URL}/patreon/linked#confirm=`. +The fragment is never sent in an HTTP request or Referer header. Failure +redirects retain `?status=&tier=` and never contain identities. + +All three public confirmation routes accept `{"token": ".."}` in a POST +body; the token is the credential, with no browser session required: + +- `/patreon/link/pending` returns only `target_kind` (`discord` or `minecraft`), + `target_name`, and `patreon_name`, without consuming the token. +- `/patreon/link/confirm` consumes it before attempting the link, then creates + the OAuth link and recomputes. An unknown member triggers the existing + creator sync before returning an entitlement result. The response is always + `{"status": "ok|not_a_member|already_linked|relink_cooldown|expired|error", + "tier": ""}`. Consumed tokens stay consumed on failures. +- `/patreon/link/cancel` consumes it and creates nothing, returning + `{"status": "ok"}`. + +Unknown, used or expired tokens return `expired` (`{"status": "expired"}` +for pending/cancel, with an empty `tier` for confirm). Responses and callback +redirects disable caching. Logs contain fixed messages/status codes only. +OAuth states without the target kind or display name are treated as expired: +callers must start again so the confirmation page can identify the link target. + +The website removes the fragment from the address bar, loads the pending +names as text, and asks the person to confirm that the target is theirs. +Cancel displays the existing “Link not completed” result. This addresses login +CSRF across all three entry points: someone completing an attacker's consent +URL sees the attacker's account before any link is created. A browser-session +binding cannot establish ownership for links started in Discord or Minecraft. +`POST /patreon/link/unlink` also accepts an absent body for profile sessions; +staff/plugin requests still require their subject through the existing auth +helper. + +## Interface for OAuth and webhook routes + +Import from `src.patreon.service`: + +- `create_or_update_link(patreon_user_id, discord_user_id=None, + player_uuid=None, method="oauth", *, force=False, config=None, conn=None, + now=None) -> str`. Returns `ok`, `already_linked`, `relink_cooldown`, or + `invalid_subject`. Invalid UUID or method raises `ServiceError` with a stable + code. Staff force bypasses cooldown but never subject uniqueness. This + function stores the link but does not recompute; call `recompute_link` next. +- `recompute_link(patreon_user_id, *, config=None, conn=None, + link_success=False) -> dict`. Returns the public status shape, computes + current entitlement, and enqueues changes (or logs them in shadow mode). + Pass `link_success=True` after OAuth. It deduplicates this notification by + the link event. Recomputing also clears previously owned subjects on relink. +- `refresh_member(member_id, *, config=None, client=None) -> dict`. Fetches + one complete API member and stores/recomputes atomically. Returns + `{"ok": true, "members": 1, "brake_held": bool}`, or `{"ok": false, + "detail": "patreon_sync_busy"|"patreon_sync_failed"|"patreon_disabled"}`. + It is synchronous: dispatch it with `asyncio.to_thread` or a background + worker. Webhook handling should retain/retry busy or failed refreshes. +- `sync_now(*, config=None, client=None)` has the same summary contract for + a complete paginated campaign snapshot. Never call the real API in tests; + inject `PatreonClient(..., http=httpx.Client(transport=MockTransport(...)))`. + +Import `PatreonClient` from `src.patreon.client`: + +- `exchange_authorization_code(code, redirect_uri=None) -> dict` returns the + patron token response. Nothing persists this response. Discard it after use. +- `identity(access_token) -> dict` returns the JSON:API `data` resource, with + `id` and `attributes`; it does not persist the patron token. +- `member(member_id)`, `members()`, `creator_get(path, **kwargs)`, + `refresh_tokens()` use authoritative persisted creator credentials. +- `close()` closes an internally owned HTTP client. Errors are `PatreonError` + with fixed, non-sensitive codes. Never log tokens or API response bodies. + +`conn=` on link and recompute functions participates in the caller's +transaction without committing. Confirmation uses +`with src.skins.db.connect() as conn`, `BEGIN IMMEDIATE`, create the link and, +if successful, recompute with that connection. `Config.from_env()` supplies +runtime configuration. Router helpers `require_enabled`, `caller_subject`, +`require_staff`, `require_plugin` are in `src.api.patreon_routes`. Register +future routes on `patreon_router` or on a separate router under `/patreon`. + +## State and delivery behavior + +Unlink retains an inactive link tombstone for cooldown enforcement. Auto-link +runs only when no link row exists for that Patreon user, so an explicit unlink +stays disconnected until the patron links again or staff links them. Missing +identity halves are resolved dynamically from `discord_links`; they cannot +claim a subject explicitly owned by another Patreon link. + +Desired state and applied state are separate. Only an acknowledgement (or an +explicit staff legacy import grant) records ownership of a tier. Outbox rows +not yet fetched may coalesce into a new id. Fetched rows remain immutable; +their acknowledgement records exactly their contents, then queues a fresh +change toward the latest desired tier. Cancelled/other-target ids cannot grant +ownership. Appliers should deduplicate DMs by change id before acknowledging: +HTTP outbox delivery is at least once, so an ack lost after sending a DM can +otherwise replay it. Backend recomputes do not create a second DM for the +same transition. `PATREON_SUPPRESS_DMS=1` still stores that DM on the change +row (so an acknowledgement marks it delivered) but the Discord outbox sends +`dm: null`, including after the flag is turned off, via `dm_suppressed`. + +The persistent removal brake affects both outboxes and rosters. While held, +new removals are not delivered and rosters retain the highest currently owned +tier. Additions continue. Staff release replans against current desired state, +not a stale saved snapshot. Already dispatched changes cannot be recalled. +Release approvals are persisted by desired-state generation, so another sync +cannot immediately re-hold the same reductions while acknowledgements are +still pending. Distinct subsequent transitions remain subject to the brake. + +Shadow mode computes member history, links and desired state, but never +queues or delivers outbox changes. Rosters expose the recorded applied tier +in shadow mode, avoiding reconciliation applying shadow decisions. Switching +apply on causes the next sync to enqueue outstanding differences. + +The lifespan loop keeps a database leader lease across polling intervals; +individual snapshots and member refreshes share a second lease. HTTP calls +renew these leases, and ownership is checked inside the snapshot transaction. +Other workers wait to take leadership after expiry. Shutdown waits for the +current atomic sync and releases leadership. Creator token refresh has its +own lease to protect rotating tokens. + +Emails are returned only by lookup, unlinked and import. Alerts and logging +use fixed messages and tier keys, with no emails, names, tokens or API bodies. +Only the primary `PLUGIN_KEY` may use the rank outbox/roster; secondary server +keys are refused so one server owns LuckPerms application. diff --git a/projects/ProvinceSystem/docs/integrations/patreon.md b/projects/ProvinceSystem/docs/integrations/patreon.md index 11f6458..3b60957 100644 --- a/projects/ProvinceSystem/docs/integrations/patreon.md +++ b/projects/ProvinceSystem/docs/integrations/patreon.md @@ -148,4 +148,4 @@ Update Patreon app client credentials and webhook secret in the backend secret e - Do not change legacy/VIP groups or any role/group outside the mapped supporter tiers. - Use shadow mode first when introducing a new mapping or deployment. Turn on backend apply only after the computed changes have been reviewed. -See the [TFMCWeb identity guide](../identity/tfmcweb.md), [Discord bot integration](discord-bot.md), and the backend handoff in `ProvinceSystem/backend/src/patreon/README.md` for implementation details. +See the [TFMCWeb identity guide](../identity/tfmcweb.md), [Discord bot integration](discord-bot.md), and the [backend protocol reference](patreon-backend.md) for implementation details. diff --git a/projects/ProvinceSystem/docs/integrations/rail.md b/projects/ProvinceSystem/docs/integrations/rail.md new file mode 100644 index 0000000..b382cac --- /dev/null +++ b/projects/ProvinceSystem/docs/integrations/rail.md @@ -0,0 +1,44 @@ +# Rail network + +[Project index](../../README.md) · [Source module](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/backend/src/rail) + +Bare filenames below refer to `backend/src/rail/` in the ProvinceSystem checkout; paths beginning with `src/` are relative to `backend/`. + +The staff panel's Rail tab draws the Minecraft server's rail network on the +live map: every VehicleFramework track, its junctions and ends, broken or +damaged stretches, and the stops along it. `GET /admin/rail?map=` serves +it to admins and the owner (`view_rail`). Track positions are not personal +data, so views are not audited. + +VehicleFramework saves one JSON file per track in +`plugins/VehicleFramework/data/tracks//`, with switches in +`junctions/` beside them. A track holds a sample about every block and one +segment per pair of samples; a segment can be broken or damaged. The backend +reads the files on request and keeps the result until any file, the map's +`map_markers.json` or its `province_id_runs.bin.gz` changes. A file caught +mid-save is skipped and counted, so the page can say so. + +- **Stops** are the settlements whose provinces a track crosses. Each sits + where the track comes closest to the settlement inside its provinces. +- **Lines** are tracks joined by junctions. A line is named after its first and + last stop along its longest track; one with no stops is an "Unnamed line". +- Tracks are simplified (Douglas-Peucker, 0.35 blocks) before they are sent. + +## Settings + +| Variable | Meaning | +| --- | --- | +| `RAIL_TRACKS_DIR` | VehicleFramework's `data/tracks` folder inside the backend container. Unset: the tab says the site is not set up to read the network. | +| `RAIL_WORLD` | The world folder to read: the server's `level-name`. Defaults to `COREPROTECT_MAP_WORLD`, then `TFMC_Map`. | + +Mount the folder read-only in the host's `docker-compose.override.yml`, like +CoreProtect's: + +```yaml +services: + backend: + volumes: + - /home/amp/.ampdata/instances/TFMCMain01/Minecraft/plugins/VehicleFramework/data/tracks:/vehicleframework-tracks:ro + environment: + - RAIL_TRACKS_DIR=/vehicleframework-tracks +``` diff --git a/projects/ProvinceSystem/frontend/app/wiki/README.md b/projects/ProvinceSystem/frontend/app/wiki/README.md index b4e4e67..8ed5a9c 100644 --- a/projects/ProvinceSystem/frontend/app/wiki/README.md +++ b/projects/ProvinceSystem/frontend/app/wiki/README.md @@ -10,7 +10,7 @@ Everything under `/wiki` is a public player manual. Write for someone joining th - Include every ordinary player command supported by the evidence. Omit commands that require an operator or an unconfirmed external grant. - Keep real gameplay cautions, costs, cooldowns, item consumption, death effects, and irreversible choices. - Do not use em dash characters or em dash HTML or JavaScript escapes. Use a comma, colon, parentheses, or a full stop. Use `N/A` or `None` for an empty table value. -- Display research dates through `WikiPage` as `Last modified`. The compatibility prop remains named `lastVerified`. +- `WikiPage` displays an `Updated` date. Use the optional `lastModified` prop for a page-specific revision; otherwise the maintained `WIKI_LAST_MODIFIED` date is used. - Cards and callouts use a uniform neutral border. Do not add a coloured left border. - Use `SeeAlso` only with registered, existing player pages. @@ -38,7 +38,7 @@ import { Callout, DataTable, SeeAlso, StatGrid, WikiPage, WikiSectionHeading } f export default function BeekeepingPage() { return ( - + Getting started {/* Concrete steps and reference data */} @@ -66,7 +66,7 @@ export const beekeepingCommands: WikiCommandSet = { }; ``` -Register `commands: []` when the feature has no player command and interaction happens through blocks, items, or menus. Never invent a command. +Set the `WikiCommandSet.commands` array to `[]` when the feature has no player command and interaction happens through blocks, items, or menus. Never invent a command. ## Recipes and assets @@ -86,4 +86,4 @@ Source artwork that the site does not serve stays outside `public/`. The ammunit ## Validation -Run the existing route, registry, shared-component, and relevant feature tests. Run `npx tsc --noEmit --pretty false` and the guarded production build. Check every `SeeAlso` target, every top-level wiki route, dynamic detail routes, referenced assets, visible command filtering, and a rendered-output scan for prohibited player-facing copy. Verify the desktop sidebar has its own bounded vertical scroll and that smaller layouts retain normal page flow. +From `frontend/`, run `npm test` and `npx tsc --noEmit --pretty false`, then the guarded production build with `PS_PRODUCTION=1 NEXT_PUBLIC_API_URL=http://127.0.0.1:8000 npm run build`. Check every `SeeAlso` target, every top-level wiki route, dynamic detail routes, referenced assets, visible command filtering, and a rendered-output scan for prohibited player-facing copy. Verify the desktop sidebar has its own bounded vertical scroll and that smaller layouts retain normal page flow. diff --git a/projects/RPCharacters/README.md b/projects/RPCharacters/README.md index 381b1d6..b1d7aa9 100644 --- a/projects/RPCharacters/README.md +++ b/projects/RPCharacters/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 controls, classes and combat](docs/character-controls.md) - [Graves](docs/graves-system/SYSTEM.md) - [Injuries, healing and prosthetics](docs/injuries-system/README.md) - [Persona, identity and chat](docs/persona-system/SYSTEM.md) diff --git a/projects/RPCharacters/docs/character-controls.md b/projects/RPCharacters/docs/character-controls.md new file mode 100644 index 0000000..228543e --- /dev/null +++ b/projects/RPCharacters/docs/character-controls.md @@ -0,0 +1,53 @@ +# Character controls and combat + +[Project index](../README.md) + +Commands and configuration below describe the bundled defaults on `main`. Server configuration can override them. + +## Mail recipient visibility + +`/rpcharacter mail` toggles whether your active character appears in BirdMessenger’s recipient list; `/rpcharacter mail off` hides it and `/rpcharacter mail on` restores it. Characters are listed by default, and the setting persists across logouts and restarts. Already-sent mail still arrives. + +## Character focus + +Focus is a shared, regenerating per-character resource used by Research and Magic. Right-clicking a Focus Potion restores 50 focus (set in `focus.yml` under `restore_items`); it is not used up while focus is full. + +## Class picks + +`/class`, `/rpcharacter class` and the Class button in Character Info open the base-class window. `/subclass`, `/rpcharacter subclass` and the Subclasses button open a separate window showing only the active character's class family, in place of MMOCore class points. + +A character's first class and first subclass are free. Other picks are priced like the class creation stage: free during its lock-time (5 days), then the `paid-changes` class rule (100, 1000, 3000 denars). `class-selection.change-cost` is used only when there is no class rule. `infinite-points: true` makes every pick free, for the tutorial server. A class picked above a skill-slot level still gets the slots its exp table unlocks. + +Staff with `class-selection.reset-permission` (`rpchar.class.reset`) can run `/rpcharacter admin resetclasses confirm` to give every character a fresh class window: class and subclass changes are free for the lock-time again, paid prices start over and the first subclass is free again. Online characters start now, offline ones when they next join. The reset count is kept in `data/class-resets.yml`. + +The class level is shared by every character and kept in the player file (`account-class-level`). A class capped below it, such as a base class, shows its cap without lowering the level other classes and characters get; accounts saved before this start from the level in MMOCore's own save. + +## Nonlethal knockouts + +GSit holds downed players in a crawl pose for the knockout duration, alongside freeze and blindness. The pose uses GSit's API, bypasses command restrictions, respects other plugins' crawl vetoes, and releases only knockout-created crawls on recovery. GSit is optional for the rest of RPCharacters; without it, knockouts retain freeze and blindness only. + +While downed, a player can only be finished by another player in lethal mode, a mob, `/kill`, the void or the world border: falls, drowning, fire and other damage with no attacker leave them at half a heart, and the fall distance built up by the freeze is cleared when they get up. Knockouts never apply to players in a started SimpleFactions battle, so the battle records the death and routes the respawn. + +## PvP strikes + +After `/pvp start`, the fight lasts 15 minutes unless the player who started it runs `/pvp end`. When it ends, players who have not died see a title that a new RP interaction is needed. Whoever kills or knocks someone out chooses to spare them or give a strike; the third strike kills the character, though a killer can wound or maim instead of killing. + +The same player must wait 24 hours by default before striking a character again (`strikes.same-target-cooldown-hours` in `pvp.yml`; 0 disables the wait), so at that default they cannot land all three strikes in a day. Lockpicking, robbing, pickpocketing and looting locked graves start a timed evil RP session, during which any strike kills and a death leaves an unlocked grave. + +## Codex rarity + +`%rpcharacters_codex_percent_:%` is the nearest whole percent of Codex accounts that have unlocked that entry. `%rpcharacters_codex_holders_:%` is the count. An unknown category or entry is blank. The figure refreshes about once a minute from Codex's saved files, using live Codex data for players who are online. + +## Armour takes time + +Putting armour on takes a while (by default 1 second for a helmet, 20 for a chestplate, 12 for leggings and 8 for boots). Pieces go on one at a time and the player is slowed until they're done. Taking or dealing damage, sprinting, dying or logging out stops it. + +`/pvp start` takes off any chestplate, leggings or boots that someone in range put on in the last 3 minutes, and nobody in the fight can put armour on until it ends unless they die. Once recent armour is off, it notes who is still in a chestplate, leggings and boots: they keep their helmet and can take it off and put it back on for the rest of the fight. Anyone else also loses any helmet, mask or head they put on in the last 3 minutes, and can't put one on until the fight ends. This is decided when the fight is called and doesn't change during it. The times are set under `armour` in `pvp.yml`. Masks, heads and elytras go on at once. + +## Source references + +- [Character commands](https://github.com/TF-Minecraft/RPCharacters/blob/main/src/main/java/net/tfminecraft/rpcharacters/command/CharCommand.java) +- [Class selection and paid changes](https://github.com/TF-Minecraft/RPCharacters/blob/main/src/main/resources/config.yml) and [creation stages](https://github.com/TF-Minecraft/RPCharacters/blob/main/src/main/resources/stages.yml) +- [PvP and armour configuration](https://github.com/TF-Minecraft/RPCharacters/blob/main/src/main/resources/pvp.yml) +- [Focus configuration](https://github.com/TF-Minecraft/RPCharacters/blob/main/src/main/resources/focus.yml) +- [Codex population service](https://github.com/TF-Minecraft/RPCharacters/blob/main/src/main/java/net/tfminecraft/rpcharacters/placeholder/CodexPopulationService.java) diff --git a/projects/RPCharacters/docs/injuries-system/README.md b/projects/RPCharacters/docs/injuries-system/README.md index 2d74a85..dd8edaa 100644 --- a/projects/RPCharacters/docs/injuries-system/README.md +++ b/projects/RPCharacters/docs/injuries-system/README.md @@ -1,6 +1,6 @@ # Injuries, healing and prosthetics -Healing injuries carry remaining duration; permanent injuries can have configured +Healing injuries carry a real-time expiry that continues while offline or inactive; permanent injuries can have configured prosthetic replacements. State belongs to the character. Death-zone handling rolls permadeath first, then converts healing injuries or selects a new injury. Prosthetics are excluded from the injury risk count. @@ -13,7 +13,7 @@ The maintained references describe the current source on `main`: | [Trait persistence](trait-state-persistence.md) | Duration, fuel and saved identifiers | | [Trait effects](trait-runtime-effects.md) | Scaling and powered/depowered variants | | [Permadeath](permadeath-flow.md) | Death order, progression and risk | -| [Healing](healing-tick.md) | Active-character duration and completion | +| [Healing](healing-tick.md) | Real-time expiry and active-character completion | | [Surgery](remedies.md) | Healing injuries are treated by Surgery | | [Prosthetic installation](prosthetics-install.md) | Install, swap and confirmation | | [Prosthetic fuel](prosthetic-fuel.md) | Burn, refuel and effects | diff --git a/projects/RPCharacters/docs/injuries-system/content-and-migration.md b/projects/RPCharacters/docs/injuries-system/content-and-migration.md index d4c7437..197494e 100644 --- a/projects/RPCharacters/docs/injuries-system/content-and-migration.md +++ b/projects/RPCharacters/docs/injuries-system/content-and-migration.md @@ -35,7 +35,7 @@ Creator stages use `one_handed`, `one_legged`, and `blind` (filter: permanent-on ## `zones.yml` tutorial - Death in zone may permakill or add a healing injury -- Healing injuries recover while active online, or become permanent on another zone death +- Healing injuries recover in real time, including offline and inactive characters, or become permanent on another zone death - Surgery treats healing injuries only - Prosthetics replace some permanent injuries - More injuries raise permakill chance diff --git a/projects/RPCharacters/docs/injuries-system/healing-tick.md b/projects/RPCharacters/docs/injuries-system/healing-tick.md index cfcb388..6cb3355 100644 --- a/projects/RPCharacters/docs/injuries-system/healing-tick.md +++ b/projects/RPCharacters/docs/injuries-system/healing-tick.md @@ -1,40 +1,26 @@ # Healing tick -## Purpose - -Decrement healing duration while player is online with the injury on the active character. - -## `InjuryHealingService` - -- Runs at the interval configured in `injuries.yml` -- For each online player with active character: - - For each owned trait with `duration` in YAML: - - Subtract elapsed ms from `duration-remaining-ms` - - If `<= 0`: remove trait, lost message, update integrator, clear state - - Else: `character.update()` if progress crossed int threshold (optional optimization: only on minute boundaries) - -## Tick rules - -- **Only** when character is active (`character.isActive()`) -- **Only** while player online -- Inactive characters or offline: duration frozen - -## Configuration (`injuries.yml`) - -```yaml -healing-tick-interval: 1m -``` - -Default 1 minute if omitted. - -## Acceptance - -- [ ] Active character heals over time; inactive does not -- [ ] Fully healed injury removed with lost message -- [ ] Attribute penalties decrease as duration decreases - -## Implementation - -- `healing-tick-interval: 1m` in `injuries.yml`, read by `InjuryPoolLoader` -- `InjuryHealingService` repeating task: decrements `duration-remaining-ms`, removes healed traits with lost message, refreshes integrator on active characters -- Started from `PlayerManager.start()` +Healing duration elapses in real time, including while a player is offline or a +character is inactive. The saved expiry controls the remaining time; the task +removes expired injuries and refreshes effects on active characters. + +## Runtime behavior + +[`InjuryHealingService`](https://github.com/TF-Minecraft/RPCharacters/blob/main/src/main/java/net/tfminecraft/rpcharacters/injuries/InjuryHealingService.java) +runs at `healing-tick-interval` in `injuries.yml` (default `1m`). It visits online +players with an active character, skipping dead players and pending permadeath +respawns. Traits missing duration state receive their full configured duration. +Expired traits are removed through `TraitChangeService`, with the lost-trait +message; remaining traits have their scaled effects refreshed. + +The task does not subtract a fixed interval from saved time. +[`TraitInstanceState`](https://github.com/TF-Minecraft/RPCharacters/blob/main/src/main/java/net/tfminecraft/rpcharacters/objects/TraitInstanceState.java) +computes remaining duration from `expires-at-ms`. Inactive characters lose expired +traits when loaded from storage. See [trait persistence](trait-state-persistence.md). + +## Verification + +- Check that remaining time decreases for active, inactive and offline characters. +- Reload an inactive character after expiry and confirm the expired trait is absent. +- Confirm active-character completion removes the injury, updates effects and sends the lost message. +- Confirm penalties fade with the remaining fraction of the configured duration. diff --git a/projects/RPCharacters/docs/injuries-system/trait-state-persistence.md b/projects/RPCharacters/docs/injuries-system/trait-state-persistence.md index fb1957c..6708b29 100644 --- a/projects/RPCharacters/docs/injuries-system/trait-state-persistence.md +++ b/projects/RPCharacters/docs/injuries-system/trait-state-persistence.md @@ -31,7 +31,7 @@ Map traitState; // keyed by trait id (lowercase) | Field | Type | Used by | |-------|------|---------| -| `durationRemainingMs` | long | Healing injuries | +| `expiresAtMs` | long | Healing injury expiry in epoch milliseconds | | `fuel` | double | Fueled prosthetics | ## Database (`Database.java`) @@ -40,32 +40,35 @@ Map traitState; // keyed by trait id (lowercase) ```json "trait-state": { - "broken_arm": { "duration-remaining-ms": 172800000 }, + "broken_arm": { "expires-at-ms": 1791633600000, "duration-remaining-ms": 172800000 }, "arcane_prosthetic_arm": { "fuel": 42.5 } } ``` +`duration-remaining-ms` is also written as a compatibility snapshot; `expires-at-ms` is authoritative for current readers. + **Load:** 1. Parse `trait-state` or default `{}` -2. For each trait on character, apply defaults (see below) -3. No trait id migration in Database +2. Prefer `expires-at-ms`; legacy `duration-remaining-ms` starts a new expiry from load time. +3. Apply missing-state defaults, and remove expired duration traits on inactive characters. +4. No trait id migration in Database. ## Defaults on load | Trait type | Missing state | |------------|---------------| -| Healing (`duration` in YAML) | `duration-remaining-ms` = full duration from trait def | +| Healing (`duration` in YAML) | Expiry = current time + full duration from trait definition | | Fueled prosthetic | `fuel` = `fuel-capacity` from trait def | | Other | no state entry required | ## API on `RPCharacter` -- `getTraitState(traitId)`, `setDurationRemainingMs`, `setFuel`, `removeTraitState` +- `getTraitState(traitId)`, `setDurationRemainingMs`, `setDurationExpiresAtMs`, `setFuel`, `removeTraitState` - `initializeTraitState` on `addTrait`, clear on `removeTrait` - `ensureTraitStateDefaults()` after load ## Acceptance - [ ] Old characters without `trait-state` load cleanly -- [ ] Round trip save/load preserves duration and fuel +- [ ] Round trip save/load preserves expiry and fuel; remaining time continues to decrease - [ ] Saved `one_handed` / `one_legged` ids resolve without JSON rewrite diff --git a/projects/RPCharacters/docs/injuries-system/verify-and-deploy.md b/projects/RPCharacters/docs/injuries-system/verify-and-deploy.md index ae1412d..7699234 100644 --- a/projects/RPCharacters/docs/injuries-system/verify-and-deploy.md +++ b/projects/RPCharacters/docs/injuries-system/verify-and-deploy.md @@ -21,7 +21,7 @@ Manual test matrix and rollback. | # | Action | Expected | |---|--------|----------| | 6 | Active online 48h duration | Remaining decreases | -| 7 | Switch character / offline | Remaining frozen | +| 7 | Switch character / offline | Remaining time keeps decreasing; expired injuries disappear when processed or loaded | | 8 | Duration hits 0 | Trait removed, lost message | | 9 | Successful surgery on a healing injury | Injury removed, lost message | | 10 | Surgery offer with only permanent injuries | Refused | diff --git a/projects/RPCharacters/overview.md b/projects/RPCharacters/overview.md index 9855199..5c25dc9 100644 --- a/projects/RPCharacters/overview.md +++ b/projects/RPCharacters/overview.md @@ -4,7 +4,7 @@ Roleplay characters, identity, progression and character state. Build and run wi ## Dependencies -TLibs and SimpleFactions resolve as Maven plugin artifacts at the versions declared in `pom.xml`. The shared `setup-plugins` action installs those versions. For a local build, check out TLibs `main` alongside the source and run `python3 ../tlibs/tools/install-plugins.py --pom pom.xml --mode pinned`. The installer verifies release checksums; see [TLibs dependency setup](../TLibs/README.md). Runtime optional integrations can still be mandatory compile dependencies. +TLibs, SimpleFactions and VehicleFramework resolve as Maven plugin artifacts at the versions declared in `pom.xml`. The shared `setup-plugins` action installs those versions. For a local build, check out TLibs `main` alongside the source and run `python3 ../tlibs/tools/install-plugins.py --pom pom.xml --mode pinned`. The installer verifies release checksums; see [TLibs dependency setup](../TLibs/README.md). Runtime optional integrations can still be mandatory compile dependencies. The remaining private reference jars are ItemsAdder 4.0.18, MMOCore 1.13.1, MMOItems, MythicLib, ProtocolLib and MythicMobs. `.github/scripts/prepare-release.sh` downloads the pinned files listed in `.github/dependencies.sha256` from private `TF-Minecraft/ServerAssets` into the ignored `libs/` directory, then runs `install-local-dependencies.sh`, which verifies the checksums and installs each jar into the local Maven repository. It requires `GH_TOKEN` with read access; CI supplies the `DEPS_TOKEN` secret. Keep licensed jars private. diff --git a/projects/Recycler/docs/SYSTEM.md b/projects/Recycler/docs/SYSTEM.md index 726a703..8257eb4 100644 --- a/projects/Recycler/docs/SYSTEM.md +++ b/projects/Recycler/docs/SYSTEM.md @@ -43,6 +43,7 @@ final_amount = floor(base_amount * return_rate * durability_factor * stack_amoun | `guns` | `GunsAndGadgetsProvider` | `0.5` | | `goldsmith_jewelry` | `GoldsmithProvider` | `0.5` | | `recipes` | `ConfigProvider` | `1.0` (the amounts written in `recipes/*.yml`) | +| `artifacts` | `ArtifactProvider` | `1.0` (the rarity amounts in `artifact_returns`) | - Rates are clamped to `0.0`-`1.0` with a console warning; non-finite values use the default. - A missing key falls back to the pre-0.3.0 keys when present: `max_return_rate` for the four crafted-item providers, `scrap_return_rate` for alloy scrap. @@ -53,7 +54,11 @@ final_amount = floor(base_amount * return_rate * durability_factor * stack_amoun ## Provider inputs -Every provider returns what actually went into the item, never the recipe as it reads today. Items crafted before their plugin recorded this are not handled (the station says they cannot be recycled). +Crafted equipment providers read recorded inputs. AdvancedCrafting ingredient +paths and alloy recipes are resolved from current definitions, as described +below. Items without the required crafting record are skipped by those +providers; an explicit config recipe may still handle them. Artifact and config +providers use configured outputs instead of crafting records. ### AdvancedCrafting (crafted items) @@ -61,7 +66,7 @@ Read `CraftProvenance` from item PDC (`ac_craft_inputs` JSON list of kind/id/amo Map `ingredient.*` inputs to live `Ingredient.getPath()` x stamped amount. Map `alloy.*` inputs by decomposing each alloy's forge recipe (base + catalyst ingredient paths) x stamped amount. -Raw AC ingredients/alloys (`ac_ingredient_id` / `ac_alloy_id`) are not provenance-backed - handle via config recipes or a future AC rule. +Raw AC ingredients/alloys (`ac_ingredient_id` / `ac_alloy_id`) are not provenance-backed; use config recipes to recycle them. ### AdvancedCrafting (alloy scrap) @@ -98,6 +103,35 @@ Rates use `0.0`-`1.0`: out-of-range values are clamped and non-finite values res Magic 0.4.7+ stamps the materials a craft actually charged on the weapon (`magic:gear_craft_inputs`, JSON item path to amount; empty for staff bypass crafts) and carries it through socket rewrites and refreshes. `MagicGearProvider` reads it with `GearProvenance.readInputs`. Broken weapons, weapons with socketed runes, and weapons without the stamp are not handled. +### Magic (artifacts) + +`ArtifactProvider` returns the configured item by the artifact's recorded +rarity. Artifacts are found rather than crafted, so they do not need a crafting +record. Stored aura is lost. + +```yaml +artifact_returns: + min_muffle: 0.0 + item: m.currency.enchanted_dust + rarities: + common: 4 + uncommon: 8 + rare: 12 + epic: 16 + legendary: 28 + default: 4 +``` + +The default `min_muffle: 0.0` accepts any artifact; `1.0` requires it to be fully +muffled. Missing or unrecognised stored rarities use `default`. Omitted standard +rarity keys retain their shipped defaults; set a rarity to `0` to refuse it. +Artifacts below the muffle requirement or with no configured output are refused +before recipe fallback, so a matching config recipe cannot bypass these rules. + +`return_rates.artifacts` multiplies these amounts using the normal return math. +The shipped rarity table is tuned so recycling the artifacts from a Dowsing +Artifact Mine node yields roughly half the enchanted dust of a Dust Mine node. + ### GunsAndGadgets (guns) GunsAndGadgets 2.0.6+ stamps the materials a craft actually took on the gun (`gunsandgadgets:gg_craft_inputs`; empty for staff bypass or when inputs are not required) and keeps it through stat refreshes. `GunsAndGadgetsProvider` reads it with `GunCraftInputs.readFrom`. Broken guns and guns without the stamp are not handled. diff --git a/projects/Research/README.md b/projects/Research/README.md index 783ac61..1a8a850 100644 --- a/projects/Research/README.md +++ b/projects/Research/README.md @@ -11,7 +11,7 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. Research builds with Java **21** against `io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT`. Install the pinned TLibs `2.0.1` and RPCharacters `2.1.0` release artifacts with the shared dependency -installer. The build also prepares checksum-verified MMOCore `1.13.1` and +installer. The build also prepares checksum-verified MMOCore `1.13.1-SNAPSHOT` and MythicLib `1.7.1-SNAPSHOT` inputs from the private ServerAssets repository. At runtime, `plugin.yml` requires MMOItems, MythicLib, ItemsAdder, TLibs, and diff --git a/projects/Research/overview.md b/projects/Research/overview.md index afce217..6bf4fce 100644 --- a/projects/Research/overview.md +++ b/projects/Research/overview.md @@ -19,6 +19,12 @@ resolves its result, and then consumes `start_item.amount` from the hand used. If the rolled output is disabled or its result cannot be resolved, nothing is consumed. Other players get an "in use" message; the owner reopens the menu. +Lecterns without an active project retain vanilla book handling: players can +place a written book or book and quill, or open a book already on the lectern. +A book in the other hand also works when the interacting hand is empty. Remove +an existing book before starting research; an active project takes precedence +over book interactions. + **Experimenting.** Clicking an item in the player's inventory moves one of it into the experiment slot, and clicking the slot returns it. Only items listed in an aspect's `primary_items` or `secondary_items` are accepted, and each item path diff --git a/projects/ServerAssets/README.md b/projects/ServerAssets/README.md index 0e08fb2..3e69b55 100644 --- a/projects/ServerAssets/README.md +++ b/projects/ServerAssets/README.md @@ -7,7 +7,7 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. Technical documentation is maintained here. Run commands from the source checkout unless a guide says otherwise. - [JARS.md](JARS.md) -- [README.md](overview.md) +- [Contents and verification](overview.md) ## Verification and dependency access diff --git a/projects/ServerAssets/overview.md b/projects/ServerAssets/overview.md index 79233ce..9b3e060 100644 --- a/projects/ServerAssets/overview.md +++ b/projects/ServerAssets/overview.md @@ -7,14 +7,15 @@ packs. Its `main` branch and `manifest.json` define the available assets. ## Contents - `jars/`: plugin and API binaries, grouped by SHA-256 prefix with versioned filenames. -- `configs/`: plugin settings, MMOItems definitions and ItemsAdder rail assets. +- `configs/`: plugin settings, MMOItems definitions, ItemsAdder content and captured ModelEngine resource dependencies. - `configs/Archaeo/`: TFMC's Archaeo preset and lore catalogs, preserved from the source repository. See [configuration ownership and installation](../Archaeo/docs/configuration.md). -- `models/`: ModelEngine blueprints with embedded textures. +- `models/`: ModelEngine blueprints and editable ItemsAdder model sources. - `resourcepacks/`: client resource packs. - `manifest.json`: file paths, sizes, checksums, provenance and dependency mappings. -The retained ItemsAdder content is the rail subset. Keep licensed binaries and +Assets are maintained on `main`; this repository does not publish versioned releases. +Pin a commit SHA for reproducible build inputs. Keep licensed binaries and private configurations in this repository. Use the [jar inventory](JARS.md) for dependency selection. @@ -23,10 +24,17 @@ for dependency selection. Run from an authorized checkout of ServerAssets `main`: ```sh +python3 -m pip install PyYAML==6.0.3 python3 tools/verify.py +python3 tools/verify_magic.py +python3 tools/verify_companionpets.py ``` -Verification checks the size and SHA-256 checksum of every file in the manifest. +CI checks the size and SHA-256 checksum of every file in the manifest, rune and +spell bindings, and CompanionPets provider and resource references. Results are +printed to the terminal; there is no coverage gate. These checks do not establish +the current live deployment or verify client rendering. Keep private snapshot +instructions with the assets in ServerAssets. For the Archaeo configuration import, these checks verify the transfer; its `unverified-import` role does not claim runtime validation. diff --git a/projects/SimpleFactions/README.md b/projects/SimpleFactions/README.md index 5b3d9cc..3f1f730 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) +- [Installation trade and transport access](docs/installation-trade.md) - [Espionage and special positions](docs/espionage.md) ## Related integration guides diff --git a/projects/SimpleFactions/docs/installation-trade.md b/projects/SimpleFactions/docs/installation-trade.md new file mode 100644 index 0000000..cf1ce63 --- /dev/null +++ b/projects/SimpleFactions/docs/installation-trade.md @@ -0,0 +1,32 @@ +# Installation trade + +[Project index](../README.md) + +Ports, airports and train stations carry a guild's trade and production further, and so do the sea lanes and railways between them. Trade can board a line at any province along it, and it is strongest on and off at installations. + +A guild uses installations in its own realm fully. An embargo or a war closes them. Otherwise access is the better of the trade agreement and the two realms' economy laws. An isolationist host stays closed to foreigners without an agreement. + +| Economy law | Grants to foreign guilds | Own guilds' reach abroad | +|---|---|---| +| Free trade | 50% | +10% | +| Decentralized | 35% | 0 | +| Mercantilism | 15% | +20% | +| Protectionism | 10% | 0 | +| Isolationism | 0 | -25% | + +Config keys: + +- `installation-trade.transport` — rail, sea, and air, each with `trade`, `production`, and `kept-per-1000-blocks` +- `installation-trade.corridor-share` +- Relation types: `installation-access` and `blocks-installations` +- Law modifier: `installation_access` + + +Construct installations with `/faction construct `. + +## Source references + +- [Transport configuration](https://github.com/TF-Minecraft/SimpleFactions/blob/main/src/main/resources/config.yml) +- [Diplomatic access](https://github.com/TF-Minecraft/SimpleFactions/blob/main/src/main/resources/diplomacy.yml) +- [Economy laws](https://github.com/TF-Minecraft/SimpleFactions/blob/main/src/main/resources/laws.yml) +- [Trade graph](https://github.com/TF-Minecraft/SimpleFactions/tree/main/src/main/java/net/tfminecraft/simplefactions/guild/network) diff --git a/projects/TFMCCore/README.md b/projects/TFMCCore/README.md index a4bed33..ab09a83 100644 --- a/projects/TFMCCore/README.md +++ b/projects/TFMCCore/README.md @@ -9,9 +9,9 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. - [Overview](overview.md) - [Shared feature ownership](ownership.md) - [Three-part resource-pack delivery](resource-pack.md) -- [src/main/java/net/tfminecraft/tfmccore/stats/ARCHITECTURE.md](src/main/java/net/tfminecraft/tfmccore/stats/ARCHITECTURE.md) -- [src/main/java/net/tfminecraft/tfmccore/stats/STATS.md](src/main/java/net/tfminecraft/tfmccore/stats/STATS.md) -- [src/main/java/net/tfminecraft/tfmccore/tfmc/TFMC.md](src/main/java/net/tfminecraft/tfmccore/tfmc/TFMC.md) +- [Statistics architecture](src/main/java/net/tfminecraft/tfmccore/stats/ARCHITECTURE.md) +- [Statistics configuration and commands](src/main/java/net/tfminecraft/tfmccore/stats/STATS.md) +- [Player commands](src/main/java/net/tfminecraft/tfmccore/tfmc/TFMC.md) ## Builds and releases diff --git a/projects/TFMCCore/overview.md b/projects/TFMCCore/overview.md index fbbd267..6317fff 100644 --- a/projects/TFMCCore/overview.md +++ b/projects/TFMCCore/overview.md @@ -6,7 +6,7 @@ Shared gameplay systems and statistics integrations. Build and run with **Java 2 TLibs, VehicleFramework, RPCharacters, AdvancedCrafting and SimpleFactions resolve as Maven plugin artifacts at the versions declared in `pom.xml`. The shared `setup-plugins` action installs those versions. For a local build, check out TLibs `main` alongside the source and run `python3 ../tlibs/tools/install-plugins.py --pom pom.xml --mode pinned`. The installer verifies release checksums; see [TLibs dependency setup](../TLibs/README.md). Runtime optional integrations can still be mandatory compile dependencies. -The remaining private reference jars are MMOCore 1.13.1 and MythicLib. `.github/scripts/prepare-release.sh` downloads the pinned files listed in `.github/dependencies.sha256` from private `TF-Minecraft/ServerAssets` into the ignored `libs/` directory, then runs `install-local-dependencies.sh`, which verifies the checksums and installs each jar into the local Maven repository. It requires `GH_TOKEN` with read access; CI supplies the `DEPS_TOKEN` secret. Keep licensed jars private. +The private inputs include MMOCore, MythicLib, ItemsAdder and ProtocolLib, plus ModelEngine for tests. `.github/scripts/prepare-release.sh` downloads the pinned files listed in `.github/dependencies.sha256` from private `TF-Minecraft/ServerAssets` into the ignored `libs/` directory, then runs `install-local-dependencies.sh`, which verifies the checksums and installs each jar into the local Maven repository. It requires `GH_TOKEN` with read access; CI supplies the `DEPS_TOKEN` secret. Keep licensed jars private. ## Local build @@ -23,3 +23,14 @@ Output: a JAR under `target/` using the version declared in `pom.xml`. Use `mvn The current build workflow runs for pushes and pull requests to `main`. It installs shared plugin dependencies, prepares the pinned private jars, sets the version to `main-SNAPSHOT` (a dated `DEV-...` version for pull requests), and runs tests and packaging. Pull request artifacts contain the jar and `.build/plugin-dependencies.json`; unit-test reports are uploaded separately when present. Jar filenames therefore follow the selected Maven version. The separate release workflow delegates to the shared Maven release workflow. Keep the dependency properties, private download script and `.github/dependencies.sha256` aligned when changing dependencies. + +## Signed-book appearance + +`hide-book-glint` in `config.yml` defaults to `true`. With ProtocolLib enabled, +TFMCCore hides the default enchantment glint on signed books, including letters +and book skins. It changes the item copies sent to clients; stored books retain +their data. Books with an explicit glint override are left unchanged. Without +ProtocolLib, this feature logs a warning and leaves the appearance unchanged. + +Change the setting and run `/tcore reload config` to apply it. Validate books in +inventories, hands, item frames and dropped items with the intended resource pack. diff --git a/projects/TFMCWeb/README.md b/projects/TFMCWeb/README.md index 6cec1ce..4120d0c 100644 --- a/projects/TFMCWeb/README.md +++ b/projects/TFMCWeb/README.md @@ -8,7 +8,7 @@ TFMC runs Minecraft **1.21.10**. See the [shared platform and build baseline](.. ## Runtime and ownership -The entrypoint is `net.tfminecraft.tfmcweb.TFMCWeb`. TLibs is a required plugin and Essentials an optional one, declared in `src/main/resources/plugin.yml`. RPCharacters depends on TFMCWeb; TFMCWeb resolves its Discord gate API at runtime. Without RPCharacters, the Discord Survival gate is disabled while linking and HTTP remain active. +The entrypoint is `net.tfminecraft.tfmcweb.TFMCWeb`. TLibs is a required plugin; Essentials and LuckPerms are optional integrations, declared in `src/main/resources/plugin.yml`. RPCharacters depends on TFMCWeb; TFMCWeb resolves its Discord gate API at runtime. Without RPCharacters, the Discord Survival gate is disabled while linking and HTTP remain active. `ProvinceSystemGateway` provides the shared web transport. The plugin loads `config.yml`, maintains a link cache, starts the notice poller, and registers `/linkdiscord`, `/unlinkdiscord`, `/web`, `/token`, `/warning` and `/patreon`. `/patreon` shows supporter status, starts Patreon authorization when unlinked, and supports `/patreon unlink`. Configure the API URL, plugin key and realm using the identity guide below; permission defaults and exact command syntax live in `plugin.yml`. @@ -40,6 +40,7 @@ Run `mvn clean verify` to build and run the available tests; use `mvn clean inst ## Related integration guides +- [LuckPerms staff-panel bridge](luckperms-bridge.md) - [Identity and web transport](../ProvinceSystem/docs/identity/tfmcweb.md) - [Patreon integration and one-writer setup](../ProvinceSystem/docs/integrations/patreon.md) - [Authentication and security](../ProvinceSystem/docs/identity/auth-security.md) diff --git a/projects/TFMCWeb/luckperms-bridge.md b/projects/TFMCWeb/luckperms-bridge.md new file mode 100644 index 0000000..66bf26b --- /dev/null +++ b/projects/TFMCWeb/luckperms-bridge.md @@ -0,0 +1,26 @@ +# LuckPerms staff-panel bridge + +[Project index](README.md) + +The bridge is off by default and needs LuckPerms on the server: + +```yaml +luckperms-bridge: + publish: false # send LuckPerms snapshots to this server's site + apply: false # also apply staff-queued changes (implies publish) + poll-seconds: 3 # how often to fetch queued changes when applying + snapshot-seconds: 30 # how often to publish a snapshot +``` + +LuckPerms storage is shared between servers, so set `apply: true` on exactly one +of them. A server that only publishes makes its site's panel read-only. Changes +are checked against live LuckPerms data, saved together or not at all, logged in +`/lp log`, and followed by a fresh snapshot. A change made by a website admin +(rather than root) is refused if the player already holds a staff group, or if +the group it adds, or any group that group inherits, is outside the admin groups. `/web status` shows the bridge state +and the age of the last snapshot. + + +Set `LUCKPERMS_READ_ONLY=1` on a website that must never queue writes. The backend also refuses new changes without a recent applying bridge poll. + +See the [website protocol and permission policy](../ProvinceSystem/docs/integrations/luckperms.md), [plugin configuration](https://github.com/TF-Minecraft/TFMCWeb/blob/main/src/main/resources/config.yml), and [bridge implementation](https://github.com/TF-Minecraft/TFMCWeb/tree/main/src/main/java/net/tfminecraft/tfmcweb/luckperms). diff --git a/projects/TLibs/README.md b/projects/TLibs/README.md index 26e3e27..cef2c39 100644 --- a/projects/TLibs/README.md +++ b/projects/TLibs/README.md @@ -16,7 +16,7 @@ Startup loads `config.yml`, initializes APIs, registers armour and furniture lis ## Build and validation -The POM targets **Java 21** (`maven.compiler.release=21`) and the Minecraft **1.21.10** API. Build with JDK 21 and the matching rebuilt TFMC dependency releases. +The POM targets **Java 21** (`maven.compiler.release=21`) and the Minecraft **1.21.10** API. The plugin descriptor retains `api-version: 1.21.4`; this loader metadata is separate from the runtime and build baseline. Build with JDK 21 and the matching TFMC dependency releases. Use the private dependency preparation script and checksum file in the source repository to populate `libs/`. GunsAndGadgets and Cooking resolve as provided diff --git a/projects/Thievery/README.md b/projects/Thievery/README.md index c821ddf..d6215e2 100644 --- a/projects/Thievery/README.md +++ b/projects/Thievery/README.md @@ -6,8 +6,9 @@ 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. -- [docs/SYSTEM.md](docs/SYSTEM.md) -- [docs/TEST_MATRIX.md](docs/TEST_MATRIX.md) +- [Locks and theft systems](docs/SYSTEM.md) +- [Automated tests and coverage](docs/testing.md) +- [Manual test matrix](docs/TEST_MATRIX.md) ## Builds and releases diff --git a/projects/Thievery/docs/testing.md b/projects/Thievery/docs/testing.md new file mode 100644 index 0000000..bc90454 --- /dev/null +++ b/projects/Thievery/docs/testing.md @@ -0,0 +1,37 @@ +# Automated tests and coverage + +Run from the [Thievery source checkout](https://github.com/TF-Minecraft/Thievery) +with Java 21 and the pinned dependencies prepared according to the +[shared build guide](../../../PIPELINES.md#build-dependencies): + +```sh +mvn -B --no-transfer-progress clean verify +python3 .github/scripts/check-test-reports.py +``` + +JUnit, MockBukkit and Mockito exercise configuration, persistence, inventory +transfers, ownership rules, commands, plugin lifecycle and theft sessions. +Fixtures use temporary directories; legacy relative persistence paths are +isolated under `target/test-runtime`. External plugin APIs use mocks or fixtures. +Optional RPCharacters API compatibility uses a separately loaded fixture to +model servers with and without that API. + +Surefire writes test results to `target/surefire-reports/`. JaCoCo includes every +production class and writes `target/site/jacoco/index.html` and +`target/site/jacoco/jacoco.xml`. The `verify` phase requires 100% instruction, +line, branch, method and class coverage with no production exclusions. The +report checker requires test reports and rejects skipped tests; Maven fails +on test failures and errors. CI runs both commands; the Build workflow uploads +both report folders. + +Use a clean full run for combined coverage. Focused tests are useful during +development but do not establish the full suite's coverage. Tests should assert +observable contracts or regressions, including real configuration, metadata +and integration failures. Do not add impossible inventory states or invoke +private constructors solely to raise coverage. MockBukkit's unimplemented +operations can appear as skipped tests, so a passing coverage run must also +have zero skipped tests. + +Live Paper gameplay and integration checks remain separate. Use the +[manual test matrix](TEST_MATRIX.md) and record the actual server, dependency +set and results with the release. diff --git a/projects/VFBuilders/README.md b/projects/VFBuilders/README.md index 0711ca7..318e1dc 100644 --- a/projects/VFBuilders/README.md +++ b/projects/VFBuilders/README.md @@ -13,13 +13,14 @@ VFBuilders adds configurable vehicle-building stations and blueprints to Use JDK 21 and Maven from `main`. The POM resolves Paper API **1.21.10-R0.1-SNAPSHOT** with `provided` scope and targets Java 21. Install -the declared TLibs and VehicleFramework versions with the +the declared TLibs, VehicleFramework and CoreProtect versions with the [shared installer](../TLibs/README.md) in pinned mode. Prepare the authorized private dependencies with `.github/scripts/prepare-release.sh` and verify `.github/dependencies.sha256`. -The remaining local inputs in `libs/` are `ItemsAdder-4.0.18.jar`, -`json-simple-1.1.1.jar` and `gson-2.14.0.jar`. +The private inputs in `libs/` are `ItemsAdder-4.0.18.jar`, +`json-simple-1.1.1.jar`, `gson-2.14.0.jar`, `ModelEngine-R4.1.1.jar` and +`NBTAPI-2.16.1.jar`. ModelEngine and NBTAPI are test dependencies. ```sh mvn clean verify @@ -38,10 +39,10 @@ loads `config.yml`, `stations.yml`, `categories.yml` and files under `plugins/VFBuilders/blueprints/`, then starts the station manager. It creates a `data/` directory under the plugin's data folder. -Only `config.yml` and `plugin.yml` are present in the reviewed source resource -directory. The entrypoint attempts to copy missing station/category defaults -from the jar, so a fresh install needs those configurations supplied and checked; -the source checkout alone does not establish a complete first-start setup. +The JAR bundles `config.yml` plus empty `stations.yml` and `categories.yml` +defaults, and copies them when missing. Existing files are preserved. The +`blueprints/` directory starts empty: configure stations, categories and +blueprints before players can begin construction. `/vfbuilders reload` reloads configuration and rebinds stations. It requires `vfbuilders.reload`, granted to operators by default. diff --git a/projects/VehicleFramework/README.md b/projects/VehicleFramework/README.md index 45d3a53..d8ba5fb 100644 --- a/projects/VehicleFramework/README.md +++ b/projects/VehicleFramework/README.md @@ -32,7 +32,9 @@ Use `m.utils.arcane_fuel` for fuel. Matching MMOItems UTILS definitions, item ty ## Build -Use JDK 21 and Maven from the source checkout. Prepare the pinned private jars +Use JDK 21 and Maven from the source checkout. The POM uses Paper API +`1.21.10-R0.1-SNAPSHOT`; `plugin.yml` retains `api-version: 1.21.4`, which is +loader metadata rather than the supported runtime baseline. Prepare the pinned private jars with `.github/scripts/prepare-release.sh`, verify `.github/dependencies.sha256`, and install the matching TLibs and CoreProtect Maven dependencies using their source builds or the shared installer in pinned mode. Then run @@ -43,6 +45,13 @@ The ModelEngine input is the supplied `ModelEngine-R4.1.1.jar` runtime. Its newe bytecode; the Minecraft 1.21.10 adapter and API compile on Java 21. Keep the exact checksum-matching jar instead of substituting an older binary by filename. +## Metrics and coverage + +bStats is bundled from the upstream `bstats-bukkit:3.1.0` Maven artifact and +relocated into the plugin namespace. Third-party dependency bytecode is outside +the production-source coverage denominator; metrics configuration and lifecycle +integration are included in the Java tests. + ## Builds and releases See the [shared pipeline guide](../../PIPELINES.md) for development artifacts, release tags, dependency selection and rollback. diff --git a/projects/VehicleFramework/overview.md b/projects/VehicleFramework/overview.md index 4e79ca7..f00d881 100644 --- a/projects/VehicleFramework/overview.md +++ b/projects/VehicleFramework/overview.md @@ -360,7 +360,7 @@ Spline tracks persist samples and segment health separately from displays. Consi | Key | Default | Purpose | |-----|---------|---------| | `aim-mode` | `manual` | `manual` uses WASD aim keybinds; `cursor` slews the turret toward the gunner's crosshair each tick while controlled | -| `aim-vector` | first `bones` entry | `base.align` bone pair defining barrel aim direction in world space (logs a warning when falling back) | +| `aim-vector` | first `bones` entry | `base.align` bone pair defining barrel aim direction in world space (cursor-aim weapons warn when falling back; manual and fixed weapons do not) | | `cursor-range` | `80` | Fallback distance along the look ray when no block or entity is hit | | `turn-rate` | `0.5` | Follow speed in both modes; scales down with weapon health | diff --git a/projects/Woodworking/README.md b/projects/Woodworking/README.md index 307e1ae..ecf9a75 100644 --- a/projects/Woodworking/README.md +++ b/projects/Woodworking/README.md @@ -14,7 +14,22 @@ Install these matching Java 21 TFMC artifacts into local Maven before building: Populate `libs/` with the exact files and hashes listed in `.github/dependencies.sha256`. `.github/scripts/prepare-release.sh` downloads those pinned private assets when supplied with the approved dependency token. -Run `mvn clean verify` to build and run the available tests; use `mvn clean install` when another plugin needs the result as a Maven dependency. The plugin JAR is written under `target/`. Gameplay and web integration checks on the Minecraft 1.21.10 server remain separate from build verification. +Run `mvn clean verify` to build and run the available tests; use `mvn clean install` when another plugin needs the result as a Maven dependency. The plugin JAR is written under `target/`. Gameplay and external-plugin integration checks on the Minecraft 1.21.10 server remain separate from build verification. + +## Workbench rules + +Each workbench holds a project while its materials and required actions are +completed. The bench accepts any woodworking material and any tool action, so +players need to know the design. Tool work starts once every material group has +at least the amount listed by the project. Finishing waits until every required +hit group has reached its listed count too; an attempt before then leaves the +project unchanged. + +Once those minimums are met, finishing produces furniture only when the material +ids, amounts and tool actions match the recipe exactly. Any other finish ruins +the project and loses its deposited materials. Players can inspect progress +with the branding tool or cancel unfinished work for a full material refund. +Station progress persists across server restarts. ## Builds and releases diff --git a/projects/WorldBorder/README.md b/projects/WorldBorder/README.md index d26f22d..6f2ab21 100644 --- a/projects/WorldBorder/README.md +++ b/projects/WorldBorder/README.md @@ -6,8 +6,8 @@ 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. -- [docs/SYSTEM.md](docs/SYSTEM.md) -- [docs/TEST_MATRIX.md](docs/TEST_MATRIX.md) +- [Border behaviour, configuration and commands](docs/SYSTEM.md) +- [Manual test matrix](docs/TEST_MATRIX.md) ## Build and dependencies diff --git a/projects/WorldBorder/docs/SYSTEM.md b/projects/WorldBorder/docs/SYSTEM.md index c408787..d7f27d3 100644 --- a/projects/WorldBorder/docs/SYSTEM.md +++ b/projects/WorldBorder/docs/SYSTEM.md @@ -1,6 +1,6 @@ # WorldBorder - System design -Punish players who cross the configured square world border on the X/Z plane. Admins set real border coordinates per world on deploy; the jar ships a config template only. +Punish players who cross the configured rectangular world border on the X/Z plane. Admins set real border coordinates per world on deploy; the jar ships a config template only. See [TEST_MATRIX.md](TEST_MATRIX.md) for the manual checklist.