Files
2026-08-17 14:14:15 -04:00

215 lines
8.2 KiB
Markdown

# RoseFirefighter
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
- Extinguishing `FIRE`.
- Extinguishing `SOUL_FIRE`.
- 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, including fire that naturally spreads during the incident.
Only players currently working the configured JobsReborn `Firefighter` job can receive submitted JobsReborn rewards.
## Anti-farming behavior
With `rewards.anti-farm: true`, RoseFirefighter rejects payment for player-created fire, including:
- Flint-and-steel ignition.
- Fire-charge/direct player ignition.
- Fire started by a projectile shot by a player.
- Fire spread from a player-created fire.
- Fire caused by player-placed lava or tracked lava flow.
- Campfires a player relights and then extinguishes.
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
- JobsReborn 5.2.6.x
- CMILib, as required by JobsReborn
- 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.
Windows:
```text
gradlew.bat clean build
```
Linux/macOS:
```text
./gradlew clean build
```
The resulting JAR is written to `build/libs/`.
## Install
1. Copy `jobs/Firefighter.yml` to `plugins/Jobs/jobs/Firefighter.yml`.
2. Reload JobsReborn or restart the server.
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`.
## Duty controls
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
`jobs/Firefighter.yml` contains the base income and job-XP values. RoseFirefighter does not calculate those amounts itself.
| Verified extinguish | JobsReborn action | Base income | Base job XP |
|---|---|---:|---:|
| `FIRE` | `BREAK/FIRE` | 1.00 | 1.00 |
| `SOUL_FIRE` | `BREAK/SOUL_FIRE` | 1.25 | 1.25 |
| `CAMPFIRE` | `COLLECT/CAMPFIRE` | 0.50 | 0.50 |
| `SOUL_CAMPFIRE` | `COLLECT/SOUL_CAMPFIRE` | 0.75 | 0.75 |
Emergency fires additionally apply `emergencies.reward-multiplier`, which defaults to `5`.
## Random incident dispatch
Fixed sites are optional. Automatic incidents and `/firefighter emergency start random` can select random locations without any configured sites.
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
/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.
## Natural fire spread and instant rollback
Emergency fire is allowed to behave naturally **inside the active response radius** when `emergencies.fire-spread` and `emergencies.fire-damage` are enabled.
RoseFirefighter treats the incident as a temporary world transaction:
- 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.
When the incident is contained, manually stopped, expires, the plugin disables, or the server performs a normal shutdown, RoseFirefighter 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.
Offline Firefighter item returns are saved in `pending-returns.yml` and delivered on the player's next join.
## GriefPrevention integration and forced entry
RoseFirefighter never enables `/IgnoreClaims` and never gives Firefighters a global `griefprevention.adminclaims` bypass.
Instead, during an active incident it uses GriefPrevention's claim-permission API to allow the temporary response permission only when:
- The player is working the JobsReborn Firefighter job.
- The emergency is active.
- The affected block is inside the active incident radius.
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.
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:
- 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.
## Development dependencies
Paper 26.2:
```kotlin
compileOnly("io.papermc.paper:paper-api:26.2.build.+")
```
JobsReborn:
```kotlin
compileOnly("com.github.Zrips:Jobs:v5.2.6.2") {
isTransitive = false
}
```
GriefPrevention:
```kotlin
compileOnly("com.github.GriefPrevention:GriefPrevention:16.18.6") {
isTransitive = false
}
```