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 uses: actions/checkout@v4
- name: 'Set up JDK' - name: 'Set up JDK'
uses: actions/setup-java@v3 uses: actions/setup-java@v4
with: with:
java-version: '11' java-version: '25'
distribution: 'adopt' distribution: 'temurin'
cache: 'gradle' cache: 'gradle'
- name: 'Build' - 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 ## Getting Started
+34 -6
View File
@@ -1,31 +1,36 @@
plugins { plugins {
id("java-library") id("java-library")
id("com.github.johnrengelman.shadow") version "8.1.1" id("com.gradleup.shadow") version "9.6.1"
} }
group = "com.garbagemule" group = "com.garbagemule"
version = "0.109" version = "0.109.1-tss3"
repositories { repositories {
mavenLocal() mavenLocal()
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") maven("https://repo.papermc.io/repository/maven-public/")
maven("https://jitpack.io") maven("https://jitpack.io")
maven("https://repo.maven.apache.org/maven2/") maven("https://repo.maven.apache.org/maven2/")
} }
dependencies { 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") compileOnly("com.github.MilkBowl:VaultAPI:1.7.1")
api("org.bstats:bstats-bukkit:2.2.1") api("org.bstats:bstats-bukkit:2.2.1")
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
testImplementation("org.hamcrest:hamcrest-all:1.3") 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 { java {
toolchain { toolchain {
languageVersion.set(JavaLanguageVersion.of(8)) languageVersion.set(JavaLanguageVersion.of(25))
} }
} }
@@ -66,3 +71,26 @@ tasks {
// Let the build task produce the final artifact. // Let the build task produce the final artifact.
build { dependsOn(shadowJar) } 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] ## [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 ## [0.109] - 2024-10-13
### Added ### 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. - 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 - Swatacular for help with testing bug fixes
- Haileykins for contributions to the code base - 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.109]: https://github.com/garbagemule/MobArena/compare/0.108...0.109
[0.108]: https://github.com/garbagemule/MobArena/compare/0.107...0.108 [0.108]: https://github.com/garbagemule/MobArena/compare/0.107...0.108
[0.107]: https://github.com/garbagemule/MobArena/compare/0.106...0.107 [0.107]: https://github.com/garbagemule/MobArena/compare/0.106...0.107
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
@@ -43,10 +43,17 @@ import org.bukkit.attribute.Attribute;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.AbstractHorse;
import org.bukkit.entity.Boat;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.ExperienceOrb;
import org.bukkit.entity.Horse; 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.entity.Player;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.InventoryView;
@@ -1026,7 +1033,7 @@ public class ArenaImpl implements Arena
mount.setTamed(true); mount.setTamed(true);
mount.setOwner(p); mount.setOwner(p);
mount.addPassenger(p); mount.addPassenger(p);
mount.setHealth(mount.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue()); mount.setHealth(mount.getAttribute(Attribute.MAX_HEALTH).getValue());
// Add saddle // Add saddle
mount.getInventory().addItem(new ItemStack(Material.SADDLE)); mount.getInventory().addItem(new ItemStack(Material.SADDLE));
@@ -1414,15 +1421,14 @@ public class ArenaImpl implements Arena
continue; continue;
} }
switch (e.getType()) { if (e instanceof Item
case DROPPED_ITEM: || e instanceof ExperienceOrb
case EXPERIENCE_ORB: || e instanceof AbstractArrow
case ARROW: || e instanceof Minecart
case MINECART: || e instanceof Boat
case BOAT: || e instanceof TNTPrimed
case PRIMED_TNT: || e instanceof ShulkerBullet) {
case SHULKER_BULLET: e.remove();
e.remove();
} }
} }
} }
@@ -25,6 +25,7 @@ import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.block.PistonMoveReaction;
import org.bukkit.block.Sign; import org.bukkit.block.Sign;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.AbstractHorse;
@@ -89,10 +90,12 @@ import org.bukkit.event.vehicle.VehicleExitEvent;
import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.material.Attachable;
import org.bukkit.material.Bed; import org.bukkit.block.data.Bisected;
import org.bukkit.material.Door; import org.bukkit.block.data.BlockData;
import org.bukkit.material.Redstone; import org.bukkit.block.data.type.Bed;
import org.bukkit.block.data.type.Door;
import org.bukkit.metadata.MetadataValue; import org.bukkit.metadata.MetadataValue;
import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
@@ -235,7 +238,7 @@ public class ArenaListener
r = new RepairableContainer(state); r = new RepairableContainer(state);
else if (state instanceof Sign) else if (state instanceof Sign)
r = new RepairableSign(state); r = new RepairableSign(state);
else if (state.getData() instanceof Attachable) else if (state.getBlockData().getPistonMoveReaction() == PistonMoveReaction.BREAK)
r = new RepairableAttachable(state); r = new RepairableAttachable(state);
else else
r = new RepairableBlock(state); r = new RepairableBlock(state);
@@ -485,12 +488,15 @@ public class ArenaListener
// Handle all the blocks in the block list. // Handle all the blocks in the block list.
for (Block b : blocks) { for (Block b : blocks) {
BlockState state = b.getState(); 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(); state = b.getRelative(BlockFace.DOWN).getState();
data = state.getBlockData();
} }
else if (state.getData() instanceof Bed && ((Bed) state.getData()).isHeadOfBed()) { else if (data instanceof Bed && ((Bed) data).getPart() == Bed.Part.HEAD) {
state = b.getRelative(((Bed) state.getData()).getFacing().getOppositeFace()).getState(); state = b.getRelative(((Bed) data).getFacing().getOppositeFace()).getState();
data = state.getBlockData();
} }
// Create a Repairable from the block. // Create a Repairable from the block.
@@ -499,11 +505,11 @@ public class ArenaListener
r = new RepairableContainer(state); r = new RepairableContainer(state);
else if (state instanceof Sign) else if (state instanceof Sign)
r = new RepairableSign(state); r = new RepairableSign(state);
else if (state.getData() instanceof Bed) else if (data instanceof Bed)
r = new RepairableBed(state); r = new RepairableBed(state);
else if (state.getData() instanceof Door) else if (data instanceof Door)
r = new RepairableDoor(state); r = new RepairableDoor(state);
else if (state.getData() instanceof Attachable || state.getData() instanceof Redstone) else if (data.getPistonMoveReaction() == PistonMoveReaction.BREAK)
r = new RepairableAttachable(state); r = new RepairableAttachable(state);
else else
r = new RepairableBlock(state); r = new RepairableBlock(state);
@@ -958,7 +964,7 @@ public class ArenaListener
// If a potion has harmful effects, remove all players. // If a potion has harmful effects, remove all players.
for (PotionEffect effect : potion.getEffects()) { for (PotionEffect effect : potion.getEffects()) {
PotionEffectType type = effect.getType(); 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()) { for (LivingEntity le : event.getAffectedEntities()) {
if (le instanceof Player) { if (le instanceof Player) {
event.setIntensity(le, 0.0); event.setIntensity(le, 0.0);
@@ -972,7 +978,7 @@ public class ArenaListener
// Otherwise, check for monster infighting // Otherwise, check for monster infighting
for (PotionEffect effect : potion.getEffects()) { for (PotionEffect effect : potion.getEffects()) {
PotionEffectType type = effect.getType(); 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()) { for (LivingEntity le : event.getAffectedEntities()) {
if (!(le instanceof Player)) { if (!(le instanceof Player)) {
event.setIntensity(le, 0.0); event.setIntensity(le, 0.0);
@@ -226,9 +226,9 @@ public class MASpawnThread implements Runnable
monsterManager.addMonster(e); monsterManager.addMonster(e);
// Set the health. // 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 { try {
e.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(health); e.getAttribute(Attribute.MAX_HEALTH).setBaseValue(health);
e.setHealth(health); e.setHealth(health);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
// Spigot... *facepalm* // Spigot... *facepalm*
@@ -266,9 +266,9 @@ public class MASpawnThread implements Runnable
} }
break; break;
case SWARM: 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); 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; break;
case SUPPLY: case SUPPLY:
SupplyWave sw = (SupplyWave) w; SupplyWave sw = (SupplyWave) w;
@@ -2,37 +2,20 @@ package com.garbagemule.MobArena.repairable;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.material.Attachable;
public class RepairableAttachable extends RepairableBlock public class RepairableAttachable extends RepairableBlock
{ {
private int x, y, z;
public RepairableAttachable(BlockState state) public RepairableAttachable(BlockState state)
{ {
super(state); super(state);
state.getBlock().setType(Material.STONE, false);
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);
} }
@Override
public void repair() public void repair()
{ {
Block b = getWorld().getBlockAt(x,y,z); Block block = getWorld().getBlockAt(getX(), getY(), getZ());
if (b.getType() == Material.AIR) block.setBlockData(getData(), false);
b.setType(Material.STONE);
super.repair();
} }
} }
@@ -1,7 +1,7 @@
package com.garbagemule.MobArena.repairable; package com.garbagemule.MobArena.repairable;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.material.Bed; import org.bukkit.block.data.type.Bed;
public class RepairableBed extends RepairableBlock public class RepairableBed extends RepairableBlock
{ {
@@ -10,12 +10,12 @@ public class RepairableBed extends RepairableBlock
public RepairableBed(BlockState state) public RepairableBed(BlockState state)
{ {
super(state); super(state);
other = state.getBlock().getRelative(((Bed) state.getData()).getFacing()).getState(); other = state.getBlock().getRelative(((Bed) state.getBlockData()).getFacing()).getState();
} }
public void repair() public void repair()
{ {
if (getWorld().getBlockAt(getX(), getY(), getZ()).getState().getData() instanceof Bed) if (getWorld().getBlockAt(getX(), getY(), getZ()).getBlockData() instanceof Bed)
return; return;
super.repair(); super.repair();
@@ -1,11 +1,10 @@
package com.garbagemule.MobArena.repairable; package com.garbagemule.MobArena.repairable;
import org.bukkit.Material; 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.BlockData;
import org.bukkit.block.data.type.Bed; import org.bukkit.block.data.type.Bed;
import org.bukkit.block.data.type.Door; import org.bukkit.block.data.type.Door;
import org.bukkit.block.data.type.RedstoneWire;
import java.util.Comparator; import java.util.Comparator;
@@ -30,6 +29,6 @@ public class RepairableComparator implements Comparator<Repairable>
Material t = r.getType(); Material t = r.getType();
BlockData data = r.getData(); 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.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.material.Door; import org.bukkit.block.data.type.Door;
public class RepairableDoor extends RepairableAttachable//RepairableBlock public class RepairableDoor extends RepairableAttachable//RepairableBlock
{ {
@@ -24,7 +24,7 @@ public class RepairableDoor extends RepairableAttachable//RepairableBlock
public void repair() public void repair()
{ {
if (getWorld().getBlockAt(getX(), getY(), getZ()).getState().getData() instanceof Door) if (getWorld().getBlockAt(getX(), getY(), getZ()).getBlockData() instanceof Door)
return; return;
Block b = getWorld().getBlockAt(x,y,z); Block b = getWorld().getBlockAt(x,y,z);
@@ -24,13 +24,13 @@ class SetHealth extends PlayerStep {
player.setRemainingAir(NORMAL_AIR); player.setRemainingAir(NORMAL_AIR);
player.setFireTicks(NORMAL_FIRE); player.setFireTicks(NORMAL_FIRE);
double full = player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue(); double full = player.getAttribute(Attribute.MAX_HEALTH).getValue();
player.setHealth(full); player.setHealth(full);
} }
@Override @Override
public void undo() { 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); double capped = Math.min(health, max);
player.setHealth(capped); player.setHealth(capped);
@@ -25,7 +25,7 @@ public class MABoss
*/ */
public MABoss(LivingEntity entity, double maxHealth) { public MABoss(LivingEntity entity, double maxHealth) {
try { try {
entity.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(maxHealth); entity.getAttribute(Attribute.MAX_HEALTH).setBaseValue(maxHealth);
entity.setHealth(maxHealth); entity.setHealth(maxHealth);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
// Spigot... *facepalm* // Spigot... *facepalm*
@@ -57,7 +57,7 @@ public class MABoss
* @return the maximum health of the boss * @return the maximum health of the boss
*/ */
public double getMaxHealth() { 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; return;
Player player = (Player) target; 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.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 author: garbagemule
main: com.garbagemule.MobArena.MobArena main: com.garbagemule.MobArena.MobArena
version: '${project.version}' version: '${project.version}'
api-version: 1.13 api-version: '26.2'
softdepend: [Multiverse-Core,Towny,Heroes,MagicSpells,Vault] softdepend: [Multiverse-Core,Towny,Heroes,MagicSpells,Vault]
commands: commands:
ma: ma:
@@ -50,7 +50,7 @@ public class HandlesSignCreationTest {
@Test @Test
public void noSignCreationNoAction() { public void noSignCreationNoAction() {
SignChangeEvent event = new SignChangeEvent(null, null, null); SignChangeEvent event = mock(SignChangeEvent.class);
when(creator.create(event)).thenReturn(null); when(creator.create(event)).thenReturn(null);
subject.on(event); subject.on(event);
@@ -61,7 +61,8 @@ public class HandlesSignCreationTest {
@Test @Test
public void passesSignFromCreator() throws Exception { public void passesSignFromCreator() throws Exception {
Player player = mock(Player.class); 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); ArenaSign sign = new ArenaSign(location(), null, null, null);
when(creator.create(event)).thenReturn(sign); when(creator.create(event)).thenReturn(sign);
@@ -75,7 +76,8 @@ public class HandlesSignCreationTest {
@Test @Test
public void successMessageOnCreation() { public void successMessageOnCreation() {
Player player = mock(Player.class); 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"); ArenaSign sign = new ArenaSign(location(), null, "castle", "join");
when(creator.create(event)).thenReturn(sign); when(creator.create(event)).thenReturn(sign);
@@ -87,7 +89,7 @@ public class HandlesSignCreationTest {
@Test @Test
public void noWriteIfCreatorThrows() { public void noWriteIfCreatorThrows() {
SignChangeEvent event = new SignChangeEvent(null, null, null); SignChangeEvent event = mock(SignChangeEvent.class);
doThrow(IllegalArgumentException.class).when(creator).create(event); doThrow(IllegalArgumentException.class).when(creator).create(event);
subject.on(event); subject.on(event);
@@ -98,7 +100,8 @@ public class HandlesSignCreationTest {
@Test @Test
public void errorMessageIfCreatorThrows() { public void errorMessageIfCreatorThrows() {
Player player = mock(Player.class); 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"; String message = "it's bad";
doThrow(new IllegalArgumentException(message)).when(creator).create(event); doThrow(new IllegalArgumentException(message)).when(creator).create(event);
@@ -109,7 +112,7 @@ public class HandlesSignCreationTest {
@Test @Test
public void noStorageIfWriterThrows() throws Exception { 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); ArenaSign sign = new ArenaSign(null, null, null, null);
when(creator.create(event)).thenReturn(sign); when(creator.create(event)).thenReturn(sign);
doThrow(IOException.class).when(writer).write(sign); doThrow(IOException.class).when(writer).write(sign);
@@ -122,7 +125,8 @@ public class HandlesSignCreationTest {
@Test @Test
public void errorMessageIfWriterThrows() throws Exception { public void errorMessageIfWriterThrows() throws Exception {
Player player = mock(Player.class); 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); ArenaSign sign = new ArenaSign(null, null, null, null);
when(creator.create(event)).thenReturn(sign); when(creator.create(event)).thenReturn(sign);
IOException exception = new IOException("it's bad"); IOException exception = new IOException("it's bad");
@@ -40,7 +40,7 @@ public class SignCreatorTest {
@Test @Test
public void noHeaderNoAction() { public void noHeaderNoAction() {
String[] lines = {"ma", "castle", "join", "cool-sign"}; String[] lines = {"ma", "castle", "join", "cool-sign"};
SignChangeEvent event = new SignChangeEvent(null, null, lines); SignChangeEvent event = event(lines, null);
ArenaSign result = subject.create(event); ArenaSign result = subject.create(event);
@@ -202,10 +202,12 @@ public class SignCreatorTest {
} }
private SignChangeEvent event(String[] lines, Location location) { 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); Block block = mock(Block.class);
when(block.getLocation()).thenReturn(location); when(block.getLocation()).thenReturn(location);
Player player = mock(Player.class); when(event.getBlock()).thenReturn(block);
return new SignChangeEvent(block, player, lines); return event;
} }
} }
@@ -2,9 +2,12 @@ package com.garbagemule.MobArena.things;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.mockito.InOrder; import org.mockito.InOrder;
import org.mockbukkit.mockbukkit.MockBukkit;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.MatcherAssert.*;
@@ -13,6 +16,15 @@ import static org.mockito.Mockito.*;
public class ItemStackThingParserTest { public class ItemStackThingParserTest {
private ItemStackThingParser subject; private ItemStackThingParser subject;
@BeforeClass
public static void startServer() {
MockBukkit.mock();
}
@AfterClass
public static void stopServer() {
MockBukkit.unmock();
}
@Before @Before
public void setup() { public void setup() {
@@ -107,7 +119,7 @@ public class ItemStackThingParserTest {
subject.parse(input); subject.parse(input);
verify(first).parse(input); verify(first).parse(input);
verifyZeroInteractions(third); verifyNoInteractions(third);
} }
} }
@@ -70,7 +70,7 @@ public class ThingManagerTest {
verify(first).parse("thing"); verify(first).parse("thing");
verify(second).parse("thing"); verify(second).parse("thing");
verifyZeroInteractions(third); verifyNoInteractions(third);
} }
@Test @Test