Update BlockTracker to use Spigot API directly instead of compatibility

layer
Fixes #4692 Fixes #4698
This commit is contained in:
nossr50 2021-12-27 11:09:27 -08:00
parent ddc9a69f4b
commit dd550feb65
7 changed files with 38 additions and 117 deletions

View File

@ -21,7 +21,6 @@ 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;
@ -131,12 +130,10 @@ public class BlockListener implements Listener {
BlockFace direction = event.getDirection();
Block movedBlock;
WorldCompatibilityLayer worldCompatibilityLayer = mcMMO.getCompatibilityManager().getWorldCompatibilityLayer();
for (Block block : event.getBlocks()) {
movedBlock = block.getRelative(direction);
if(BlockUtils.isWithinWorldBounds(worldCompatibilityLayer, movedBlock)) {
if(BlockUtils.isWithinWorldBounds(movedBlock)) {
mcMMO.getPlaceStore().setTrue(movedBlock);
}
}
@ -161,15 +158,13 @@ public class BlockListener implements Listener {
BlockFace direction = event.getDirection();
Block movedBlock = event.getBlock().getRelative(direction);
WorldCompatibilityLayer worldCompatibilityLayer = mcMMO.getCompatibilityManager().getWorldCompatibilityLayer();
//Spigot makes bad things happen in its API
if(BlockUtils.isWithinWorldBounds(worldCompatibilityLayer, movedBlock)) {
if(BlockUtils.isWithinWorldBounds(movedBlock)) {
mcMMO.getPlaceStore().setTrue(movedBlock);
}
for (Block block : event.getBlocks()) {
if(BlockUtils.isWithinWorldBounds(worldCompatibilityLayer, block)) {
if(BlockUtils.isWithinWorldBounds(block)) {
mcMMO.getPlaceStore().setTrue(block.getRelative(direction));
}
}
@ -192,10 +187,9 @@ public class BlockListener implements Listener {
BlockState blockState = event.getNewState();
if(ExperienceConfig.getInstance().isSnowExploitPrevented() && BlockUtils.shouldBeWatched(blockState)) {
WorldCompatibilityLayer worldCompatibilityLayer = mcMMO.getCompatibilityManager().getWorldCompatibilityLayer();
Block block = blockState.getBlock();
if(BlockUtils.isWithinWorldBounds(worldCompatibilityLayer, block)) {
if(BlockUtils.isWithinWorldBounds(block)) {
mcMMO.getPlaceStore().setTrue(block);
}
}
@ -215,10 +209,9 @@ public class BlockListener implements Listener {
if(ExperienceConfig.getInstance().preventStoneLavaFarming()) {
BlockState newState = event.getNewState();
WorldCompatibilityLayer worldCompatibilityLayer = mcMMO.getCompatibilityManager().getWorldCompatibilityLayer();
if(newState.getType() != Material.OBSIDIAN && ExperienceConfig.getInstance().doesBlockGiveSkillXP(PrimarySkillType.MINING, newState.getBlockData())) {
if(BlockUtils.isWithinWorldBounds(worldCompatibilityLayer, newState.getBlock())) {
if(BlockUtils.isWithinWorldBounds(newState.getBlock())) {
mcMMO.getPlaceStore().setTrue(newState);
}
}
@ -243,9 +236,7 @@ public class BlockListener implements Listener {
return;
}
WorldCompatibilityLayer worldCompatibilityLayer = mcMMO.getCompatibilityManager().getWorldCompatibilityLayer();
if(BlockUtils.isWithinWorldBounds(worldCompatibilityLayer, block)) {
if(BlockUtils.isWithinWorldBounds(block)) {
//NOTE: BlockMultiPlace has its own logic so don't handle anything that would overlap
if (!(event instanceof BlockMultiPlaceEvent)) {
mcMMO.getPlaceStore().setTrue(blockState);
@ -283,10 +274,8 @@ public class BlockListener implements Listener {
BlockState blockState = replacedBlockState.getBlock().getState();
Block block = blockState.getBlock();
WorldCompatibilityLayer worldCompatibilityLayer = mcMMO.getCompatibilityManager().getWorldCompatibilityLayer();
/* Check if the blocks placed should be monitored so they do not give out XP in the future */
if(BlockUtils.isWithinWorldBounds(worldCompatibilityLayer, block)) {
if(BlockUtils.isWithinWorldBounds(block)) {
//Updated: 10/5/2021
//Note: For some reason Azalea trees trigger this event but no other tree does (as of 10/5/2021) but if this changes in the future we may need to update this
if(BlockUtils.isPartOfTree(event.getBlockPlaced())) {
@ -311,9 +300,7 @@ public class BlockListener implements Listener {
return;
// Minecraft is dumb, the events still throw when a plant "grows" higher than the max block height. Even though no new block is created
WorldCompatibilityLayer worldCompatibilityLayer = mcMMO.getCompatibilityManager().getWorldCompatibilityLayer();
if(BlockUtils.isWithinWorldBounds(worldCompatibilityLayer, block)) {
if(BlockUtils.isWithinWorldBounds(block)) {
mcMMO.getPlaceStore().setFalse(block);
}
}

View File

@ -7,7 +7,6 @@ import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.skills.repair.Repair;
import com.gmail.nossr50.skills.salvage.Salvage;
import com.gmail.nossr50.util.compat.layers.world.WorldCompatibilityLayer;
import com.gmail.nossr50.util.random.RandomChanceSkill;
import com.gmail.nossr50.util.random.RandomChanceUtil;
import org.bukkit.Material;
@ -302,11 +301,17 @@ public final class BlockUtils {
return hasWoodcuttingXP(block.getState()) || isNonWoodPartOfTree(block.getType());
}
public static boolean isWithinWorldBounds(@NotNull WorldCompatibilityLayer worldCompatibilityLayer, @NotNull Block block) {
/**
* Checks to see if a Block is within the world bounds
* Prevent processing blocks from other plugins (or perhaps odd spigot anomalies) from sending blocks that can't exist within the world
* @param block
* @return
*/
public static boolean isWithinWorldBounds(@NotNull Block block) {
World world = block.getWorld();
//World min height = inclusive | World max height = exclusive
return block.getY() >= worldCompatibilityLayer.getMinWorldHeight(world) && block.getY() < worldCompatibilityLayer.getMaxWorldHeight(world);
return block.getY() >= world.getMinHeight() && block.getY() < world.getMaxHeight();
}
}

View File

@ -1,6 +1,5 @@
package com.gmail.nossr50.util.blockmeta;
import com.gmail.nossr50.mcMMO;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
@ -25,7 +24,7 @@ public class BitSetChunkStore implements ChunkStore {
private transient boolean dirty = false;
public BitSetChunkStore(@NotNull World world, int cx, int cz) {
this(world.getUID(), mcMMO.getCompatibilityManager().getWorldCompatibilityLayer().getMinWorldHeight(world), world.getMaxHeight(), cx, cz);
this(world.getUID(), world.getMinHeight(), world.getMaxHeight(), cx, cz);
}
private BitSetChunkStore(@NotNull UUID worldUid, int worldMin, int worldMax, int cx, int cz) {
@ -109,23 +108,23 @@ 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) {
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;
throw new RuntimeException("Cannot grab a minimum world height for an unloaded world");
return mcMMO.getCompatibilityManager().getWorldCompatibilityLayer().getMinWorldHeight(world);
return world.getMinHeight();
}
private static int getWorldMax(@NotNull UUID worldUid, int storedWorldMax)
private static int getWorldMax(@NotNull UUID worldUid)
{
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 storedWorldMax;
throw new RuntimeException("Cannot grab a maximum world height for an unloaded world");
return world.getMaxHeight();
}
@ -171,8 +170,8 @@ public class BitSetChunkStore implements ChunkStore {
in.readFully(temp);
BitSet stored = BitSet.valueOf(temp);
int currentWorldMin = getWorldMin(worldUid, worldMin);
int currentWorldMax = getWorldMax(worldUid, worldMax);
int currentWorldMin = getWorldMin(worldUid);
int currentWorldMax = getWorldMax(worldUid);
// The order in which the world height update code occurs here is important, the world max truncate math only holds up if done before adjusting for min changes
// Lop off extra data if world max has shrunk
@ -273,8 +272,8 @@ public class BitSetChunkStore implements ChunkStore {
public @NotNull BitSetChunkStore convert()
{
int currentWorldMin = getWorldMin(worldUid, 0);
int currentWorldMax = getWorldMax(worldUid, worldMax);
int currentWorldMin = getWorldMin(worldUid);
int currentWorldMax = getWorldMax(worldUid);
BitSetChunkStore converted = new BitSetChunkStore(worldUid, currentWorldMin, currentWorldMax, cx, cz);

View File

@ -10,12 +10,9 @@ 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;
@ -40,7 +37,6 @@ public class CompatibilityManager {
private AbstractPersistentDataLayer persistentDataLayer;
private AbstractBungeeSerializerCompatibilityLayer bungeeSerializerCompatibilityLayer;
private AbstractMasterAnglerCompatibility masterAnglerCompatibility;
private WorldCompatibilityLayer worldCompatibilityLayer;
public CompatibilityManager(@NotNull MinecraftGameVersion minecraftGameVersion) {
mcMMO.p.getLogger().info("Loading compatibility layers...");
@ -71,29 +67,10 @@ public class CompatibilityManager {
initPersistentDataLayer();
initBungeeSerializerLayer();
initMasterAnglerLayer();
initWorldCompatibilityLayer();
isFullyCompatibleServerSoftware = true;
}
private void initWorldCompatibilityLayer() {
if(minecraftGameVersion.isAtLeast(1, 17, 0)) {
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.isAtLeast(1, 16, 3)) {
masterAnglerCompatibility = new MasterAnglerCompatibilityLayer();
@ -202,10 +179,6 @@ public class CompatibilityManager {
return masterAnglerCompatibility;
}
public @NotNull WorldCompatibilityLayer getWorldCompatibilityLayer() {
return worldCompatibilityLayer;
}
public @Nullable MinecraftGameVersion getMinecraftGameVersion() {
return minecraftGameVersion;
}

View File

@ -1,11 +0,0 @@
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 256; }
}

View File

@ -1,16 +0,0 @@
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();
}
}

View File

@ -4,7 +4,6 @@ package com.gmail.nossr50.util.blockmeta;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.BlockUtils;
import com.gmail.nossr50.util.compat.CompatibilityManager;
import com.gmail.nossr50.util.compat.layers.world.WorldCompatibilityLayer;
import com.gmail.nossr50.util.platform.PlatformManager;
import com.google.common.io.Files;
import org.bukkit.Bukkit;
@ -39,10 +38,7 @@ class ChunkStoreTest {
}
private World mockWorld;
private CompatibilityManager compatibilityManager;
private WorldCompatibilityLayer worldCompatibilityLayer;
private PlatformManager platformManager;
private MockedStatic<Bukkit> bukkitMock;
private MockedStatic<mcMMO> mcMMOMock;
@ -57,24 +53,10 @@ class ChunkStoreTest {
bukkitMock = Mockito.mockStatic(Bukkit.class);
bukkitMock.when(() -> Bukkit.getWorld(worldUUID)).thenReturn(mockWorld);
platformManager = Mockito.mock(PlatformManager.class);
compatibilityManager = Mockito.mock(CompatibilityManager.class);
worldCompatibilityLayer = Mockito.mock(WorldCompatibilityLayer.class);
mcMMOMock = Mockito.mockStatic(mcMMO.class);
mcMMOMock.when(() -> mcMMO.getPlatformManager()).thenReturn(platformManager);
Assertions.assertNotNull(mcMMO.getPlatformManager());
mcMMOMock.when(() -> mcMMO.getCompatibilityManager()).thenReturn(compatibilityManager);
Assertions.assertNotNull(mcMMO.getCompatibilityManager());
Mockito.when(platformManager.getCompatibilityManager()).thenReturn(compatibilityManager);
Mockito.when(platformManager.getCompatibilityManager().getWorldCompatibilityLayer()).thenReturn(worldCompatibilityLayer);
Assertions.assertNotNull(mcMMO.getCompatibilityManager().getWorldCompatibilityLayer());
Mockito.when(worldCompatibilityLayer.getMinWorldHeight(mockWorld)).thenReturn(LEGACY_WORLD_HEIGHT_MIN);
Mockito.when(worldCompatibilityLayer.getMaxWorldHeight(mockWorld)).thenReturn(LEGACY_WORLD_HEIGHT_MAX);
Mockito.when(mockWorld.getMinHeight()).thenReturn(LEGACY_WORLD_HEIGHT_MIN);
Mockito.when(mockWorld.getMaxHeight()).thenReturn(LEGACY_WORLD_HEIGHT_MAX);
}
@AfterEach
@ -85,7 +67,7 @@ class ChunkStoreTest {
@Test
void testIndexOutOfBounds() {
Mockito.when(mcMMO.getCompatibilityManager().getWorldCompatibilityLayer().getMinWorldHeight(mockWorld)).thenReturn(-64);
Mockito.when(mockWorld.getMinHeight()).thenReturn(-64);
HashChunkManager hashChunkManager = new HashChunkManager();
// Top Block
@ -96,7 +78,7 @@ class ChunkStoreTest {
@Test
void testSetTrue() {
Mockito.when(mcMMO.getCompatibilityManager().getWorldCompatibilityLayer().getMinWorldHeight(mockWorld)).thenReturn(-64);
Mockito.when(mockWorld.getMinHeight()).thenReturn(-64);
HashChunkManager hashChunkManager = new HashChunkManager();
int radius = 2; // Could be anything but drastically changes test time
@ -117,7 +99,7 @@ class ChunkStoreTest {
Block bottomBlock = initMockBlock(1337, 0, -1337);
Assertions.assertFalse(hashChunkManager.isTrue(bottomBlock));
Assertions.assertTrue(BlockUtils.isWithinWorldBounds(worldCompatibilityLayer, bottomBlock));
Assertions.assertTrue(BlockUtils.isWithinWorldBounds(bottomBlock));
hashChunkManager.setTrue(bottomBlock);
Assertions.assertTrue(hashChunkManager.isTrue(bottomBlock));
@ -125,7 +107,7 @@ class ChunkStoreTest {
Block topBlock = initMockBlock(1337, 255, -1337);
Assertions.assertFalse(hashChunkManager.isTrue(topBlock));
Assertions.assertTrue(BlockUtils.isWithinWorldBounds(worldCompatibilityLayer, topBlock));
Assertions.assertTrue(BlockUtils.isWithinWorldBounds(topBlock));
hashChunkManager.setTrue(topBlock);
Assertions.assertTrue(hashChunkManager.isTrue(topBlock));
}
@ -161,7 +143,7 @@ class ChunkStoreTest {
@Test
void testNegativeWorldMin() throws IOException {
Mockito.when(mcMMO.getCompatibilityManager().getWorldCompatibilityLayer().getMinWorldHeight(mockWorld)).thenReturn(-64);
Mockito.when(mockWorld.getMinHeight()).thenReturn(-64);
BitSetChunkStore original = new BitSetChunkStore(mockWorld, 1, 2);
original.setTrue(14, -32, 12);
@ -180,8 +162,9 @@ class ChunkStoreTest {
original.setTrue(13, 3, 12);
byte[] serializedBytes = serializeChunkstore(original);
Mockito.when(mcMMO.getCompatibilityManager().getWorldCompatibilityLayer().getMinWorldHeight(mockWorld)).thenReturn(-64);
Mockito.when(mockWorld.getMinHeight()).thenReturn(-64);
ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(new DataInputStream(new ByteArrayInputStream(serializedBytes)));
assert deserialized != null;
assertEqualIgnoreMinMax(original, deserialized);
}
@ -202,6 +185,7 @@ class ChunkStoreTest {
original.setTrue(13, 89, 12);
byte[] serializedBytes = serializeChunkstore(original);
ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(new DataInputStream(new ByteArrayInputStream(serializedBytes)));
assert deserialized != null;
assertEqual(original, deserialized);
}
@ -221,6 +205,7 @@ class ChunkStoreTest {
try (DataInputStream is = region.getInputStream(original.getChunkX(), original.getChunkZ())) {
Assertions.assertNotNull(is);
ChunkStore deserialized = BitSetChunkStore.Serialization.readChunkStore(is);
assert deserialized != null;
assertEqual(original, deserialized);
}
region.close();
@ -299,7 +284,6 @@ class ChunkStoreTest {
}
public static class LegacyChunkStore implements ChunkStore, Serializable {
private static final long serialVersionUID = -1L;
transient private boolean dirty = false;
public boolean[][][] store;