Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2649c70
Update pom.xml
BONNe Jul 9, 2020
e02f38a
Update to Minecraft 1.18 biome changes
BONNe Dec 17, 2021
012b07d
Update pom.xml 1.13.0
tastybento Jan 13, 2024
8950c87
Merge branch 'master' into develop
tastybento Jan 13, 2024
cc6db10
Update to latest Minecraft API
tastybento Jan 13, 2024
c69ecb2
Added test class
tastybento Jan 13, 2024
f79346e
Add distribution management section to POM
tastybento Jan 14, 2024
6bbccd9
Update to MC 1.21.4 and CodeMC changes (#16)
tastybento Nov 9, 2024
71bb5bb
Merge branch 'master' into develop
tastybento Nov 9, 2024
70bf1a3
Add CLAUDE.md with build commands and architecture notes
tastybento May 30, 2026
8958028
Add Pladdon and plugin.yml
tastybento May 30, 2026
4949cb2
Update to Java 21, Paper 1.21.11, BentoBox 3.14.0
tastybento May 30, 2026
adac15b
Add JUnit 5 test suite with MockBukkit
tastybento May 30, 2026
a7db171
Add GitHub Actions build with SonarCloud analysis
tastybento May 30, 2026
efe39b8
feat: per-gamemode mob spawn replacement rules (#19)
Copilot May 30, 2026
c9428ea
Update build version to 1.15.0
tastybento May 30, 2026
dd178df
Refresh README to match modern addon style
tastybento May 30, 2026
6e66c6c
Merge branch 'master' into develop
tastybento May 30, 2026
6955e9e
Remove obsolete ServerMocks after master merge
tastybento May 30, 2026
3e04c21
Address SonarCloud maintainability issues
tastybento May 31, 2026
a96a099
Wire Jacoco agent into surefire argLine
tastybento May 31, 2026
51e3e90
Address Copilot review comments on PR #20
tastybento May 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Build
on:
push:
branches:
- develop
- master
pull_request:
types: [opened, synchronize, reopened]
jobs:
build:
name: Build and analyze
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 21
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0
with:
java-version: 21
distribution: 'zulu'
- name: Cache SonarQube packages
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Maven packages
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: Build with Maven
run: mvn -B verify
- name: Analyze with SonarCloud
# SONAR_TOKEN is not exposed to PRs from forks, so skip the Sonar step
# in that case rather than failing the workflow. Pushes and PRs from
# the same repo still get analysed.
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: mvn -B org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=BentoBoxWorld_ExtraMobs
39 changes: 39 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project

ExtraMobs is a BentoBox addon (Paper plugin) that re-skins certain natural mob spawns inside GameMode-managed worlds: Zombified Piglins / Piglins → Blaze/Wither Skeleton in the Nether, Endermen → Shulkers in the End, and Fish → Guardians in deep-ocean overworld biomes. The addon does not alter Minecraft's spawn rules — it listens for natural spawns and conditionally cancels + re-spawns a different entity. As of 1.15.0 it also ships a Pladdon entry point so it can be loaded directly by Paper as well as via BentoBox.

## Build & test

- `mvn clean package` — default goal; produces `target/ExtraMobs-<version>-LOCAL.jar`.
- `mvn test` — runs the test suite.
- `mvn test -Dtest=MobsSpawnListenerTest` — single test class.
- `mvn test -Dtest=MobsSpawnListenerTest#methodName` — single test method.
- Java 21 (`<java.version>21</java.version>` in pom.xml). Targets Paper 1.21.11 and BentoBox 3.14.0-SNAPSHOT.
- Build versioning is driven by Maven profiles: `-LOCAL` by default, `-b<BUILD_NUMBER>` under Jenkins CI, and a clean release version when `GIT_BRANCH=origin/master`. Don't hand-edit version strings — change `build.version` in `pom.xml`.

## Architecture

Tiny codebase, four production classes:

- `ExtraMobsAddon` (`src/main/java/world/bentobox/extramobs/`) — extends `world.bentobox.bentobox.api.addons.Addon`. In `onLoad()` it loads `Settings` via BentoBox's `Config<>` (auto-creates `config.yml` from `src/main/resources/`). In `onEnable()` it iterates `getAddonsManager().getGameModeAddons()`, sets `hooked=true` if any GameMode is not in `disabledGameModes`, and registers `MobsSpawnListener`. If nothing hooks, the addon disables itself.
- `ExtraMobsPladdon` — extends `Pladdon`. Lets Paper load the addon directly as a plugin (paired with `src/main/resources/plugin.yml`); returns a fresh `ExtraMobsAddon` from `getAddon()`.
- `config.Settings` — `ConfigObject` with `@StoreAt(filename="config.yml", path="addons/ExtraMobs")`. Field annotations (`@ConfigEntry`, `@ConfigComment`) drive both YAML parsing and the on-disk comment block; getters/setters are mandatory for the BentoBox config framework to bind values. The `gamemode-settings` field is stored as `Map<String, Object>` because BentoBox's config layer doesn't model the nested-list-of-maps shape; `getReplacements(gameMode, env)` parses the raw structure into `MobSpawnReplacement` records on read.
- `config.MobSpawnReplacement` — POJO for one per-gamemode rule (`old` mob, `new` mob, `chance`). `resolveOldEntityType()` / `resolveNewEntityType()` upper-case via `Locale.ROOT` then call `EntityType.valueOf` defensively.
- `listeners.MobsSpawnListener` — single `@EventHandler(priority=HIGHEST, ignoreCancelled=true)` on `CreatureSpawnEvent`. Only `SpawnReason.NATURAL` events are considered. Flow: `resolveActiveGameMode(world)` returns the GameMode name (or `null` if absent / disabled), then `onEntitySpawn` dispatches by entity type + environment to `handleNetherSpawn` / `handleEndSpawn` / `handleOverworldSpawn`. Each handler first checks a "suitable block" predicate (nether brick / purpur / prismarine, with slab+stairs variants), then calls `applyGameModeReplacements` for per-gamemode rules, and only falls back to the global `nether-chances` / `end-chances` / `overworld-chance` values if no per-gamemode rule fires for that event. The four block sets are pre-computed `Set<Material>` statics on the class.

The "suitable location" helpers encode the design rule that drives the addon: replacement is gated on the player having built a themed structure. Changes to spawn rules almost always live in these predicates plus the dispatch branches in `onEntitySpawn`.

Per-gamemode rules **supplement** the globals rather than suppress them — a per-gamemode rule with `chance: 0.05` for `ZOMBIFIED_PIGLIN→WITHER_SKELETON` falls through to the global wither/blaze chances on a miss, and `PIGLIN` (different entity) always falls through. The tests `testPerGameModeNetherChanceZeroFallsBackToGlobal` and `testPerGameModeNetherReplacementEntityMismatchFallsBackToGlobal` enshrine this behaviour — keep it in mind before changing the listener.

## Testing notes

- JUnit 5 + Mockito 5 + MockBukkit (`org.mockbukkit.mockbukkit:mockbukkit-v1.21`). Test classes extend `CommonTestSetup` which calls `MockBukkit.mock()` in `@BeforeEach` and tears down in `@AfterEach`; it also injects the `BentoBox` singleton via `WhiteBox.setInternalState(BentoBox.class, "instance", plugin)` and statically stubs `Bukkit` + `Util`. The surefire plugin's long `--add-opens` argLine plus the leading `@{argLine}` (which late-binds the Jacoco prepare-agent javaagent) are required; don't strip either.
- Jacoco excludes `**/*Names*` and `org/bukkit/Material*` to avoid synthetic-field / "Material too large to mock" failures. Coverage is reported to SonarCloud via the GitHub Actions workflow.

## Resources & packaging

`src/main/resources/addon.yml` declares the addon to BentoBox (`main`, `softdepend` GameModes, icon). `config.yml` is filtered (`${version}` substitution), while `locales/*.yml` and `blueprints/*.{blu,json}` are copied unfiltered into the jar root under `./locales` and `./blueprints` — keep new resource directories consistent with this layout in `pom.xml` so BentoBox finds them at runtime.
128 changes: 93 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,60 +1,118 @@
# ExtraMobs Addon
# 👾 ExtraMobs Add-on for BentoBox
[![Discord](https://img.shields.io/discord/272499714048524288.svg?logo=discord)](https://discord.bentobox.world)
[![Build Status](https://ci.codemc.org/buildStatus/icon?job=BentoBoxWorld/ExtraMobs)](https://ci.codemc.org/job/BentoBoxWorld/job/ExtraMobs/)

Add-on for BentoBox that adjusts some mob spawning rules to get Blazes, Wither Skeleton, and Shulkers.
## 🔍 What is ExtraMobs?

## Where to find
**ExtraMobs** is a BentoBox add-on that lets players spawn **Blazes**, **Wither Skeletons**, **Shulkers** and **Guardians** on their islands by building the right structures. It does **not** change Minecraft's spawning rules — it watches for natural spawns and replaces certain mobs by chance when the surrounding blocks match a themed pattern.

Currently ExtraMobs Addon is in **Alpha stage**, so it may or may not contain bugs... a lot of bugs. Also it means, that some features are not working or implemented.
You can download it from [Release tab](https://github.com/BentoBoxWorld/ExtraMobs/releases)
Works with every BentoBox game mode (AcidIsland, BSkyBlock, CaveBlock, SkyGrid, …).

Or you can try **nightly builds** where you can check and test new features that will be implemented in next release from [Jenkins Server](https://ci.codemc.org/job/BentoBoxWorld/job/ExtraMobs/lastStableBuild/).
---

If you like this addon but something is missing or is not working as you want, you can always submit an [Issue request](https://github.com/BentoBoxWorld/ExtraMobs/issues) or get a support in Discord [BentoBox ![icon](https://avatars2.githubusercontent.com/u/41555324?s=15&v=4)](https://discord.bentobox.world)
## 🚀 Getting Started

## How to use
1. Place the **ExtraMobs** `.jar` into your BentoBox `addons` folder.
2. Restart your server.
3. The addon creates `addons/ExtraMobs/config.yml`.
4. Edit `config.yml` to adjust the spawn chances or disable specific game modes.
5. Restart the server (or reload BentoBox) to apply your changes.

1. Place the addon jar in the addons folder of the BentoBox plugin
2. Restart the server
3. In game you can change flags that allows to use current addon.
---

## Information
## ✨ How Replacements Work

This addon does not change Minecraft spawning rules. Instead it uses other mobs that are naturally generated and change their type with new entity, if all conditions are met.
ExtraMobs only acts on **natural** spawns inside a BentoBox-managed world. When a candidate mob spawns on a themed block, ExtraMobs rolls against the configured chance; on success the original spawn is cancelled and the replacement entity is summoned in its place.

##### For Wither Skeleton and Blaze:
### 🔥 Nether — Wither Skeleton & Blaze

Addon will replace Zombie Pigmen with Blaze or Wither Skeleton by chance from config, if:
- given world is generated by GameMode Addon.
- given world is Nether
- Zombie Pigmen is standing on nether brick, nether brick slab or nether brick stairs.
A **Zombified Piglin** or **Piglin** is replaced when:
- the world is the BentoBox Nether,
- and the mob is standing on **nether brick**, **nether brick slab**, or **nether brick stairs**.

##### For Shulkers:
The wither skeleton roll is checked first, then the blaze roll.

Addon will replace Enderman with Shulker by chance from config if:
- given world is generated by GameMode Addon.
- given world is the End
- Enderman is standing on purpur block, purpur stair or purpur slab.
### 🌌 End — Shulker

##### For Guardians:
An **Enderman** is replaced with a **Shulker** when:
- the world is the BentoBox End,
- and the mob is standing on a **purpur block**, **purpur slab**, or **purpur stairs**.

Addon will replace Cod, Salmon or Tropical fish with Guardian by chance from config if:
- given world is generated by GameMode Addon.
- given world is the Overworld
- biome in given location is deep ocean or any its variants
- first block above water where fish is spawned is prismarine, prismarine brick or dark prismarine (blocks, slabs and stairs).
### 🌊 Overworld — Guardian

A naturally-spawned **Cod**, **Salmon**, or **Tropical Fish** is replaced with a **Guardian** when:
- the world is the BentoBox Overworld,
- the biome is **Deep Ocean** (or Deep Cold / Deep Frozen / Deep Lukewarm),
- and the first non-water block above the fish is **prismarine**, **prismarine bricks**, or **dark prismarine** (block, slab, or stairs variant).

## Compatibility
---

- [x] BentoBox - 1.11.0 version
## ⚙️ Configuration

Addon is build on Minecraft 1.15.2 and BentoBox 1.11.0 version, however, it should even work on Minecraft 1.13.2 and BentoBox 1.0 Release.
### Global Defaults

Addon supports all Game mode addons.
```yaml
# Game modes in which ExtraMobs should not run.
# Add the GameMode addon name (e.g. BSkyBlock, AcidIsland, CaveBlock).
disabled-gamemodes: []

nether-chances:
# Chance (0.0–1.0) to spawn a Wither Skeleton instead of a Zombified Piglin.
wither-skeleton: 0.01
# Chance (0.0–1.0) to spawn a Blaze instead of a Zombified Piglin.
blaze: 0.1

## Information
end-chances:
# Chance (0.0–1.0) to spawn a Shulker instead of an Enderman.
shulker: 0.1

More information can be found in [Wiki Pages](https://github.com/BentoBoxWorld/ExtraMobs/wiki).
overworld-chance:
# Chance (0.0–1.0) to spawn a Guardian instead of a fish.
guardian: 0.1
```

All chance values are decimals between `0.0` (never) and `1.0` (always). For the Nether the wither skeleton roll is evaluated before the blaze roll, so the blaze chance is effectively conditional on the wither skeleton roll failing.

### Per-Gamemode Overrides

You can define replacement rules that only apply to a specific game mode. Each gamemode may set rules for the `nether`, `end`, and/or `world` (overworld) environments. Rules are tried in order before the global defaults. **If a rule matches the spawning entity and its chance roll succeeds, the replacement is applied and processing stops; otherwise the global defaults above are used as a fallback** — so per-gamemode rules supplement the globals rather than replace them, and you can keep the globals as a safety net for any entity the per-gamemode block doesn't cover.

```yaml
gamemode-settings:
BSkyBlock:
nether:
- old: ZOMBIFIED_PIGLIN
new: WITHER_SKELETON
chance: 0.05
- old: ZOMBIFIED_PIGLIN
new: BLAZE
chance: 0.1
end:
- old: ENDERMAN
new: SHULKER
chance: 0.3
world:
- old: COD
new: GUARDIAN
chance: 0.15
AcidIsland:
end:
- old: ENDERMAN
new: SHULKER
chance: 0.5
```

Each rule needs:
- `old` — the EntityType name of the mob to replace (e.g. `ZOMBIFIED_PIGLIN`, `ENDERMAN`, `COD`).
- `new` — the EntityType name of the replacement (e.g. `WITHER_SKELETON`, `SHULKER`, `GUARDIAN`).
- `chance` — probability in the range `0.0`–`1.0`.

The gamemode key must exactly match the GameMode addon name as registered in BentoBox (`BSkyBlock`, `AcidIsland`, `CaveBlock`, `SkyGrid`, …). Themed-block requirements (nether brick / purpur / prismarine) still apply to per-gamemode rules.

---

## 🐛 Bugs and Feature Requests

Please submit issues at [GitHub Issues](https://github.com/BentoBoxWorld/ExtraMobs/issues) or ask in the [BentoBox Discord](https://discord.bentobox.world).

More information is available on the [Wiki](https://github.com/BentoBoxWorld/ExtraMobs/wiki).
Loading
Loading