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

8.2 KiB

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:

gradlew.bat clean build

Linux/macOS:

./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.

/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:

🔥 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:

/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:

compileOnly("io.papermc.paper:paper-api:26.2.build.+")

JobsReborn:

compileOnly("com.github.Zrips:Jobs:v5.2.6.2") {
    isTransitive = false
}

GriefPrevention:

compileOnly("com.github.GriefPrevention:GriefPrevention:16.18.6") {
    isTransitive = false
}