Fix ArrayIndexOutOfBounds for certain events due to spigot API bug

Fixes #4488
This commit is contained in:
nossr50
2021-04-14 15:50:13 -07:00
parent 22b24b4774
commit 700a7f4d35
9 changed files with 429 additions and 28 deletions

View File

@ -20,6 +20,7 @@ import com.gmail.nossr50.skills.repair.Repair;
import com.gmail.nossr50.skills.salvage.Salvage;
import com.gmail.nossr50.skills.woodcutting.WoodcuttingManager;
import com.gmail.nossr50.util.*;
import com.gmail.nossr50.util.compat.layers.world.WorldCompatibilityLayer;
import com.gmail.nossr50.util.player.UserManager;
import com.gmail.nossr50.util.skills.SkillUtils;
import com.gmail.nossr50.util.sounds.SoundManager;
@ -147,13 +148,21 @@ public class BlockListener implements Listener {
// Get opposite direction so we get correct block
BlockFace direction = event.getDirection();
Block movedBlock = event.getBlock().getRelative(direction);
if (movedBlock.getY() >= Misc.getWorldMinCompat(movedBlock.getWorld())) // Very weird that the event is giving us these, they shouldn't exist
WorldCompatibilityLayer worldCompatibilityLayer = mcMMO.getCompatibilityManager().getWorldCompatibilityLayer();
World world = movedBlock.getWorld();
//Spigot makes bad things happen in its API
if(event.getBlock().getY() < worldCompatibilityLayer.getMaxWorldHeight(world) || event.getBlock().getY() >= worldCompatibilityLayer.getMinWorldHeight(world)) {
mcMMO.getPlaceStore().setTrue(movedBlock);
}
for (Block block : event.getBlocks()) {
movedBlock = block.getRelative(direction);
if (movedBlock.getY() < Misc.getWorldMinCompat(movedBlock.getWorld())) // Very weird that the event is giving us these, they shouldn't exist
continue;
if(block.getY() < worldCompatibilityLayer.getMaxWorldHeight(world) || block.getY() >= worldCompatibilityLayer.getMinWorldHeight(world)) {
mcMMO.getPlaceStore().setTrue(block.getRelative(direction));
}
mcMMO.getPlaceStore().setTrue(movedBlock);
}
}
@ -185,13 +194,21 @@ public class BlockListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBlockFormEvent(BlockFormEvent event)
{
/* WORLD BLACKLIST CHECK */
if(WorldBlacklist.isWorldBlacklisted(event.getBlock().getWorld()))
return;
World world = event.getBlock().getWorld();
/* WORLD BLACKLIST CHECK */ {
if(WorldBlacklist.isWorldBlacklisted(world))
return;
}
BlockState newState = event.getNewState();
if(ExperienceConfig.getInstance().preventStoneLavaFarming()) {
WorldCompatibilityLayer worldCompatibilityLayer = mcMMO.getCompatibilityManager().getWorldCompatibilityLayer();
if(event.getBlock().getY() > worldCompatibilityLayer.getMaxWorldHeight(world) || event.getBlock().getY() < worldCompatibilityLayer.getMinWorldHeight(world)) {
return;
}
if(newState.getType() != Material.OBSIDIAN
&& ExperienceConfig.getInstance().doesBlockGiveSkillXP(PrimarySkillType.MINING, newState.getBlockData())) {
mcMMO.getPlaceStore().setTrue(newState);

View File

@ -260,12 +260,6 @@ public final class Misc {
}
}
public static int getWorldMinCompat(World world)
{
// TODO this method should access the world min variable in a version safe manner so that we don't restrict usage to new versions of spigot only
return 0;
}
public static void printProgress(int convertedUsers, int progressInterval, long startMillis) {
if ((convertedUsers % progressInterval) == 0) {
mcMMO.p.getLogger().info(String.format("Conversion progress: %d users at %.2f users/second", convertedUsers, convertedUsers / (double) ((System.currentTimeMillis() - startMillis) / TIME_CONVERSION_FACTOR)));

View File

@ -1,5 +1,6 @@
package com.gmail.nossr50.util.blockmeta;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.Misc;
import org.bukkit.Bukkit;
import org.bukkit.World;
@ -25,7 +26,7 @@ public class BitSetChunkStore implements ChunkStore {
private transient boolean dirty = false;
public BitSetChunkStore(@NotNull World world, int cx, int cz) {
this(world.getUID(), Misc.getWorldMinCompat(world), world.getMaxHeight(), cx, cz);
this(world.getUID(), mcMMO.getCompatibilityManager().getWorldCompatibilityLayer().getMinWorldHeight(world), world.getMaxHeight(), cx, cz);
}
private BitSetChunkStore(@NotNull UUID worldUid, int worldMin, int worldMax, int cx, int cz) {
@ -109,15 +110,14 @@ public class BitSetChunkStore implements ChunkStore {
return (z * 16 + x) + (256 * (y + yOffset));
}
private static int getWorldMin(@NotNull UUID worldUid, int storedWorldMin)
{
private static int getWorldMin(@NotNull UUID worldUid, int storedWorldMin) {
World world = Bukkit.getWorld(worldUid);
// Not sure how this case could come up, but might as well handle it gracefully. Loading a chunkstore for an unloaded world?
if (world == null)
return storedWorldMin;
return Misc.getWorldMinCompat(world);
return mcMMO.getCompatibilityManager().getWorldCompatibilityLayer().getMinWorldHeight(world);
}
private static int getWorldMax(@NotNull UUID worldUid, int storedWorldMax)

View File

@ -8,5 +8,5 @@ public interface CompatibilityLayer {
* Whether or not this CompatibilityLayer successfully initialized and in theory should be functional
* @return true if this CompatibilityLayer is functional
*/
boolean noErrorsOnInitialize();
default boolean noErrorsOnInitialize() { return true; };
}

View File

@ -10,9 +10,12 @@ import com.gmail.nossr50.util.compat.layers.persistentdata.SpigotPersistentDataL
import com.gmail.nossr50.util.compat.layers.persistentdata.SpigotPersistentDataLayer_1_14;
import com.gmail.nossr50.util.compat.layers.skills.AbstractMasterAnglerCompatibility;
import com.gmail.nossr50.util.compat.layers.skills.MasterAnglerCompatibilityLayer;
import com.gmail.nossr50.util.compat.layers.world.WorldCompatibilityLayer;
import com.gmail.nossr50.util.compat.layers.world.WorldCompatibilityLayer_1_16_4;
import com.gmail.nossr50.util.nms.NMSVersion;
import com.gmail.nossr50.util.platform.MinecraftGameVersion;
import com.gmail.nossr50.util.text.StringUtils;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -37,6 +40,7 @@ public class CompatibilityManager {
private AbstractPersistentDataLayer persistentDataLayer;
private AbstractBungeeSerializerCompatibilityLayer bungeeSerializerCompatibilityLayer;
private AbstractMasterAnglerCompatibility masterAnglerCompatibility;
private WorldCompatibilityLayer worldCompatibilityLayer;
public CompatibilityManager(MinecraftGameVersion minecraftGameVersion) {
mcMMO.p.getLogger().info("Loading compatibility layers...");
@ -67,10 +71,31 @@ public class CompatibilityManager {
initPersistentDataLayer();
initBungeeSerializerLayer();
initMasterAnglerLayer();
initWorldCompatibilityLayer();
isFullyCompatibleServerSoftware = true;
}
private void initWorldCompatibilityLayer() {
if(minecraftGameVersion.getMinorVersion().asInt() >= 16 && minecraftGameVersion.getPatchVersion().asInt() >= 4 || minecraftGameVersion.getMajorVersion().asInt() >= 2) {
if(hasNewWorldMinHeightAPI()) {
worldCompatibilityLayer = new WorldCompatibilityLayer_1_16_4();
}
} else {
worldCompatibilityLayer = new WorldCompatibilityLayer() {
@Override
public int getMinWorldHeight(@NotNull World world) {
return WorldCompatibilityLayer.super.getMinWorldHeight(world);
}
@Override
public int getMaxWorldHeight(@NotNull World world) {
return WorldCompatibilityLayer.super.getMaxWorldHeight(world);
}
};
}
}
private void initMasterAnglerLayer() {
if(minecraftGameVersion.getMinorVersion().asInt() >= 16 || minecraftGameVersion.getMajorVersion().asInt() >= 2) {
if(hasNewFishingHookAPI()) {
@ -81,6 +106,16 @@ public class CompatibilityManager {
}
}
private boolean hasNewWorldMinHeightAPI() {
try {
Class<?> checkForClass = Class.forName("org.bukkit.World");
checkForClass.getMethod("getMinHeight");
return true;
} catch (ClassNotFoundException | NoSuchMethodException e) {
return false;
}
}
private boolean hasNewFishingHookAPI() {
try {
Class<?> checkForClass = Class.forName("org.bukkit.entity.FishHook");
@ -182,4 +217,8 @@ public class CompatibilityManager {
public @Nullable AbstractMasterAnglerCompatibility getMasterAnglerCompatibilityLayer() {
return masterAnglerCompatibility;
}
public @NotNull WorldCompatibilityLayer getWorldCompatibilityLayer() {
return worldCompatibilityLayer;
}
}

View File

@ -0,0 +1,11 @@
package com.gmail.nossr50.util.compat.layers.world;
import com.gmail.nossr50.util.compat.CompatibilityLayer;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
public interface WorldCompatibilityLayer extends CompatibilityLayer {
default int getMinWorldHeight(@NotNull World world) { return 0; }
default int getMaxWorldHeight(@NotNull World world) { return 255; }
}

View File

@ -0,0 +1,16 @@
package com.gmail.nossr50.util.compat.layers.world;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
public class WorldCompatibilityLayer_1_16_4 implements WorldCompatibilityLayer {
@Override
public int getMinWorldHeight(@NotNull World world) {
return world.getMinHeight();
}
@Override
public int getMaxWorldHeight(@NotNull World world) {
return world.getMaxHeight();
}
}