# 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 /firefighter site list /firefighter emergency start /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 } ```