feat: add dynamic fire dispatch and rollback
Build / build (push) Failing after 8s

This commit is contained in:
Michael Burgess
2026-08-17 14:14:15 -04:00
parent f6f2f97f86
commit 0df9b059e2
11 changed files with 860 additions and 193 deletions
+103 -83
View File
@@ -1,6 +1,6 @@
# RoseFirefighter
RoseFirefighter is a Paper 26.2 companion plugin for **JobsReborn**. It does not implement its own economy or leveling system. Instead, it detects legitimate firefighting actions and submits matching JobsReborn actions so JobsReborn continues to handle job membership, levels, job XP, income progression, bonuses, taxes, payment limits, payment visualization, and level-ups.
RoseFirefighter is a Paper 26.2 companion plugin for **JobsReborn**. It detects legitimate firefighting, runs temporary fire emergencies, integrates with GriefPrevention, and submits JobsReborn actions so JobsReborn remains responsible for job membership, levels, XP, income, bonuses, taxes, payment limits, and progression.
## Supported firefighting
@@ -9,9 +9,9 @@ RoseFirefighter is a Paper 26.2 companion plugin for **JobsReborn**. It does not
- Extinguishing lit `CAMPFIRE` blocks.
- Extinguishing lit `SOUL_CAMPFIRE` blocks.
- Water-bucket extinguishing when the affected fire can be attributed to the player who emptied the bucket.
- Controlled RoseFirefighter emergency fires.
- Controlled RoseFirefighter emergency fires, including fire that naturally spreads during the incident.
Only players currently working the configured JobsReborn `Firefighter` job can receive the submitted JobsReborn rewards.
Only players currently working the configured JobsReborn `Firefighter` job can receive submitted JobsReborn rewards.
## Anti-farming behavior
@@ -24,22 +24,20 @@ With `rewards.anti-farm: true`, RoseFirefighter rejects payment for player-creat
- Fire caused by player-placed lava or tracked lava flow.
- Campfires a player relights and then extinguishes.
Active player-created fire and active player-placed lava remain tainted for as long as those sources still exist; they do not become payable merely because a timer expires. Configurable retention timers remain after the active source disappears. A separate location cooldown prevents duplicate/delayed-event payouts and repeated same-location farming.
Creative and spectator players do not receive rewards by default. Creative earning can be enabled explicitly in `config.yml`.
Active player-created fire and player-placed lava stay tainted while their sources remain active. Configurable retention timers remain afterward, and a separate location cooldown prevents duplicate payouts.
## Requirements
- Paper 26.2
- Java 25 for compilation/server runtime
- Java 25
- JobsReborn 5.2.6.x
- CMILib, as required by JobsReborn
- GriefPrevention 16.18.6+ (optional; enables protected-claim emergency building and forced entry)
- GriefPrevention 16.18.6+ (optional, but recommended for claim-aware dispatch and protected-property response)
- A JobsReborn-compatible economy if monetary payouts are desired
## Build
The repository includes the Gradle 9.6.1 Wrapper. A global Gradle installation is **not** required.
The repository includes the Gradle 9.6.1 Wrapper. A global Gradle installation is not required.
Windows:
@@ -53,23 +51,27 @@ Linux/macOS:
./gradlew clean build
```
The resulting plugin JAR is written to `build/libs/`.
The resulting JAR is written to `build/libs/`.
## Install
1. Copy `jobs/Firefighter.yml` to:
`plugins/Jobs/jobs/Firefighter.yml`
1. Copy `jobs/Firefighter.yml` to `plugins/Jobs/jobs/Firefighter.yml`.
2. Reload JobsReborn or restart the server.
3. Copy the RoseFirefighter JAR from `build/libs/` into the server's `plugins/` directory.
3. Copy the RoseFirefighter JAR into the server's `plugins/` directory.
4. Restart Paper.
5. Players can join with the normal JobsReborn command, for example `/jobs join Firefighter`.
5. Players can join the profession with the normal JobsReborn command, for example:
## Duty controls
`/jobs join Firefighter`
Firefighters are **on duty by default**. Duty state is persisted by UUID in `duty-status.yml`.
```text
/firefighter onduty
/firefighter offduty
/firefighter status
```
`/firefighter offduty` silences RoseFirefighter dispatch/lifecycle notifications for that Firefighter without forcing them to leave the JobsReborn job. Off-duty players retain emergency capability if they choose to respond manually, but they are not counted toward the minimum on-duty responders required to start an incident.
## JobsReborn payouts
@@ -82,113 +84,131 @@ The resulting plugin JAR is written to `build/libs/`.
| `CAMPFIRE` | `COLLECT/CAMPFIRE` | 0.50 | 0.50 |
| `SOUL_CAMPFIRE` | `COLLECT/SOUL_CAMPFIRE` | 0.75 | 0.75 |
Campfires intentionally use a synthetic JobsReborn `COLLECT` action. Putting campfires under the JobsReborn `Break` section would also reward players for simply mining the campfire block.
Emergency fires additionally apply `emergencies.reward-multiplier`, which defaults to `5`.
The per-type `action-count` options in RoseFirefighter's `config.yml` control how many JobsReborn actions are submitted for a verified extinguish. Keep them at `1` for the normal base payout. Emergency fires additionally apply `emergencies.reward-multiplier`, which defaults to `5`.
## Random incident dispatch
## Emergency sites
Fixed sites are optional. Automatic incidents and `/firefighter emergency start random` can select random locations without any configured sites.
Stand at the center of an area where controlled emergencies may appear:
When GriefPrevention is installed and `emergencies.random-locations.prefer-griefprevention-claims` is enabled, RoseFirefighter prefers a random top-level player-owned claim. The incident is labeled using the current/known claim owner, for example:
```text
🔥 FIRE ALERT! Fire reported at The_Rose94's home in world near X:123 Y:68 Z:-245.
```
On-duty Firefighters also receive a dispatch-detail line containing:
- Exact world and coordinates.
- Nearby GriefPrevention claim owners.
- Approximate distance to nearby claims, including `inside` when the incident is within the claim.
If no suitable player claim is available, RoseFirefighter can fall back to a random currently loaded world/chunk location and then to a configured fixed site. All of these behaviors are configurable.
## Fixed emergency sites
Fixed sites remain available for special locations or manual testing:
```text
/firefighter site add RoseGarden 18
```
Other management commands:
```text
/firefighter status
/firefighter site remove <name>
/firefighter site list
/firefighter emergency start <site>
/firefighter emergency start random
/firefighter emergency stop
/firefighter reload
```
`rosefirefighter.admin` is required for site, emergency, and reload management and defaults to server operators.
## Emergency behavior
## Natural fire spread and instant rollback
Automatic emergencies default to a random 30-60 minute interval and require at least one online Firefighter. Manual starts use the same online-Firefighter requirement unless `emergencies.require-firefighters-online` is disabled.
Emergency fire is allowed to behave naturally **inside the active response radius** when `emergencies.fire-spread` and `emergencies.fire-damage` are enabled.
Emergency fire generation and response behavior:
RoseFirefighter treats the incident as a temporary world transaction:
- Uses admin-defined named sites and radii.
- Can be limited to configured world names with `emergencies.allowed-worlds`.
- Places only temporary fire blocks on safe solid support blocks.
- Does not replace the supporting block.
- Cancels emergency-fire spread and spread ignition.
- Cancels nearby block burning.
- Tracks and removes remaining emergency fire on completion, manual stop, expiry, plugin disable, or server shutdown.
- Grants on-duty Firefighters the temporary `rosefirefighter.emergency.build` runtime permission while an incident is active.
- When GriefPrevention is installed, uses its claim-permission API to allow emergency building, water use, and temporary forced entry only inside the active site.
- With `emergencies.forced-entry: true`, protected doors, windows, walls, and other blocks can be temporarily breached by an on-duty Firefighter. Forced-entry blocks produce no item drops or XP and are restored to their original block states when the incident ends.
- Does **not** grant `/IgnoreClaims` or global `griefprevention.adminclaims`, so an incident does not become a server-wide claim bypass.
- Tracks blocks Firefighters place at the incident and restores the replaced block state when the incident ends.
- Returns one consumed placement item per still-tracked placement to the Firefighter who placed it. Blocks the player already broke are removed from the return ledger to avoid duplication.
- Saves returns for offline Firefighters in `pending-returns.yml` and delivers them on their next join.
- Periodically removes stale tracked fire entries if an emergency fire disappears through another legitimate game event.
- Broadcasts configurable alerts, by default to all online players.
- Initial emergency fires are tracked.
- Every naturally spread fire becomes another tracked emergency fire.
- Fire spread outside the incident boundary is cancelled.
- Before emergency fire changes or burns a block, RoseFirefighter captures the first pre-incident `BlockState` for that location.
- The first snapshot wins, so later changes cannot overwrite the original property state.
- Tile/block-entity state is retained.
- Container inventories are copied and protected from becoming duplicate fire loot before rollback.
- Fire-triggered TNT is blocked by default to prevent explosion chains from escaping the controlled incident boundary.
- Secondary physics damage is suppressed by default so falling blocks and support-dependent decorations do not create untracked damage or duplicate blocks.
Example alert:
When the incident is contained, manually stopped, expires, the plugin disables, or the server performs a normal shutdown, RoseFirefighter immediately:
```text
🔥 FIRE ALERT! A fire has been reported at RoseGarden near X:123 Y:64 Z:-245. Firefighters are needed immediately!
```
1. Removes all remaining tracked emergency fire, including spread fire.
2. Removes/restores temporary Firefighter construction.
3. Restores forced-entry damage.
4. Restores all captured fire-damaged blocks to their first pre-incident state.
5. Restores preserved container contents.
6. Returns eligible temporary response-block items to the Firefighters who placed them.
## Protected-site integration
Offline Firefighter item returns are saved in `pending-returns.yml` and delivered on the player's next join.
`emergencies.protected-site-building` controls temporary emergency build access. The permission is granted with Bukkit's runtime `PermissionAttachment`, so it is temporary and does not create permanent LuckPerms/permissions-plugin data.
## GriefPrevention integration and forced entry
With GriefPrevention installed, RoseFirefighter listens to `ClaimPermissionCheckEvent` and clears a denied `Build` check only when all of the following are true:
RoseFirefighter never enables `/IgnoreClaims` and never gives Firefighters a global `griefprevention.adminclaims` bypass.
- An emergency is active.
- The player is currently working the JobsReborn Firefighter job.
- The player currently has RoseFirefighter's temporary emergency-build attachment.
- The affected block is inside the active site's configured radius.
- The action is emergency response construction, water use, extinguishing emergency fire, breaking a tracked temporary Firefighter block, or a forced-entry break when `emergencies.forced-entry` is enabled.
Instead, during an active incident it uses GriefPrevention's claim-permission API to allow the temporary response permission only when:
Forced-entry damage is temporary. The plugin snapshots the original block states, suppresses drops and XP, removes the breached blocks without physics, and restores those original states when the emergency is cleared, stopped, expires, or the plugin shuts down. Multi-block doors and beds are handled together so the structure can be restored correctly.
- The player is working the JobsReborn Firefighter job.
- The emergency is active.
- The affected block is inside the active incident radius.
`emergencies.cleanup-firefighter-blocks` controls whether placements made by Firefighters inside the active site are added to the temporary response-construction ledger.
Inside that radius Firefighters can place response blocks, empty water buckets, extinguish fire, and—when `emergencies.forced-entry: true`—make temporary forced entry through protected doors, windows, walls, and other property.
## Configuration
Forced-entry blocks:
- Produce no item drops.
- Produce no XP.
- Are captured in the same first-state rollback ledger.
- Are automatically restored when the incident ends.
Temporary build access uses Bukkit runtime `PermissionAttachment` with `rosefirefighter.emergency.build`; it does not create permanent LuckPerms data.
## Emergency configuration highlights
`src/main/resources/config.yml` includes controls for:
- JobsReborn job name.
- Normal fire, soul fire, campfire, and soul-campfire action submission.
- Duplicate reward cooldown.
- Player-created-fire and player-lava tracking retention.
- Creative-mode earning.
- Water attribution scan radius.
- Emergency enable/disable and automatic scheduling.
- Minimum online Firefighters.
- Emergency duration and fire counts.
- Emergency enable/disable and scheduling.
- Minimum **on-duty** Firefighters.
- Random claim/location dispatch.
- Fixed-site fallback.
- Nearby-claim reporting radius/count.
- Fire spread and fire damage.
- Instant fire-damage restoration.
- Fire-triggered TNT prevention.
- Secondary physics protection.
- Protected-site building.
- Forced entry.
- Temporary response-block cleanup and item return.
- Emergency duration and initial fire count.
- Emergency reward multiplier.
- Permitted worlds.
- Announcement behavior.
- Permitted emergency worlds.
- Protected-site emergency building, temporary forced entry, and temporary Firefighter block cleanup.
- Saved emergency sites.
## Development notes
## Development dependencies
Paper 26.2 uses the current Paper dependency format and Java 25 toolchain:
Paper 26.2:
```kotlin
compileOnly("io.papermc.paper:paper-api:26.2.build.+")
```
JobsReborn is referenced through its documented JitPack coordinate:
JobsReborn:
```kotlin
compileOnly("com.github.Zrips:Jobs:v5.2.6.2")
compileOnly("com.github.Zrips:Jobs:v5.2.6.2") {
isTransitive = false
}
```
GriefPrevention is an optional compile-time API dependency and a runtime soft dependency:
GriefPrevention:
```kotlin
compileOnly("com.github.GriefPrevention:GriefPrevention:16.18.6")
compileOnly("com.github.GriefPrevention:GriefPrevention:16.18.6") {
isTransitive = false
}
```
The JobsReborn and GriefPrevention dependencies are non-transitive because RoseFirefighter only needs their public API classes and should not pull their optional integrations into its compile classpath.