Skip to content

Commit 886221e

Browse files
authored
Merge pull request #20 from BentoBoxWorld/develop
Release 1.15.0
2 parents db9d8e3 + 51e3e90 commit 886221e

16 files changed

Lines changed: 2062 additions & 377 deletions

File tree

.github/workflows/build.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Build
2+
on:
3+
push:
4+
branches:
5+
- develop
6+
- master
7+
pull_request:
8+
types: [opened, synchronize, reopened]
9+
jobs:
10+
build:
11+
name: Build and analyze
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
15+
with:
16+
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
17+
- name: Set up JDK 21
18+
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4.8.0
19+
with:
20+
java-version: 21
21+
distribution: 'zulu'
22+
- name: Cache SonarQube packages
23+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
24+
with:
25+
path: ~/.sonar/cache
26+
key: ${{ runner.os }}-sonar
27+
restore-keys: ${{ runner.os }}-sonar
28+
- name: Cache Maven packages
29+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
30+
with:
31+
path: ~/.m2
32+
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
33+
restore-keys: ${{ runner.os }}-m2
34+
- name: Build with Maven
35+
run: mvn -B verify
36+
- name: Analyze with SonarCloud
37+
# SONAR_TOKEN is not exposed to PRs from forks, so skip the Sonar step
38+
# in that case rather than failing the workflow. Pushes and PRs from
39+
# the same repo still get analysed.
40+
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
41+
env:
42+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
43+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
44+
run: mvn -B org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=BentoBoxWorld_ExtraMobs

CLAUDE.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project
6+
7+
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.
8+
9+
## Build & test
10+
11+
- `mvn clean package` — default goal; produces `target/ExtraMobs-<version>-LOCAL.jar`.
12+
- `mvn test` — runs the test suite.
13+
- `mvn test -Dtest=MobsSpawnListenerTest` — single test class.
14+
- `mvn test -Dtest=MobsSpawnListenerTest#methodName` — single test method.
15+
- Java 21 (`<java.version>21</java.version>` in pom.xml). Targets Paper 1.21.11 and BentoBox 3.14.0-SNAPSHOT.
16+
- 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`.
17+
18+
## Architecture
19+
20+
Tiny codebase, four production classes:
21+
22+
- `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.
23+
- `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()`.
24+
- `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.
25+
- `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.
26+
- `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.
27+
28+
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`.
29+
30+
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.
31+
32+
## Testing notes
33+
34+
- 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.
35+
- 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.
36+
37+
## Resources & packaging
38+
39+
`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.

README.md

Lines changed: 93 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,118 @@
1-
# ExtraMobs Addon
1+
# 👾 ExtraMobs Add-on for BentoBox
22
[![Discord](https://img.shields.io/discord/272499714048524288.svg?logo=discord)](https://discord.bentobox.world)
33
[![Build Status](https://ci.codemc.org/buildStatus/icon?job=BentoBoxWorld/ExtraMobs)](https://ci.codemc.org/job/BentoBoxWorld/job/ExtraMobs/)
44

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

7-
## Where to find
7+
**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.
88

9-
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.
10-
You can download it from [Release tab](https://github.com/BentoBoxWorld/ExtraMobs/releases)
9+
Works with every BentoBox game mode (AcidIsland, BSkyBlock, CaveBlock, SkyGrid, …).
1110

12-
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/).
11+
---
1312

14-
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)
13+
## 🚀 Getting Started
1514

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

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

22-
## Information
23+
## ✨ How Replacements Work
2324

24-
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.
25+
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.
2526

26-
##### For Wither Skeleton and Blaze:
27+
### 🔥 Nether — Wither Skeleton & Blaze
2728

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

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

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

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

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

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

49-
## Compatibility
48+
---
5049

51-
- [x] BentoBox - 1.11.0 version
50+
## ⚙️ Configuration
5251

53-
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.
52+
### Global Defaults
5453

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

59+
nether-chances:
60+
# Chance (0.0–1.0) to spawn a Wither Skeleton instead of a Zombified Piglin.
61+
wither-skeleton: 0.01
62+
# Chance (0.0–1.0) to spawn a Blaze instead of a Zombified Piglin.
63+
blaze: 0.1
5764

58-
## Information
65+
end-chances:
66+
# Chance (0.0–1.0) to spawn a Shulker instead of an Enderman.
67+
shulker: 0.1
5968

60-
More information can be found in [Wiki Pages](https://github.com/BentoBoxWorld/ExtraMobs/wiki).
69+
overworld-chance:
70+
# Chance (0.0–1.0) to spawn a Guardian instead of a fish.
71+
guardian: 0.1
72+
```
73+
74+
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.
75+
76+
### Per-Gamemode Overrides
77+
78+
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.
79+
80+
```yaml
81+
gamemode-settings:
82+
BSkyBlock:
83+
nether:
84+
- old: ZOMBIFIED_PIGLIN
85+
new: WITHER_SKELETON
86+
chance: 0.05
87+
- old: ZOMBIFIED_PIGLIN
88+
new: BLAZE
89+
chance: 0.1
90+
end:
91+
- old: ENDERMAN
92+
new: SHULKER
93+
chance: 0.3
94+
world:
95+
- old: COD
96+
new: GUARDIAN
97+
chance: 0.15
98+
AcidIsland:
99+
end:
100+
- old: ENDERMAN
101+
new: SHULKER
102+
chance: 0.5
103+
```
104+
105+
Each rule needs:
106+
- `old` — the EntityType name of the mob to replace (e.g. `ZOMBIFIED_PIGLIN`, `ENDERMAN`, `COD`).
107+
- `new` — the EntityType name of the replacement (e.g. `WITHER_SKELETON`, `SHULKER`, `GUARDIAN`).
108+
- `chance` — probability in the range `0.0`–`1.0`.
109+
110+
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.
111+
112+
---
113+
114+
## 🐛 Bugs and Feature Requests
115+
116+
Please submit issues at [GitHub Issues](https://github.com/BentoBoxWorld/ExtraMobs/issues) or ask in the [BentoBox Discord](https://discord.bentobox.world).
117+
118+
More information is available on the [Wiki](https://github.com/BentoBoxWorld/ExtraMobs/wiki).

0 commit comments

Comments
 (0)