Fix Paper 26.2 explosion restoration stall

This commit is contained in:
Michael Burgess
2026-08-06 15:28:36 -04:00
parent 53219f19df
commit 9e69155868
21 changed files with 225 additions and 89 deletions
+79
View File
@@ -0,0 +1,79 @@
name: build
on:
workflow_dispatch:
push:
branches:
- '**'
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: 'Checkout'
uses: actions/checkout@v4
- name: 'Set up JDK'
uses: actions/setup-java@v4
with:
java-version: '25'
distribution: 'temurin'
cache: 'gradle'
- name: 'Build'
run: ./gradlew build --no-daemon
- name: 'Upload artifact'
uses: actions/upload-artifact@v4
with:
name: MobArena.jar
path: build/libs/MobArena-*.jar
- name: 'Output version'
id: version
run: |
version=$(
unzip -p build/libs/MobArena-*.jar plugin.yml \
| grep '^version: ' \
| awk '{printf $2}' \
| tr -d "'" \
)
echo "version=${version}" >> "${GITHUB_OUTPUT}"
draft:
needs: build
if: |
needs.build.result == 'success' &&
github.ref_name == 'master' &&
startsWith(github.event.head_commit.message, 'Release ') &&
!endsWith(needs.build.outputs.version, '-SNAPSHOT')
runs-on: ubuntu-latest
permissions:
contents: write
env:
VERSION: ${{ needs.build.outputs.version }}
steps:
- name: 'Checkout'
uses: actions/checkout@v4
- name: 'Download artifact'
uses: actions/download-artifact@v4
with:
name: MobArena.jar
- name: 'Extract release notes'
run: scripts/extract-release-notes -f github "${VERSION}" > release-notes.md
- name: 'Create release draft'
run: gh release create "${VERSION}" --draft --notes-file release-notes.md MobArena-*.jar
env:
GITHUB_TOKEN: ${{ github.token }}
+3 -3
View File
@@ -18,10 +18,10 @@ jobs:
uses: actions/checkout@v4
- name: 'Set up JDK'
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'adopt'
java-version: '25'
distribution: 'temurin'
cache: 'gradle'
- name: 'Build'
+9 -3
View File
@@ -1,7 +1,13 @@
MobArena [![Build Status](https://github.com/garbagemule/MobArena/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/garbagemule/MobArena/actions/workflows/build.yml)
========
MobArena — Paper 26.2 fork
==========================
MobArena is an arena-style minigame for Spigot-based Minecraft servers
This fork updates upstream MobArena 0.109 for **Paper 26.2** and **Java 25**. It removes the legacy Bukkit material access that could stall the server during arena explosions and updates APIs renamed in modern Paper.
Upstream project: https://github.com/garbagemule/MobArena
Build with `./gradlew build`. The deployable plugin is written to `build/libs/MobArena-0.109.1-tss3.jar`.
MobArena is an arena-style minigame for Paper-based Minecraft servers.
## Getting Started
+34 -6
View File
@@ -1,31 +1,36 @@
plugins {
id("java-library")
id("com.github.johnrengelman.shadow") version "8.1.1"
id("com.gradleup.shadow") version "9.6.1"
}
group = "com.garbagemule"
version = "0.109"
version = "0.109.1-tss3"
repositories {
mavenLocal()
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://jitpack.io")
maven("https://repo.maven.apache.org/maven2/")
}
dependencies {
compileOnly("org.spigotmc:spigot-api:1.19-R0.1-SNAPSHOT")
compileOnly("io.papermc.paper:paper-api:26.2.build.84-stable")
compileOnly("com.github.MilkBowl:VaultAPI:1.7.1")
api("org.bstats:bstats-bukkit:2.2.1")
testImplementation("junit:junit:4.13.2")
testImplementation("org.hamcrest:hamcrest-all:1.3")
testImplementation("org.mockito:mockito-core:3.12.4")
testImplementation("org.mockito:mockito-core:5.23.0")
testImplementation("org.mockbukkit.mockbukkit:mockbukkit-v26.1.2:4.113.2")
}
// Tests use MockBukkit's exact Paper API build because 26.2 registries are not modeled yet.
configurations.matching { it.name.startsWith("test") }.configureEach {
resolutionStrategy.force("io.papermc.paper:paper-api:26.1.2.build.57-stable")
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
languageVersion.set(JavaLanguageVersion.of(25))
}
}
@@ -66,3 +71,26 @@ tasks {
// Let the build task produce the final artifact.
build { dependsOn(shadowJar) }
}
val checkNoLegacyMaterialApi = tasks.register("checkNoLegacyMaterialApi") {
group = "verification"
description = "Fails if legacy Bukkit material access returns to production sources."
val sources = fileTree("src/main/java") { include("**/*.java") }
inputs.files(sources)
doLast {
val violations = sources.files.flatMap { file ->
file.readLines().mapIndexedNotNull { index, line ->
if ("org.bukkit.material" in line || "state.getData()" in line) {
"${file.relativeTo(projectDir)}:${index + 1}"
} else null
}
}
check(violations.isEmpty()) {
"Legacy Bukkit material API usage found:\n${violations.joinToString("\n")}"
}
}
}
tasks.named("check") {
dependsOn(checkNoLegacyMaterialApi)
}
+12 -1
View File
@@ -12,6 +12,16 @@ These changes will (most likely) be included in the next version.
## [Unreleased]
## [0.109.1-tss3] - 2026-08-06
### Changed
- Updated the build and runtime target to Paper 26.2 and Java 25.
- Updated renamed health attributes, potion effects, and entity types for the current Paper API.
- Updated Gradle, Shadow, Mockito, and the test harness for Java 25.
### Fixed
- Removed legacy Bukkit material access from explosion handling and arena block restoration. Creeper and other block explosions no longer initialize CraftLegacy and DataFixerUpper on the server thread.
- Restored support-sensitive blocks without physics after their supporting blocks, avoiding legacy attached-face data while preserving soft restoration.
## [0.109] - 2024-10-13
### Added
- MobArena now properly supports Vault economy providers registered after MobArena has started. This should make it possible to use custom economy providers that aren't built into Vault, such as those created with Denizen.
@@ -281,7 +291,8 @@ Thanks to:
- Swatacular for help with testing bug fixes
- Haileykins for contributions to the code base
[Unreleased]: https://github.com/garbagemule/MobArena/compare/0.109...HEAD
[Unreleased]: https://git.tss3.us/skywalker3200/MobArena/compare/0.109.1-tss3...master
[0.109.1-tss3]: https://git.tss3.us/skywalker3200/MobArena/compare/0.109...0.109.1-tss3
[0.109]: https://github.com/garbagemule/MobArena/compare/0.108...0.109
[0.108]: https://github.com/garbagemule/MobArena/compare/0.107...0.108
[0.107]: https://github.com/garbagemule/MobArena/compare/0.106...0.107
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -43,10 +43,17 @@ import org.bukkit.attribute.Attribute;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.AbstractHorse;
import org.bukkit.entity.Boat;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.ExperienceOrb;
import org.bukkit.entity.Horse;
import org.bukkit.entity.Item;
import org.bukkit.entity.Minecart;
import org.bukkit.entity.ShulkerBullet;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.InventoryView;
@@ -1026,7 +1033,7 @@ public class ArenaImpl implements Arena
mount.setTamed(true);
mount.setOwner(p);
mount.addPassenger(p);
mount.setHealth(mount.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
mount.setHealth(mount.getAttribute(Attribute.MAX_HEALTH).getValue());
// Add saddle
mount.getInventory().addItem(new ItemStack(Material.SADDLE));
@@ -1414,14 +1421,13 @@ public class ArenaImpl implements Arena
continue;
}
switch (e.getType()) {
case DROPPED_ITEM:
case EXPERIENCE_ORB:
case ARROW:
case MINECART:
case BOAT:
case PRIMED_TNT:
case SHULKER_BULLET:
if (e instanceof Item
|| e instanceof ExperienceOrb
|| e instanceof AbstractArrow
|| e instanceof Minecart
|| e instanceof Boat
|| e instanceof TNTPrimed
|| e instanceof ShulkerBullet) {
e.remove();
}
}
@@ -25,6 +25,7 @@ import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.PistonMoveReaction;
import org.bukkit.block.Sign;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.AbstractHorse;
@@ -89,10 +90,12 @@ import org.bukkit.event.vehicle.VehicleExitEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.Attachable;
import org.bukkit.material.Bed;
import org.bukkit.material.Door;
import org.bukkit.material.Redstone;
import org.bukkit.block.data.Bisected;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Bed;
import org.bukkit.block.data.type.Door;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
@@ -235,7 +238,7 @@ public class ArenaListener
r = new RepairableContainer(state);
else if (state instanceof Sign)
r = new RepairableSign(state);
else if (state.getData() instanceof Attachable)
else if (state.getBlockData().getPistonMoveReaction() == PistonMoveReaction.BREAK)
r = new RepairableAttachable(state);
else
r = new RepairableBlock(state);
@@ -485,12 +488,15 @@ public class ArenaListener
// Handle all the blocks in the block list.
for (Block b : blocks) {
BlockState state = b.getState();
BlockData data = state.getBlockData();
if (state.getData() instanceof Door && ((Door) state.getData()).isTopHalf()) {
if (data instanceof Door && ((Door) data).getHalf() == Bisected.Half.TOP) {
state = b.getRelative(BlockFace.DOWN).getState();
data = state.getBlockData();
}
else if (state.getData() instanceof Bed && ((Bed) state.getData()).isHeadOfBed()) {
state = b.getRelative(((Bed) state.getData()).getFacing().getOppositeFace()).getState();
else if (data instanceof Bed && ((Bed) data).getPart() == Bed.Part.HEAD) {
state = b.getRelative(((Bed) data).getFacing().getOppositeFace()).getState();
data = state.getBlockData();
}
// Create a Repairable from the block.
@@ -499,11 +505,11 @@ public class ArenaListener
r = new RepairableContainer(state);
else if (state instanceof Sign)
r = new RepairableSign(state);
else if (state.getData() instanceof Bed)
else if (data instanceof Bed)
r = new RepairableBed(state);
else if (state.getData() instanceof Door)
else if (data instanceof Door)
r = new RepairableDoor(state);
else if (state.getData() instanceof Attachable || state.getData() instanceof Redstone)
else if (data.getPistonMoveReaction() == PistonMoveReaction.BREAK)
r = new RepairableAttachable(state);
else
r = new RepairableBlock(state);
@@ -958,7 +964,7 @@ public class ArenaListener
// If a potion has harmful effects, remove all players.
for (PotionEffect effect : potion.getEffects()) {
PotionEffectType type = effect.getType();
if (type.equals(PotionEffectType.HARM) || type.equals(PotionEffectType.POISON)) {
if (type.equals(PotionEffectType.INSTANT_DAMAGE) || type.equals(PotionEffectType.POISON)) {
for (LivingEntity le : event.getAffectedEntities()) {
if (le instanceof Player) {
event.setIntensity(le, 0.0);
@@ -972,7 +978,7 @@ public class ArenaListener
// Otherwise, check for monster infighting
for (PotionEffect effect : potion.getEffects()) {
PotionEffectType type = effect.getType();
if (type.equals(PotionEffectType.HARM) || type.equals(PotionEffectType.POISON)) {
if (type.equals(PotionEffectType.INSTANT_DAMAGE) || type.equals(PotionEffectType.POISON)) {
for (LivingEntity le : event.getAffectedEntities()) {
if (!(le instanceof Player)) {
event.setIntensity(le, 0.0);
@@ -226,9 +226,9 @@ public class MASpawnThread implements Runnable
monsterManager.addMonster(e);
// Set the health.
int health = (int) Math.max(1D, e.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue() * mul);
int health = (int) Math.max(1D, e.getAttribute(Attribute.MAX_HEALTH).getValue() * mul);
try {
e.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(health);
e.getAttribute(Attribute.MAX_HEALTH).setBaseValue(health);
e.setHealth(health);
} catch (IllegalArgumentException ex) {
// Spigot... *facepalm*
@@ -266,9 +266,9 @@ public class MASpawnThread implements Runnable
}
break;
case SWARM:
health = (int) (mul < 1D ? e.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue() * mul : 1);
health = (int) (mul < 1D ? e.getAttribute(Attribute.MAX_HEALTH).getValue() * mul : 1);
health = Math.max(1, health);
e.setHealth(Math.min(health, e.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()));
e.setHealth(Math.min(health, e.getAttribute(Attribute.MAX_HEALTH).getValue()));
break;
case SUPPLY:
SupplyWave sw = (SupplyWave) w;
@@ -2,37 +2,20 @@ package com.garbagemule.MobArena.repairable;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.material.Attachable;
public class RepairableAttachable extends RepairableBlock
{
private int x, y, z;
public RepairableAttachable(BlockState state)
{
super(state);
BlockState attached;
if (state.getData() instanceof Attachable)
attached = state.getBlock().getRelative(((Attachable) state.getData()).getAttachedFace()).getState();
else
attached = state.getBlock().getRelative(BlockFace.DOWN).getState();
x = attached.getX();
y = attached.getY();
z = attached.getZ();
state.getBlock().setType(Material.STONE);
state.getBlock().setType(Material.STONE, false);
}
@Override
public void repair()
{
Block b = getWorld().getBlockAt(x,y,z);
if (b.getType() == Material.AIR)
b.setType(Material.STONE);
super.repair();
Block block = getWorld().getBlockAt(getX(), getY(), getZ());
block.setBlockData(getData(), false);
}
}
@@ -1,7 +1,7 @@
package com.garbagemule.MobArena.repairable;
import org.bukkit.block.BlockState;
import org.bukkit.material.Bed;
import org.bukkit.block.data.type.Bed;
public class RepairableBed extends RepairableBlock
{
@@ -10,12 +10,12 @@ public class RepairableBed extends RepairableBlock
public RepairableBed(BlockState state)
{
super(state);
other = state.getBlock().getRelative(((Bed) state.getData()).getFacing()).getState();
other = state.getBlock().getRelative(((Bed) state.getBlockData()).getFacing()).getState();
}
public void repair()
{
if (getWorld().getBlockAt(getX(), getY(), getZ()).getState().getData() instanceof Bed)
if (getWorld().getBlockAt(getX(), getY(), getZ()).getBlockData() instanceof Bed)
return;
super.repair();
@@ -1,11 +1,10 @@
package com.garbagemule.MobArena.repairable;
import org.bukkit.Material;
import org.bukkit.block.data.Attachable;
import org.bukkit.block.PistonMoveReaction;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Bed;
import org.bukkit.block.data.type.Door;
import org.bukkit.block.data.type.RedstoneWire;
import java.util.Comparator;
@@ -30,6 +29,6 @@ public class RepairableComparator implements Comparator<Repairable>
Material t = r.getType();
BlockData data = r.getData();
return (data instanceof Attachable || data instanceof RedstoneWire || data instanceof Door || data instanceof Bed || t == Material.LAVA || t == Material.WATER || t == Material.FIRE);
return (data.getPistonMoveReaction() == PistonMoveReaction.BREAK || data instanceof Door || data instanceof Bed || t == Material.LAVA || t == Material.WATER || t == Material.FIRE);
}
}
@@ -4,7 +4,7 @@ import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.material.Door;
import org.bukkit.block.data.type.Door;
public class RepairableDoor extends RepairableAttachable//RepairableBlock
{
@@ -24,7 +24,7 @@ public class RepairableDoor extends RepairableAttachable//RepairableBlock
public void repair()
{
if (getWorld().getBlockAt(getX(), getY(), getZ()).getState().getData() instanceof Door)
if (getWorld().getBlockAt(getX(), getY(), getZ()).getBlockData() instanceof Door)
return;
Block b = getWorld().getBlockAt(x,y,z);
@@ -24,13 +24,13 @@ class SetHealth extends PlayerStep {
player.setRemainingAir(NORMAL_AIR);
player.setFireTicks(NORMAL_FIRE);
double full = player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue();
double full = player.getAttribute(Attribute.MAX_HEALTH).getValue();
player.setHealth(full);
}
@Override
public void undo() {
double max = player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue();
double max = player.getAttribute(Attribute.MAX_HEALTH).getValue();
double capped = Math.min(health, max);
player.setHealth(capped);
@@ -25,7 +25,7 @@ public class MABoss
*/
public MABoss(LivingEntity entity, double maxHealth) {
try {
entity.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(maxHealth);
entity.getAttribute(Attribute.MAX_HEALTH).setBaseValue(maxHealth);
entity.setHealth(maxHealth);
} catch (IllegalArgumentException ex) {
// Spigot... *facepalm*
@@ -57,7 +57,7 @@ public class MABoss
* @return the maximum health of the boss
*/
public double getMaxHealth() {
return entity.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue();
return entity.getAttribute(Attribute.MAX_HEALTH).getValue();
}
/**
@@ -33,8 +33,8 @@ public class RootTarget implements Ability
return;
Player player = (Player) target;
player.addPotionEffect(new PotionEffect(PotionEffectType.SLOW, DURATION, AMPLIFIER));
player.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, DURATION, AMPLIFIER));
player.addPotionEffect(new PotionEffect(PotionEffectType.SLOW_FALLING, DURATION, AMPLIFIER));
player.addPotionEffect(new PotionEffect(PotionEffectType.JUMP, DURATION, -AMPLIFIER));
player.addPotionEffect(new PotionEffect(PotionEffectType.JUMP_BOOST, DURATION, -AMPLIFIER));
}
}
+1 -1
View File
@@ -2,7 +2,7 @@ name: ${project.name}
author: garbagemule
main: com.garbagemule.MobArena.MobArena
version: '${project.version}'
api-version: 1.13
api-version: '26.2'
softdepend: [Multiverse-Core,Towny,Heroes,MagicSpells,Vault]
commands:
ma:
@@ -50,7 +50,7 @@ public class HandlesSignCreationTest {
@Test
public void noSignCreationNoAction() {
SignChangeEvent event = new SignChangeEvent(null, null, null);
SignChangeEvent event = mock(SignChangeEvent.class);
when(creator.create(event)).thenReturn(null);
subject.on(event);
@@ -61,7 +61,8 @@ public class HandlesSignCreationTest {
@Test
public void passesSignFromCreator() throws Exception {
Player player = mock(Player.class);
SignChangeEvent event = new SignChangeEvent(null, player, null);
SignChangeEvent event = mock(SignChangeEvent.class);
when(event.getPlayer()).thenReturn(player);
ArenaSign sign = new ArenaSign(location(), null, null, null);
when(creator.create(event)).thenReturn(sign);
@@ -75,7 +76,8 @@ public class HandlesSignCreationTest {
@Test
public void successMessageOnCreation() {
Player player = mock(Player.class);
SignChangeEvent event = new SignChangeEvent(null, player, null);
SignChangeEvent event = mock(SignChangeEvent.class);
when(event.getPlayer()).thenReturn(player);
ArenaSign sign = new ArenaSign(location(), null, "castle", "join");
when(creator.create(event)).thenReturn(sign);
@@ -87,7 +89,7 @@ public class HandlesSignCreationTest {
@Test
public void noWriteIfCreatorThrows() {
SignChangeEvent event = new SignChangeEvent(null, null, null);
SignChangeEvent event = mock(SignChangeEvent.class);
doThrow(IllegalArgumentException.class).when(creator).create(event);
subject.on(event);
@@ -98,7 +100,8 @@ public class HandlesSignCreationTest {
@Test
public void errorMessageIfCreatorThrows() {
Player player = mock(Player.class);
SignChangeEvent event = new SignChangeEvent(null, player, null);
SignChangeEvent event = mock(SignChangeEvent.class);
when(event.getPlayer()).thenReturn(player);
String message = "it's bad";
doThrow(new IllegalArgumentException(message)).when(creator).create(event);
@@ -109,7 +112,7 @@ public class HandlesSignCreationTest {
@Test
public void noStorageIfWriterThrows() throws Exception {
SignChangeEvent event = new SignChangeEvent(null, null, null);
SignChangeEvent event = mock(SignChangeEvent.class);
ArenaSign sign = new ArenaSign(null, null, null, null);
when(creator.create(event)).thenReturn(sign);
doThrow(IOException.class).when(writer).write(sign);
@@ -122,7 +125,8 @@ public class HandlesSignCreationTest {
@Test
public void errorMessageIfWriterThrows() throws Exception {
Player player = mock(Player.class);
SignChangeEvent event = new SignChangeEvent(null, player, null);
SignChangeEvent event = mock(SignChangeEvent.class);
when(event.getPlayer()).thenReturn(player);
ArenaSign sign = new ArenaSign(null, null, null, null);
when(creator.create(event)).thenReturn(sign);
IOException exception = new IOException("it's bad");
@@ -40,7 +40,7 @@ public class SignCreatorTest {
@Test
public void noHeaderNoAction() {
String[] lines = {"ma", "castle", "join", "cool-sign"};
SignChangeEvent event = new SignChangeEvent(null, null, lines);
SignChangeEvent event = event(lines, null);
ArenaSign result = subject.create(event);
@@ -202,10 +202,12 @@ public class SignCreatorTest {
}
private SignChangeEvent event(String[] lines, Location location) {
SignChangeEvent event = mock(SignChangeEvent.class);
when(event.getLine(anyInt())).thenAnswer(invocation -> lines[invocation.getArgument(0)]);
Block block = mock(Block.class);
when(block.getLocation()).thenReturn(location);
Player player = mock(Player.class);
return new SignChangeEvent(block, player, lines);
when(event.getBlock()).thenReturn(block);
return event;
}
}
@@ -2,9 +2,12 @@ package com.garbagemule.MobArena.things;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockbukkit.mockbukkit.MockBukkit;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;
@@ -13,6 +16,15 @@ import static org.mockito.Mockito.*;
public class ItemStackThingParserTest {
private ItemStackThingParser subject;
@BeforeClass
public static void startServer() {
MockBukkit.mock();
}
@AfterClass
public static void stopServer() {
MockBukkit.unmock();
}
@Before
public void setup() {
@@ -107,7 +119,7 @@ public class ItemStackThingParserTest {
subject.parse(input);
verify(first).parse(input);
verifyZeroInteractions(third);
verifyNoInteractions(third);
}
}
@@ -70,7 +70,7 @@ public class ThingManagerTest {
verify(first).parse("thing");
verify(second).parse("thing");
verifyZeroInteractions(third);
verifyNoInteractions(third);
}
@Test