diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 341bd4aa..b94f7f2f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '11', '17' ] + java: [ '21' ] name: Build with Java ${{ matrix.java }} steps: - name: Checkout Git repository @@ -70,11 +70,11 @@ jobs: key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - - name: Setup JDK 17 + - name: Setup JDK 21 uses: actions/setup-java@v4 with: distribution: adopt - java-version: 17 + java-version: 21 - name: Build with Gradle run: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 15e030a4..32b7d20f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -20,18 +20,18 @@ jobs: fetch-depth: 0 - name: Restore Gradle cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} restore-keys: | ${{ runner.os }}-gradle- - - name: Setup JDK 17 - uses: actions/setup-java@v3 + - name: Setup JDK 21 + uses: actions/setup-java@v4 with: distribution: adopt - java-version: 17 + java-version: 21 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 723953e4..777f130e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,18 +14,18 @@ jobs: fetch-depth: 0 - name: Restore Gradle cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} restore-keys: | ${{ runner.os }}-gradle- - - name: Setup JDK 17 - uses: actions/setup-java@v2 + - name: Setup JDK 21 + uses: actions/setup-java@v4 with: distribution: adopt - java-version: 17 + java-version: 21 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@v1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eb2cfd55..1ad3b932 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,5 +43,5 @@ Maybe the usage of [`git rebase`](http://learn.github.com/p/rebasing.html) could Requirements ------------ -- Java 11+ +- Java 21+ - Gradle 8+ diff --git a/README.md b/README.md index 94ae3484..34a1c230 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

A modern Bukkit plugin to divide enderchest into multiple inventories.
-Works with Bukkit/Spigot 1.8 to 1.20! +Works with Bukkit/Spigot 1.8 to 1.21!

@@ -52,7 +52,7 @@ Works with Bukkit/Spigot 1.8 to 1.20! |-----------|------------------------------|-------------|----------------------------------------------------------------------------------------------| | 2.2.2 | 1.8 to 1.17 | Stable | [Click here](https://www.spigotmc.org/resources/endercontainers.4750/) | | 2.2.3-dev | 1.8 to 1.20 | Beta | [Click here](https://utarwyn.fr/projects/EnderContainers/EnderContainers-2.2.3-SNAPSHOT.jar) | -| 2.3.0-dev | 1.8 to 1.20 | Development | [Click here](https://utarwyn.fr/projects/EnderContainers/EnderContainers-2.3.0-SNAPSHOT.jar) | +| 2.3.0-dev | 1.8 to 1.21 | Development | [Click here](https://utarwyn.fr/projects/EnderContainers/EnderContainers-2.3.0-SNAPSHOT.jar) | ## Technical Features diff --git a/buildSrc/src/main/groovy/fr.utarwyn.java-conventions.gradle b/buildSrc/src/main/groovy/fr.utarwyn.java-conventions.gradle index d8245a36..5b143c2c 100644 --- a/buildSrc/src/main/groovy/fr.utarwyn.java-conventions.gradle +++ b/buildSrc/src/main/groovy/fr.utarwyn.java-conventions.gradle @@ -23,13 +23,13 @@ configurations { } dependencies { - compileOnly 'org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT' + compileOnly 'org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT' // cannot upgrade due to Inventory errors testImplementation platform('org.junit:junit-bom:5.10.1') testImplementation 'org.junit.jupiter:junit-jupiter' - testImplementation 'org.mockito:mockito-core:4.11.0' // cannot upgrade to 5+ because of Java 8 compatibility - testImplementation 'org.mockito:mockito-junit-jupiter:4.11.0' - testImplementation 'org.assertj:assertj-core:3.24.2' + testImplementation 'org.mockito:mockito-core:5.15.2' + testImplementation 'org.mockito:mockito-junit-jupiter:5.15.2' + testImplementation 'org.assertj:assertj-core:3.27.3' } publishing { diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/ArmorStandAdapter.java b/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/ArmorStandAdapter.java new file mode 100644 index 00000000..a3a2b4c7 --- /dev/null +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/ArmorStandAdapter.java @@ -0,0 +1,37 @@ +package fr.utarwyn.endercontainers.compatibility; + +import fr.utarwyn.endercontainers.hologram.HologramException; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +/** + * Adapter interface for managing armor stands as holographic displays. + * This interface provides methods to spawn and destroy armor stands for specific players, + * typically used for version-specific implementations. + * + * @author Utarwyn + * @since 2.3.0 + */ +public interface ArmorStandAdapter { + + /** + * Spawns an armor stand with text at a specific location for a player. + * + * @param plugin the plugin instance + * @param observer the player who will see the armor stand + * @param location the location where to spawn the armor stand + * @param text the text to display above the armor stand + * @return the entity ID of the spawned armor stand + */ + int spawnArmorStandFor(Plugin plugin, Player observer, Location location, String text) throws HologramException; + + /** + * Destroys an armor stand for a specific player. + * + * @param observer the player who can see the armor stand + * @param entityId the entity ID of the armor stand to destroy + */ + void destroyArmorStandFor(Player observer, int entityId) throws HologramException; + +} diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/CompatibilityHelper.java b/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/CompatibilityHelper.java index cf8bc136..9b3fbc49 100644 --- a/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/CompatibilityHelper.java +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/CompatibilityHelper.java @@ -1,6 +1,8 @@ package fr.utarwyn.endercontainers.compatibility; import com.google.common.base.Preconditions; +import fr.utarwyn.endercontainers.compatibility.bukkit.BukkitArmorStandAdapter; +import fr.utarwyn.endercontainers.compatibility.nms.NMSArmorStandAdapter; import org.bukkit.Material; import org.bukkit.Sound; @@ -63,4 +65,15 @@ public static Sound searchSound(String... names) throws IllegalArgumentException }); } + /** + * Creates a new armor stand adapter instance based on the server version. + * + * @return a new armor stand adapter instance + */ + public static ArmorStandAdapter createArmorStandAdapter() { + return ServerVersion.isNewerThan(ServerVersion.V1_18) + ? new BukkitArmorStandAdapter() + : new NMSArmorStandAdapter(); + } + } diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/ServerVersion.java b/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/ServerVersion.java index f33edc45..ab70c46f 100644 --- a/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/ServerVersion.java +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/ServerVersion.java @@ -10,6 +10,7 @@ */ public enum ServerVersion { + NEWER, V1_20, V1_19_R3, V1_19_R2, @@ -35,10 +36,15 @@ public enum ServerVersion { String path = Bukkit.getServer().getClass().getPackage().getName(); BUKKIT_VERSION = path.substring(path.lastIndexOf('.') + 1); - for (ServerVersion version : values()) { - if (BUKKIT_VERSION.toUpperCase().startsWith(version.name())) { - currentVersion = version; - break; + // starting from 1.20.5, version is no more in package name + if ("craftbukkit".equals(BUKKIT_VERSION)) { + currentVersion = NEWER; + } else { + for (ServerVersion version : values()) { + if (BUKKIT_VERSION.toUpperCase().startsWith(version.name())) { + currentVersion = version; + break; + } } } } diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/bukkit/BukkitArmorStandAdapter.java b/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/bukkit/BukkitArmorStandAdapter.java new file mode 100644 index 00000000..7bcbba0b --- /dev/null +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/bukkit/BukkitArmorStandAdapter.java @@ -0,0 +1,40 @@ +package fr.utarwyn.endercontainers.compatibility.bukkit; + +import fr.utarwyn.endercontainers.compatibility.ArmorStandAdapter; +import org.bukkit.Location; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import java.util.Objects; + +public class BukkitArmorStandAdapter implements ArmorStandAdapter { + + @Override + public int spawnArmorStandFor(Plugin plugin, Player observer, Location location, String text) { + ArmorStand armorStand = (ArmorStand) Objects.requireNonNull(location.getWorld()).spawnEntity(location.add(0, 2, 0), EntityType.ARMOR_STAND); + + armorStand.setInvisible(true); + armorStand.setCustomName(text); + armorStand.setCustomNameVisible(true); + armorStand.setGravity(false); + armorStand.setInvulnerable(true); + armorStand.setMarker(true); + + observer.showEntity(plugin, armorStand); + + return armorStand.getEntityId(); + } + + @Override + public void destroyArmorStandFor(Player observer, int entityId) { + observer.getWorld().getEntities().stream() + .filter(e -> e.getEntityId() == entityId) + .findFirst() + .ifPresent(Entity::remove); + + } + +} diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/nms/NMSArmorStandAdapter.java b/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/nms/NMSArmorStandAdapter.java new file mode 100644 index 00000000..93ba5752 --- /dev/null +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/nms/NMSArmorStandAdapter.java @@ -0,0 +1,40 @@ +package fr.utarwyn.endercontainers.compatibility.nms; + +import fr.utarwyn.endercontainers.compatibility.ArmorStandAdapter; +import fr.utarwyn.endercontainers.hologram.HologramException; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +/** + * Adapter for managing armor stands as holographic displays. + * This adapter provides methods to spawn and destroy armor stands for specific players. + * This adapter is used for NMS implementations prior MC 1.19. + * + * @author Utarwyn + * @since 2.3.0 + */ +public class NMSArmorStandAdapter implements ArmorStandAdapter { + + @Override + public int spawnArmorStandFor(Plugin plugin, Player observer, Location location, String text) throws HologramException { + try { + return NMSHologramUtil.get().spawnHologram(location, text, observer); + } catch (ReflectiveOperationException cause) { + throw new HologramException("cannot spawn hologram entity", cause); + } + } + + @Override + public void destroyArmorStandFor(Player observer, int entityId) throws HologramException { + if (!observer.isOnline()) { + return; + } + try { + NMSHologramUtil.get().destroyEntity(entityId, observer); + } catch (ReflectiveOperationException cause) { + throw new HologramException("cannot destroy hologram entity", cause); + } + } + +} diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/nms/NMSHologramUtil.java b/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/nms/NMSHologramUtil.java index c95ab8c7..fc4ab075 100644 --- a/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/nms/NMSHologramUtil.java +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/compatibility/nms/NMSHologramUtil.java @@ -8,12 +8,11 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.List; import java.util.Optional; /** * This class is used to perform reflection things - * on server net classes to spawn holograms for all versions. + * on server net classes to spawn holograms for versions prior to 1.19. * * @author Utarwyn * @since 2.2.0 @@ -94,21 +93,12 @@ private NMSHologramUtil() throws ReflectiveOperationException { Class entityPlayerClass = getNMSClass("EntityPlayer", "server.level"); Class destroyPacketClass = getNMSClass("PacketPlayOutEntityDestroy", "network.protocol.game"); - // 1.19+ :: PacketPlayOutSpawnEntityLiving has been renamed PacketPlayOutSpawnEntity - String spawnPacketClassName = ServerVersion.isNewerThan(ServerVersion.V1_18) - ? "PacketPlayOutSpawnEntity" : "PacketPlayOutSpawnEntityLiving"; - Class spawnPacketClass = getNMSClass(spawnPacketClassName, "network.protocol.game"); + Class spawnPacketClass = getNMSClass("PacketPlayOutSpawnEntityLiving", "network.protocol.game"); this.packetClass = getNMSClass("Packet", "network.protocol"); this.entityClass = getNMSClass("Entity", "world.entity"); this.craftWorldClass = getCraftbukkitClass("CraftWorld"); - - // 1.19.3+ :: spawn packet constructor use Entity instead of LivingEntity - if (ServerVersion.isNewerThan(ServerVersion.V1_19)) { - this.spawnPacketConstructor = spawnPacketClass.getConstructor(this.entityClass); - } else { - this.spawnPacketConstructor = spawnPacketClass.getConstructor(getNMSClass("EntityLiving", "world.entity")); - } + this.spawnPacketConstructor = spawnPacketClass.getConstructor(getNMSClass("EntityLiving", "world.entity")); // 1.17+ :: try to use only one int in packet constructor parameters try { @@ -120,9 +110,7 @@ private NMSHologramUtil() throws ReflectiveOperationException { } // 1.17+ :: New way of retrieving player connection instance - if (ServerVersion.isNewerThan(ServerVersion.V1_19_R3)) { - this.playerConnectionField = entityPlayerClass.getField("c"); - } else if (ServerVersion.isNewerThan(ServerVersion.V1_16)) { + if (ServerVersion.isNewerThan(ServerVersion.V1_16)) { this.playerConnectionField = entityPlayerClass.getField("b"); } else { this.playerConnectionField = entityPlayerClass.getField("playerConnection"); @@ -151,13 +139,7 @@ private NMSHologramUtil() throws ReflectiveOperationException { if (ServerVersion.isNewerThan(ServerVersion.V1_14)) { Class packetMetadataClass = getNMSClass("PacketPlayOutEntityMetadata", "network.protocol.game"); Class dataWatcherClass = getNMSClass("DataWatcher", "network.syncher"); - - // 1.19.3+ :: metadata packet use a list of datawatcher items - if (ServerVersion.isNewerThan(ServerVersion.V1_19)) { - this.metadataPacketConstructor = packetMetadataClass.getConstructor(int.class, List.class); - } else { - this.metadataPacketConstructor = packetMetadataClass.getConstructor(int.class, dataWatcherClass, boolean.class); - } + this.metadataPacketConstructor = packetMetadataClass.getConstructor(int.class, dataWatcherClass, boolean.class); } else { this.metadataPacketConstructor = null; } @@ -192,12 +174,7 @@ public int spawnHologram(Location location, String text, Player observer) throws Object entity = this.createHologramEntity(location.getWorld(), location.getX(), location.getY(), location.getZ(), text); // 1.19.3+ — 1.18+ :: New method name in Entity class - Method getId; - if (ServerVersion.isNewerThan(ServerVersion.V1_19)) { - getId = this.entityClass.getMethod(ServerVersion.isNewerThan(ServerVersion.V1_19_R2) ? "af" : "ah"); - } else { - getId = getNMSDynamicMethod(this.entityClass, "getId", "ae"); - } + Method getId = getNMSDynamicMethod(this.entityClass, "getId", "ae"); int entityId = (int) getId.invoke(entity); // Send the spawn packet for 1.8+ @@ -278,9 +255,7 @@ private Object createHologramEntity(World w, double x, double y, double z, Strin Method fromStringOrNullMethod = this.chatMessageClass.getMethod("fromStringOrNull", String.class); Object chatComponent = fromStringOrNullMethod.invoke(null, text); - // 1.19+ :: method is now "b" (instead of "a") - String methodNamePost17 = ServerVersion.isNewerThan(ServerVersion.V1_18) ? "b" : "a"; - getNMSDynamicMethod(entityObject.getClass(), "setCustomName", methodNamePost17, this.chatBaseComponentClass) + getNMSDynamicMethod(entityObject.getClass(), "setCustomName", "a", this.chatBaseComponentClass) .invoke(entityObject, chatComponent); } else { Method setCustomName = entityObject.getClass().getMethod("setCustomName", String.class); @@ -320,25 +295,9 @@ private Object createHologramEntity(World w, double x, double y, double z, Strin * @throws ReflectiveOperationException if the packet cannot be instanciated */ private Object createEntityMetadataPacket(int entityId, Object entity) throws ReflectiveOperationException { - Method getDataWatcher; - if (ServerVersion.isNewerThan(ServerVersion.V1_19)) { - getDataWatcher = this.entityClass.getMethod(ServerVersion.isNewerThan(ServerVersion.V1_19_R2) ? "aj" : "al"); - } else { - getDataWatcher = getNMSDynamicMethod(this.entityClass, "getDataWatcher", "ai"); - } + Method getDataWatcher = getNMSDynamicMethod(this.entityClass, "getDataWatcher", "ai"); Object entityDataWatcher = getDataWatcher.invoke(entity); - Object metadataPacket; - - // 1.19.3+ :: metadata packet use a list of datawatcher items - if (ServerVersion.isNewerThan(ServerVersion.V1_19)) { - Method getItemListMethod = entityDataWatcher.getClass().getMethod("b"); - metadataPacket = this.metadataPacketConstructor.newInstance(entityId, getItemListMethod.invoke(entityDataWatcher)); - } else { - // 1.18+ :: New method name in Entity class - metadataPacket = this.metadataPacketConstructor.newInstance(entityId, entityDataWatcher, false); - } - - return metadataPacket; + return this.metadataPacketConstructor.newInstance(entityId, entityDataWatcher, false); } } diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/hologram/Hologram.java b/plugin/src/main/java/fr/utarwyn/endercontainers/hologram/Hologram.java index ca4c588c..090e1377 100644 --- a/plugin/src/main/java/fr/utarwyn/endercontainers/hologram/Hologram.java +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/hologram/Hologram.java @@ -1,13 +1,10 @@ package fr.utarwyn.endercontainers.hologram; -import fr.utarwyn.endercontainers.compatibility.nms.NMSHologramUtil; -import org.bukkit.Location; import org.bukkit.entity.Player; /** * This class is used to display a text above an enderchest block * if the option blockNametag was set to true. - * This class uses packets and supports versions from 1.8 to 1.15. * * @author Utarwyn * @since 2.0.0 @@ -20,16 +17,6 @@ class Hologram { */ static final double LINE_HEIGHT = 0.23D; - /** - * The title of the hologram (its content) - */ - private final String title; - - /** - * The location where the hologram have to spawn - */ - private final Location location; - /** * The player who has to receive the hologram */ @@ -38,22 +25,25 @@ class Hologram { /** * Identifier of the spawned entity for the observer */ - private Integer entityId; + private final Integer entityId; /** * Construct an hologram and spawn it directly * * @param observer The observer who has to receive the hologram - * @param title The title/content of the hologram - * @param location The location of the hologram - * @throws HologramException thrown if cannot spawn the hologram + * @param entityId The entity id of the hologram */ - Hologram(Player observer, String title, Location location) throws HologramException { + Hologram(Player observer, int entityId) { this.observer = observer; - this.title = title; - this.location = location; + this.entityId = entityId; + } - this.spawn(); + public Player getObserver() { + return observer; + } + + public Integer getEntityId() { + return entityId; } /** @@ -65,32 +55,4 @@ boolean isObserverOnline() { return this.observer != null && this.observer.isOnline(); } - /** - * Destroy the hologram for the observer with the stored entity id. - * - * @throws HologramException thrown if cannot destroy the hologram - */ - void destroy() throws HologramException { - if (this.entityId != null && this.entityId >= 0) { - try { - NMSHologramUtil.get().destroyEntity(this.entityId, this.observer); - } catch (ReflectiveOperationException cause) { - throw new HologramException("cannot destroy hologram entity", cause); - } - } - } - - /** - * Spawn the hologram. - * - * @throws HologramException thrown if cannot spawn the hologram - */ - private void spawn() throws HologramException { - try { - this.entityId = NMSHologramUtil.get().spawnHologram(this.location, this.title, this.observer); - } catch (ReflectiveOperationException cause) { - throw new HologramException("cannot spawn hologram entity", cause); - } - } - } diff --git a/plugin/src/main/java/fr/utarwyn/endercontainers/hologram/HologramManager.java b/plugin/src/main/java/fr/utarwyn/endercontainers/hologram/HologramManager.java index 11f3c52d..4fb52463 100644 --- a/plugin/src/main/java/fr/utarwyn/endercontainers/hologram/HologramManager.java +++ b/plugin/src/main/java/fr/utarwyn/endercontainers/hologram/HologramManager.java @@ -2,6 +2,8 @@ import fr.utarwyn.endercontainers.AbstractManager; import fr.utarwyn.endercontainers.Managers; +import fr.utarwyn.endercontainers.compatibility.ArmorStandAdapter; +import fr.utarwyn.endercontainers.compatibility.CompatibilityHelper; import fr.utarwyn.endercontainers.configuration.Files; import fr.utarwyn.endercontainers.configuration.LocaleKey; import fr.utarwyn.endercontainers.dependency.DependenciesManager; @@ -16,9 +18,11 @@ import org.bukkit.scheduler.BukkitTask; import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.Predicate; import java.util.logging.Level; /** @@ -38,6 +42,10 @@ public class HologramManager extends AbstractManager implements Runnable { * The BukkitTask object which manage the spawning/dispawning of holograms */ BukkitTask task; + /** + * The armor stand adapter + */ + ArmorStandAdapter armorStandAdapter; /** * The enderchest manager */ @@ -69,6 +77,7 @@ private static String generateNametagTitle(int chestCount) { public synchronized void load() { this.chestManager = Managers.get(EnderChestManager.class); this.dependenciesManager = Managers.get(DependenciesManager.class); + this.armorStandAdapter = CompatibilityHelper.createArmorStandAdapter(); this.holograms = new ConcurrentHashMap<>(); // Start the task only if the block nametag is enabled @@ -103,8 +112,15 @@ public void run() { .filter(player -> !disabledWorlds.contains(player.getWorld().getName())) .forEach(this::checkHologramStatus); - // Unused holograms can be cleared - this.holograms.entrySet().removeIf(entry -> !entry.getValue().isObserverOnline()); + // Unused holograms can be destroyed + Predicate> removePredicate = entry -> !entry.getValue().isObserverOnline(); + + this.holograms.entrySet() + .stream() + .filter(removePredicate) + .forEach(entry -> this.destroyHologram(entry.getValue())); + + this.holograms.entrySet().removeIf(removePredicate); } /** @@ -145,7 +161,8 @@ private void spawnHologram(PlayerContext context, Player observer, Block block) Location location = block.getLocation().clone().add(.5, -0.79D - Hologram.LINE_HEIGHT, .5); try { - this.holograms.put(observer.getUniqueId(), new Hologram(observer, title, location)); + int entityId = this.armorStandAdapter.spawnArmorStandFor(this.plugin, observer, location, title); + this.holograms.put(observer.getUniqueId(), new Hologram(observer, entityId)); } catch (HologramException e) { this.plugin.getLogger().log(Level.WARNING, "cannot create hologram instance", e); } @@ -158,7 +175,7 @@ private void spawnHologram(PlayerContext context, Player observer, Block block) */ private void destroyHologram(Hologram hologram) { try { - hologram.destroy(); + this.armorStandAdapter.destroyArmorStandFor(hologram.getObserver(), hologram.getEntityId()); } catch (HologramException e) { this.plugin.getLogger().log(Level.WARNING, "cannot remove hologram instance", e); } diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/TestHelper.java b/plugin/src/test/java/fr/utarwyn/endercontainers/TestHelper.java index 587480f7..cc63efc6 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/TestHelper.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/TestHelper.java @@ -24,16 +24,14 @@ import org.bukkit.plugin.PluginManager; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; +import org.mockito.stubbing.Answer; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Field; import java.net.URL; -import java.util.Collections; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; +import java.util.*; import java.util.logging.Logger; import static org.mockito.Mockito.*; @@ -71,7 +69,7 @@ public static synchronized void setUpServer() { if (!serverReady) { Server server = mock(ServerMock.class); - lenient().when(server.getVersion()).thenReturn("(MC: 1.16.5)"); + lenient().when(server.getVersion()).thenReturn("(MC: 1.21.4)"); lenient().when(server.getLogger()).thenReturn(Logger.getGlobal()); lenient().when(server.getScheduler()).thenReturn(mock(BukkitScheduler.class)); lenient().when(server.getPluginManager()).thenReturn(mock(PluginManager.class)); diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/compatibility/CompatibilityHelperTest.java b/plugin/src/test/java/fr/utarwyn/endercontainers/compatibility/CompatibilityHelperTest.java index 7f8a5500..19c61763 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/compatibility/CompatibilityHelperTest.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/compatibility/CompatibilityHelperTest.java @@ -1,6 +1,8 @@ package fr.utarwyn.endercontainers.compatibility; import fr.utarwyn.endercontainers.TestHelper; +import fr.utarwyn.endercontainers.compatibility.bukkit.BukkitArmorStandAdapter; +import fr.utarwyn.endercontainers.compatibility.nms.NMSArmorStandAdapter; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Sound; @@ -53,4 +55,20 @@ public void searchSound() { assertThat(CompatibilityHelper.searchSound("CHEST_OPEN", "BLOCK_CHEST_OPEN", "BLOCK_CHEST_CLOSE")).isEqualTo(Sound.BLOCK_CHEST_OPEN); } + @Test + public void createBukkitArmorStandAdapter() { + ServerVersion def = ServerVersion.get(); + TestHelper.overrideServerVersion(ServerVersion.V1_20); + assertThat(CompatibilityHelper.createArmorStandAdapter()).isInstanceOf(BukkitArmorStandAdapter.class); + TestHelper.overrideServerVersion(def); + } + + @Test + public void createNMSArmorStandAdapter() { + ServerVersion def = ServerVersion.get(); + TestHelper.overrideServerVersion(ServerVersion.V1_8); + assertThat(CompatibilityHelper.createArmorStandAdapter()).isInstanceOf(NMSArmorStandAdapter.class); + TestHelper.overrideServerVersion(def); + } + } diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/compatibility/bukkit/BukkitArmorStandAdapterTest.java b/plugin/src/test/java/fr/utarwyn/endercontainers/compatibility/bukkit/BukkitArmorStandAdapterTest.java new file mode 100644 index 00000000..18e91ebd --- /dev/null +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/compatibility/bukkit/BukkitArmorStandAdapterTest.java @@ -0,0 +1,80 @@ +package fr.utarwyn.endercontainers.compatibility.bukkit; + +import fr.utarwyn.endercontainers.TestHelper; +import fr.utarwyn.endercontainers.TestInitializationException; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class BukkitArmorStandAdapterTest { + + private static final String TEXT = "text"; + + private BukkitArmorStandAdapter armorStandAdapter; + + private Location location; + + @Mock + private ArmorStand armorStand; + + @Mock + private Player observer; + + @Mock + private World world; + + @BeforeAll + public static void setUpClass() { + TestHelper.setUpServer(); + } + + @BeforeEach + public void setUp() { + this.armorStandAdapter = new BukkitArmorStandAdapter(); + this.location = new Location(this.world, 0, 0, 0); + } + + @Test + public void spawnArmorStandFor() throws TestInitializationException { + doReturn(this.armorStand).when(this.world).spawnEntity(any(Location.class), eq(EntityType.ARMOR_STAND)); + + this.armorStandAdapter.spawnArmorStandFor(TestHelper.getPlugin(), this.observer, this.location, TEXT); + + verify(this.observer).showEntity(TestHelper.getPlugin(), this.armorStand); + + verify(this.armorStand).setCustomName(TEXT); + } + + @Test + public void destroyArmorStandFor() { + List entities = Arrays.asList(mock(Entity.class), mock(Entity.class)); + + doReturn(1).when(entities.get(0)).getEntityId(); + doReturn(2).when(entities.get(1)).getEntityId(); + doReturn(this.world).when(this.observer).getWorld(); + doReturn(entities).when(this.world).getEntities(); + + this.armorStandAdapter.destroyArmorStandFor(this.observer, 2); + + verify(entities.get(0), never()).remove(); + verify(entities.get(1)).remove(); + } + +} diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/compatibility/nms/NMSArmorStandAdapterTest.java b/plugin/src/test/java/fr/utarwyn/endercontainers/compatibility/nms/NMSArmorStandAdapterTest.java new file mode 100644 index 00000000..54c4d99d --- /dev/null +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/compatibility/nms/NMSArmorStandAdapterTest.java @@ -0,0 +1,85 @@ +package fr.utarwyn.endercontainers.compatibility.nms; + +import fr.utarwyn.endercontainers.TestHelper; +import fr.utarwyn.endercontainers.TestInitializationException; +import fr.utarwyn.endercontainers.hologram.HologramException; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class NMSArmorStandAdapterTest { + + private static final String TEXT = "text"; + + private NMSArmorStandAdapter armorStandAdapter; + + @Mock + private Player observer; + + @Mock + private Location location; + + @BeforeAll + public static void setUpClass() { + TestHelper.setUpServer(); + } + + @BeforeEach + public void setUp() { + this.armorStandAdapter = new NMSArmorStandAdapter(); + } + + @Test + public void spawnArmorStandFor() throws HologramException, ReflectiveOperationException, TestInitializationException { + this.armorStandAdapter.spawnArmorStandFor(TestHelper.getPlugin(), this.observer, this.location, TEXT); + verify(NMSHologramUtil.get()).spawnHologram(this.location, TEXT, this.observer); + } + + @Test + public void handleErrorWhenSpawningArmorStand() throws ReflectiveOperationException, TestInitializationException { + doThrow(ReflectiveOperationException.class).when(NMSHologramUtil.get()).spawnHologram(this.location, TEXT, this.observer); + try { + this.armorStandAdapter.spawnArmorStandFor(TestHelper.getPlugin(), this.observer, this.location, TEXT); + fail("should throw an exception"); + } catch (HologramException exception) { + assertThat(exception.getMessage()).isEqualTo("cannot spawn hologram entity"); + } + } + + @Test + public void destroyArmorStandForOnlineObserver() throws HologramException, ReflectiveOperationException { + doReturn(true).when(this.observer).isOnline(); + this.armorStandAdapter.destroyArmorStandFor(this.observer, 1); + verify(NMSHologramUtil.get()).destroyEntity(1, this.observer); + } + + @Test + public void doNotDestroyArmorStandForOfflineObserver() throws HologramException, ReflectiveOperationException { + doReturn(false).when(this.observer).isOnline(); + this.armorStandAdapter.destroyArmorStandFor(this.observer, 1); + verify(NMSHologramUtil.get(), never()).destroyEntity(1, this.observer); + } + + @Test + public void handleErrorWhenDestroyingArmorStandFor() throws ReflectiveOperationException { + doReturn(true).when(this.observer).isOnline(); + doThrow(ReflectiveOperationException.class).when(NMSHologramUtil.get()).destroyEntity(10, this.observer); + try { + this.armorStandAdapter.destroyArmorStandFor(this.observer, 10); + fail("should throw an exception"); + } catch (HologramException exception) { + assertThat(exception.getMessage()).isEqualTo("cannot destroy hologram entity"); + } + } + +} diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/hologram/HologramManagerTest.java b/plugin/src/test/java/fr/utarwyn/endercontainers/hologram/HologramManagerTest.java index e641f793..f5a70a83 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/hologram/HologramManagerTest.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/hologram/HologramManagerTest.java @@ -2,6 +2,7 @@ import fr.utarwyn.endercontainers.TestHelper; import fr.utarwyn.endercontainers.TestInitializationException; +import fr.utarwyn.endercontainers.compatibility.ArmorStandAdapter; import fr.utarwyn.endercontainers.dependency.DependenciesManager; import fr.utarwyn.endercontainers.dependency.exceptions.BlockChestOpeningException; import fr.utarwyn.endercontainers.enderchest.EnderChestManager; @@ -39,6 +40,9 @@ public class HologramManagerTest { @Mock private PlayerContext context; + @Mock + private ArmorStandAdapter armorStandAdapter; + @Mock private DependenciesManager dependenciesManager; @@ -92,15 +96,28 @@ public void unload() { } @Test - public void spawnHologram() throws BlockChestOpeningException { + public void spawnHologram() throws BlockChestOpeningException, TestInitializationException, HologramException { assertThat(this.manager.holograms).isNull(); + doReturn(5).when(this.armorStandAdapter).spawnArmorStandFor( + eq(TestHelper.getPlugin()), eq(this.observer), any(), anyString() + ); + // Spawn hologram when targeting enderchest + when(this.targetedBlock.getType()).thenReturn(Material.AIR); this.manager.load(); + // Spawn hologram when targeting enderchest + when(this.targetedBlock.getType()).thenReturn(Material.ENDER_CHEST); + this.manager.armorStandAdapter = this.armorStandAdapter; + this.manager.run(); + verify(this.dependenciesManager).validateBlockChestOpening(this.targetedBlock, this.observer); verify(this.enderChestManager).loadPlayerContext(eq(this.observer.getUniqueId()), any(Consumer.class)); assertThat(this.manager.holograms).isNotEmpty().hasSize(1); + assertThat( + this.manager.holograms.get(this.observer.getUniqueId()).getEntityId() + ).isEqualTo(5); } @Test @@ -120,15 +137,18 @@ public void disabledWorld() { } @Test - public void dispawnHologram() { + public void dispawnHologram() throws HologramException { // Spawn hologram after first load this.manager.load(); + this.manager.armorStandAdapter = this.armorStandAdapter; + assertThat(this.manager.holograms).isNotEmpty().hasSize(1); // Dispawn hologram when targeting AIR right after when(this.targetedBlock.getType()).thenReturn(Material.AIR); this.manager.run(); assertThat(this.manager.holograms).isEmpty(); + verify(this.armorStandAdapter).destroyArmorStandFor(this.observer, 0); } } diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/hologram/HologramTest.java b/plugin/src/test/java/fr/utarwyn/endercontainers/hologram/HologramTest.java index bdc59966..5477722c 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/hologram/HologramTest.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/hologram/HologramTest.java @@ -2,7 +2,6 @@ import fr.utarwyn.endercontainers.TestHelper; import fr.utarwyn.endercontainers.TestInitializationException; -import fr.utarwyn.endercontainers.compatibility.nms.NMSHologramUtil; import org.bukkit.Location; import org.bukkit.entity.Player; import org.junit.jupiter.api.BeforeAll; @@ -12,8 +11,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class HologramTest { @@ -34,23 +32,9 @@ public static void setUpClass() throws TestInitializationException { TestHelper.getPlugin(); } - @Test - public void spawn() throws HologramException, ReflectiveOperationException { - new Hologram(this.observer, TITLE, this.location); - verify(NMSHologramUtil.get()).spawnHologram(this.location, TITLE, this.observer); - } - - @Test - public void destroy() throws HologramException, ReflectiveOperationException { - when(NMSHologramUtil.get().spawnHologram(this.location, TITLE, this.observer)).thenReturn(ENTITY_ID); - Hologram h = new Hologram(this.observer, TITLE, this.location); - h.destroy(); - verify(NMSHologramUtil.get()).destroyEntity(ENTITY_ID, this.observer); - } - @Test public void observerOnline() throws HologramException { - Hologram hologram = new Hologram(this.observer, TITLE, this.location); + Hologram hologram = new Hologram(this.observer, ENTITY_ID); when(this.observer.isOnline()).thenReturn(true); assertThat(hologram.isObserverOnline()).isTrue(); @@ -58,30 +42,4 @@ public void observerOnline() throws HologramException { assertThat(hologram.isObserverOnline()).isFalse(); } - @Test - public void spawnError() throws ReflectiveOperationException { - when(NMSHologramUtil.get().spawnHologram(this.location, TITLE, this.observer)) - .thenThrow(ReflectiveOperationException.class); - - try { - new Hologram(this.observer, TITLE, this.location); - fail("spawn method must fail"); - } catch (HologramException ignored) { - } - } - - @Test - public void destroyError() throws HologramException, ReflectiveOperationException { - when(NMSHologramUtil.get().spawnHologram(this.location, TITLE, this.observer)).thenReturn(ENTITY_ID); - doThrow(ReflectiveOperationException.class).when(NMSHologramUtil.get()).destroyEntity(ENTITY_ID, this.observer); - - Hologram h = new Hologram(this.observer, TITLE, this.location); - - try { - h.destroy(); - fail("destroy method must fail"); - } catch (HologramException ignored) { - } - } - } diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/inventory/EnderChestInventoryTest.java b/plugin/src/test/java/fr/utarwyn/endercontainers/inventory/EnderChestInventoryTest.java index 2da7a9fa..42c11765 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/inventory/EnderChestInventoryTest.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/inventory/EnderChestInventoryTest.java @@ -90,7 +90,7 @@ public void updateContentsFromContainer() { ItemStack[] itemList = new ItemStack[27]; itemList[2] = new ItemStack(Material.ENDER_CHEST); itemList[8] = new ItemStack(Material.IRON_BLOCK, 2); - itemList[9] = new ItemStack(Material.GRASS, 20); + itemList[9] = new ItemStack(Material.DIRT, 20); // Reload items of the chest when(this.chest.getContents()).thenReturn(new ConcurrentHashMap() {{ @@ -112,7 +112,7 @@ public void updateContentsFromContainer() { assertThat(map.get(2)).isNotNull(); // still present in the container assertThat(map.get(8)).isNotNull(); // still present in the container assertThat(map.get(9)).isNotNull(); // still present in the container - assertThat(map.get(9).getType()).isEqualTo(Material.GRASS); + assertThat(map.get(9).getType()).isEqualTo(Material.DIRT); assertThat(map.get(9).getAmount()).isEqualTo(20); assertThat(map.get(35)).isNotNull(); // not in the container but out of bounds, so OK } diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/mock/EnchantmentMock.java b/plugin/src/test/java/fr/utarwyn/endercontainers/mock/EnchantmentMock.java index c3c74b17..36afc560 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/mock/EnchantmentMock.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/mock/EnchantmentMock.java @@ -53,5 +53,4 @@ public boolean canEnchantItem(ItemStack item) { public boolean conflictsWith(Enchantment other) { return other == this; } - } diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/mock/ItemFactoryMock.java b/plugin/src/test/java/fr/utarwyn/endercontainers/mock/ItemFactoryMock.java index 473f70ee..963b494a 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/mock/ItemFactoryMock.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/mock/ItemFactoryMock.java @@ -62,7 +62,7 @@ public ItemStack createItemStack(String input) throws IllegalArgumentException { } @Override - public Material updateMaterial(ItemMeta meta, Material material) + public Material updateMaterial(ItemMeta itemMeta, Material material) throws IllegalArgumentException { return material; } diff --git a/plugin/src/test/java/fr/utarwyn/endercontainers/storage/serialization/Base64ItemSerializerTest.java b/plugin/src/test/java/fr/utarwyn/endercontainers/storage/serialization/Base64ItemSerializerTest.java index 0a0ecee0..21685954 100644 --- a/plugin/src/test/java/fr/utarwyn/endercontainers/storage/serialization/Base64ItemSerializerTest.java +++ b/plugin/src/test/java/fr/utarwyn/endercontainers/storage/serialization/Base64ItemSerializerTest.java @@ -31,57 +31,49 @@ public void setUp() { public void serialize() throws IOException { ConcurrentMap map = new ConcurrentHashMap<>(); map.put(1, new ItemStack(Material.OAK_LOG, 10)); - map.put(17, new ItemStack(Material.GRASS, 20)); + map.put(17, new ItemStack(Material.DIRT, 20)); assertThat(this.serializer.serialize(map)) .isNotNull() .isBase64() - .isEqualTo("rO0ABXcIAAAAAgAAAAFzcgAab3JnLmJ1a2tpdC51dGlsLmlvLldyYXBwZXLyUEfs8RJv" + - "BQIAAUwAA21hcHQAD0xqYXZhL3V0aWwvTWFwO3hwc3IANWNvbS5nb29nbGUuY29tbW9uLmN" + - "vbGxlY3QuSW1tdXRhYmxlTWFwJFNlcmlhbGl6ZWRGb3JtAAAAAAAAAAACAAJMAARrZXlzdA" + - "ASTGphdmEvbGFuZy9PYmplY3Q7TAAGdmFsdWVzcQB+AAR4cHVyABNbTGphdmEubGFuZy5PY" + - "mplY3Q7kM5YnxBzKWwCAAB4cAAAAAV0AAI9PXQAAXZ0AAR0eXBldAAGYW1vdW50dAAEbWV0" + - "YXVxAH4ABgAAAAV0AB5vcmcuYnVra2l0LmludmVudG9yeS5JdGVtU3RhY2tzcgARamF2YS5" + - "sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJ" + - "UdC5TgiwIAAHhwAAAAAXQAB09BS19MT0dzcQB+AA8AAAAKc3EAfgAAc3EAfgADdXEAfgAGA" + - "AAABXEAfgAIdAAIZW5jaGFudHN0AAZkYW1hZ2V0AARsb3JldAALZGlzcGxheU5hbWV1cQB+" + - "AAYAAAAFdAAsZnIudXRhcnd5bi5lbmRlcmNvbnRhaW5lcnMubW9jay5JdGVtTWV0YU1vY2t" + - "zcgAlamF2YS51dGlsLkNvbGxlY3Rpb25zJFVubW9kaWZpYWJsZU1hcPGlqP509QdCAgABTA" + - "ABbXEAfgABeHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b" + - "3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAAdwgAAAAQAAAAAHhzcQB+AA8AAAAAc3IAE2phdmEu" + - "dXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNpemV4cAAAAAB3BAAAAAB4dAAAdwQAAAA" + - "Rc3EAfgAAc3EAfgADdXEAfgAGAAAABXEAfgAIcQB+AAlxAH4ACnEAfgALcQB+AAx1cQB+AA" + - "YAAAAFcQB+AA5xAH4AEXQABUdSQVNTc3EAfgAPAAAAFHNxAH4AAHNxAH4AA3VxAH4ABgAAA" + - "AVxAH4ACHEAfgAXcQB+ABhxAH4AGXEAfgAadXEAfgAGAAAABXEAfgAcc3EAfgAdc3EAfgAf" + - "P0AAAAAAAAB3CAAAABAAAAAAeHEAfgAhc3EAfgAiAAAAAHcEAAAAAHhxAH4AJA=="); + .isEqualTo( + "rO0ABXcIAAAAAgAAAAFzcgAab3JnLmJ1a2tpdC51dGlsLmlvLldyYXBwZXLyUEfs8RJvBQIAAUwAA21hcHQAD0" + + "xqYXZhL3V0aWwvTWFwO3hwc3IANWNvbS5nb29nbGUuY29tbW9uLmNvbGxlY3QuSW1tdXRhYmxlTWFwJFNlcmlhbGl6ZWRGb3Jt" + + "AAAAAAAAAAACAAJMAARrZXlzdAASTGphdmEvbGFuZy9PYmplY3Q7TAAGdmFsdWVzcQB+AAR4cHVyABNbTGphdmEubGFuZy5PYm" + + "plY3Q7kM5YnxBzKWwCAAB4cAAAAAV0AAI9PXQAAXZ0AAR0eXBldAAGYW1vdW50dAAEbWV0YXVxAH4ABgAAAAV0AB5vcmcuYnVr" + + "a2l0LmludmVudG9yeS5JdGVtU3RhY2tzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubG" + + "FuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXQAB09BS19MT0dzcQB+AA8AAAAKc3EAfgAAc3EAfgADdXEAfgAGAAAABXEAfgAI" + + "dAAIZW5jaGFudHN0AAZkYW1hZ2V0AARsb3JldAALZGlzcGxheU5hbWV1cQB+AAYAAAAFdAAsZnIudXRhcnd5bi5lbmRlcmNvbn" + + "RhaW5lcnMubW9jay5JdGVtTWV0YU1vY2tzcgAlamF2YS51dGlsLkNvbGxlY3Rpb25zJFVubW9kaWZpYWJsZU1hcPGlqP509QdC" + + "AgABTAABbXEAfgABeHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD" + + "9AAAAAAAAAdwgAAAAQAAAAAHhzcQB+AA8AAAAAc3IAE2phdmEudXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNpemV4cAAA" + + "AAB3BAAAAAB4dAAAdwQAAAARc3EAfgAAc3EAfgADdXEAfgAGAAAABXEAfgAIcQB+AAlxAH4ACnEAfgALcQB+AAx1cQB+AAYAAA" + + "AFcQB+AA5xAH4AEXQABERJUlRzcQB+AA8AAAAUc3EAfgAAc3EAfgADdXEAfgAGAAAABXEAfgAIcQB+ABdxAH4AGHEAfgAZcQB+" + + "ABp1cQB+AAYAAAAFcQB+ABxzcQB+AB1zcQB+AB8/QAAAAAAAAHcIAAAAEAAAAAB4cQB+ACFzcQB+ACIAAAAAdwQAAAAAeHEAfgAk" + ); } @Test public void deserialize() throws IOException { ConcurrentMap result = this.serializer.deserialize( - "rO0ABXcIAAAAAgAAAAZzcgAab3JnLmJ1a2tpdC51dGlsLmlvLldyYXBwZXLyUEfs8RJvBQIAAUwAA21" + - "hcHQAD0xqYXZhL3V0aWwvTWFwO3hwc3IANWNvbS5nb29nbGUuY29tbW9uLmNvbGxlY3QuSW" + - "1tdXRhYmxlTWFwJFNlcmlhbGl6ZWRGb3JtAAAAAAAAAAACAAJbAARrZXlzdAATW0xqYXZhL" + - "2xhbmcvT2JqZWN0O1sABnZhbHVlc3EAfgAEeHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DO" + - "WJ8QcylsAgAAeHAAAAAEdAACPT10AAF2dAAEdHlwZXQABG1ldGF1cQB+AAYAAAAEdAAeb3J" + - "nLmJ1a2tpdC5pbnZlbnRvcnkuSXRlbVN0YWNrc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpP" + - "eBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAF0A" + - "AVHUkFTU3NxAH4AAHNxAH4AA3VxAH4ABgAAAAVxAH4ACHQACGVuY2hhbnRzdAAGZGFtYWdl" + - "dAAEbG9yZXQAC2Rpc3BsYXlOYW1ldXEAfgAGAAAABXQALGZyLnV0YXJ3eW4uZW5kZXJjb25" + - "0YWluZXJzLm1vY2suSXRlbU1ldGFNb2Nrc3IAJWphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm" + - "1vZGlmaWFibGVNYXDxpaj+dPUHQgIAAUwAAW1xAH4AAXhwc3IAEWphdmEudXRpbC5IYXNoT" + - "WFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAAAHcIAAAA" + - "EAAAAAB4c3EAfgAOAAAAAHNyABNqYXZhLnV0aWwuQXJyYXlMaXN0eIHSHZnHYZ0DAAFJAAR" + - "zaXpleHAAAAAAdwQAAAAAeHQAAHcEAAAAF3NxAH4AAHNxAH4AA3VxAH4ABgAAAAVxAH4ACH" + - "EAfgAJcQB+AAp0AAZhbW91bnRxAH4AC3VxAH4ABgAAAAVxAH4ADXEAfgAQdAAHT0FLX0xPR" + - "3NxAH4ADgAAAAdzcQB+AABzcQB+AAN1cQB+AAYAAAAFcQB+AAhxAH4AFXEAfgAWcQB+ABdx" + - "AH4AGHVxAH4ABgAAAAVxAH4AGnNxAH4AG3NxAH4AHT9AAAAAAAAAdwgAAAAQAAAAAHhxAH4" + - "AH3NxAH4AIAAAAAB3BAAAAAB4cQB+ACI=" + "rO0ABXcIAAAAAgAAAAFzcgAab3JnLmJ1a2tpdC51dGlsLmlvLldyYXBwZXLyUEfs8RJvBQIAAUwAA21hcHQAD0" + + "xqYXZhL3V0aWwvTWFwO3hwc3IANWNvbS5nb29nbGUuY29tbW9uLmNvbGxlY3QuSW1tdXRhYmxlTWFwJFNlcmlhbGl6ZWRGb3Jt" + + "AAAAAAAAAAACAAJMAARrZXlzdAASTGphdmEvbGFuZy9PYmplY3Q7TAAGdmFsdWVzcQB+AAR4cHVyABNbTGphdmEubGFuZy5PYm" + + "plY3Q7kM5YnxBzKWwCAAB4cAAAAAV0AAI9PXQAAXZ0AAR0eXBldAAGYW1vdW50dAAEbWV0YXVxAH4ABgAAAAV0AB5vcmcuYnVr" + + "a2l0LmludmVudG9yeS5JdGVtU3RhY2tzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubG" + + "FuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXQAB09BS19MT0dzcQB+AA8AAAAKc3EAfgAAc3EAfgADdXEAfgAGAAAABXEAfgAI" + + "dAAIZW5jaGFudHN0AAZkYW1hZ2V0AARsb3JldAALZGlzcGxheU5hbWV1cQB+AAYAAAAFdAAsZnIudXRhcnd5bi5lbmRlcmNvbn" + + "RhaW5lcnMubW9jay5JdGVtTWV0YU1vY2tzcgAlamF2YS51dGlsLkNvbGxlY3Rpb25zJFVubW9kaWZpYWJsZU1hcPGlqP509QdC" + + "AgABTAABbXEAfgABeHBzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD" + + "9AAAAAAAAAdwgAAAAQAAAAAHhzcQB+AA8AAAAAc3IAE2phdmEudXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNpemV4cAAA" + + "AAB3BAAAAAB4dAAAdwQAAAARc3EAfgAAc3EAfgADdXEAfgAGAAAABXEAfgAIcQB+AAlxAH4ACnEAfgALcQB+AAx1cQB+AAYAAA" + + "AFcQB+AA5xAH4AEXQABERJUlRzcQB+AA8AAAAUc3EAfgAAc3EAfgADdXEAfgAGAAAABXEAfgAIcQB+ABdxAH4AGHEAfgAZcQB+" + + "ABp1cQB+AAYAAAAFcQB+ABxzcQB+AB1zcQB+AB8/QAAAAAAAAHcIAAAAEAAAAAB4cQB+ACFzcQB+ACIAAAAAdwQAAAAAeHEAfgAk" ); ConcurrentMap expected = new ConcurrentHashMap<>(); - expected.put(6, new ItemStack(Material.GRASS, 1)); - expected.put(23, new ItemStack(Material.OAK_LOG, 7)); + expected.put(1, new ItemStack(Material.OAK_LOG, 10)); + expected.put(17, new ItemStack(Material.DIRT, 20)); assertThat(result).isNotNull().isNotEmpty().hasSize(2) .containsExactlyEntriesOf(expected);