diff --git a/pom.xml b/pom.xml
index 39e5e60..4dcf82d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,13 +42,9 @@
-
- codemc-snapshots
- https://repo.codemc.org/repository/maven-snapshots
-
- codemc-releases
- https://repo.codemc.org/repository/maven-releases
+ bentoboxworld
+ https://repo.codemc.org/repository/bentoboxworld/
@@ -59,14 +55,14 @@
5.12.0
- 1.20.4-R0.1-SNAPSHOT
- 2.4.1-SNAPSHOT
+ 1.21.3-R0.1-SNAPSHOT
+ 2.7.1-SNAPSHOT
${build.version}-SNAPSHOT
-LOCAL
- 1.14.1
+ 1.15.0
BentoBoxWorld_addon-invSwitcher
bentobox-world
@@ -122,11 +118,7 @@
codemc
- https://repo.codemc.org/repository/maven-snapshots/
-
-
- codemc-public
- https://repo.codemc.org/repository/maven-public/
+ https://repo.codemc.org/repository/bentoboxworld/
diff --git a/src/main/java/com/wasteofplastic/invswitcher/Store.java b/src/main/java/com/wasteofplastic/invswitcher/Store.java
index 255551f..ef52765 100644
--- a/src/main/java/com/wasteofplastic/invswitcher/Store.java
+++ b/src/main/java/com/wasteofplastic/invswitcher/Store.java
@@ -125,9 +125,10 @@ public void getInventory(Player player, World world) {
private void setHeath(InventoryStorage store, Player player, String overworldName) {
// Health
- double health = store.getHealth().getOrDefault(overworldName, player.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
+ double health = store.getHealth().getOrDefault(overworldName,
+ player.getAttribute(Attribute.MAX_HEALTH).getValue());
- AttributeInstance attr = player.getAttribute(Attribute.GENERIC_MAX_HEALTH);
+ AttributeInstance attr = player.getAttribute(Attribute.MAX_HEALTH);
if (attr != null && health > attr.getValue()) {
health = attr.getValue();
}
diff --git a/src/main/resources/addon.yml b/src/main/resources/addon.yml
index 703d37f..aa99983 100755
--- a/src/main/resources/addon.yml
+++ b/src/main/resources/addon.yml
@@ -1,7 +1,7 @@
name: InvSwitcher
main: com.wasteofplastic.invswitcher.InvSwitcher
version: ${version}${build.number}
-api-version: 1.16
+api-version: 2.7.1
authors: tastybento
diff --git a/src/test/java/com/wasteofplastic/invswitcher/StoreTest.java b/src/test/java/com/wasteofplastic/invswitcher/StoreTest.java
index af2f4a1..b5bc708 100644
--- a/src/test/java/com/wasteofplastic/invswitcher/StoreTest.java
+++ b/src/test/java/com/wasteofplastic/invswitcher/StoreTest.java
@@ -41,6 +41,8 @@
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
+import com.wasteofplastic.invswitcher.mocks.ServerMocks;
+
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.Settings;
import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType;
@@ -73,6 +75,8 @@ public class StoreTest {
public void setUp()
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+ ServerMocks.newServer();
+
// BentoBox
BentoBox plugin = mock(BentoBox.class);
// Use reflection to set the private static field "instance" in BentoBox
@@ -124,7 +128,8 @@ public void setUp()
}
@After
- public void clear() throws IOException{
+ public void tearDown() throws IOException {
+ ServerMocks.unsetBukkitServer();
//remove any database data
File file = new File("database");
Path pathToBeDeleted = file.toPath();
diff --git a/src/test/java/com/wasteofplastic/invswitcher/mocks/ServerMocks.java b/src/test/java/com/wasteofplastic/invswitcher/mocks/ServerMocks.java
new file mode 100644
index 0000000..d169332
--- /dev/null
+++ b/src/test/java/com/wasteofplastic/invswitcher/mocks/ServerMocks.java
@@ -0,0 +1,118 @@
+package com.wasteofplastic.invswitcher.mocks;
+
+import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+import org.bukkit.Server;
+import org.bukkit.Tag;
+import org.bukkit.UnsafeValues;
+import org.eclipse.jdt.annotation.NonNull;
+
+public final class ServerMocks {
+
+ public static @NonNull Server newServer() {
+ Server mock = mock(Server.class);
+
+ Logger noOp = mock(Logger.class);
+ when(mock.getLogger()).thenReturn(noOp);
+ when(mock.isPrimaryThread()).thenReturn(true);
+
+ // Unsafe
+ UnsafeValues unsafe = mock(UnsafeValues.class);
+ when(mock.getUnsafe()).thenReturn(unsafe);
+
+ // Server must be available before tags can be mocked.
+ Bukkit.setServer(mock);
+
+ // Bukkit has a lot of static constants referencing registry values. To initialize those, the
+ // registries must be able to be fetched before the classes are touched.
+ Map, Object> registers = new HashMap<>();
+
+ doAnswer(invocationGetRegistry -> registers.computeIfAbsent(invocationGetRegistry.getArgument(0), clazz -> {
+ Registry> registry = mock(Registry.class);
+ Map cache = new HashMap<>();
+ doAnswer(invocationGetEntry -> {
+ NamespacedKey key = invocationGetEntry.getArgument(0);
+ // Some classes (like BlockType and ItemType) have extra generics that will be
+ // erased during runtime calls. To ensure accurate typing, grab the constant's field.
+ // This approach also allows us to return null for unsupported keys.
+ Class extends Keyed> constantClazz;
+ try {
+ //noinspection unchecked
+ constantClazz = (Class extends Keyed>) clazz
+ .getField(key.getKey().toUpperCase(Locale.ROOT).replace('.', '_')).getType();
+ } catch (ClassCastException e) {
+ throw new RuntimeException(e);
+ } catch (NoSuchFieldException e) {
+ return null;
+ }
+
+ return cache.computeIfAbsent(key, key1 -> {
+ Keyed keyed = mock(constantClazz);
+ doReturn(key).when(keyed).getKey();
+ return keyed;
+ });
+ }).when(registry).get(notNull());
+ return registry;
+ })).when(mock).getRegistry(notNull());
+
+ // Tags are dependent on registries, but use a different method.
+ // This will set up blank tags for each constant; all that needs to be done to render them
+ // functional is to re-mock Tag#getValues.
+ doAnswer(invocationGetTag -> {
+ Tag> tag = mock(Tag.class);
+ doReturn(invocationGetTag.getArgument(1)).when(tag).getKey();
+ doReturn(Set.of()).when(tag).getValues();
+ doAnswer(invocationIsTagged -> {
+ Keyed keyed = invocationIsTagged.getArgument(0);
+ Class> type = invocationGetTag.getArgument(2);
+ if (!type.isAssignableFrom(keyed.getClass())) {
+ return null;
+ }
+ // Since these are mocks, the exact instance might not be equal. Consider equal keys equal.
+ return tag.getValues().contains(keyed)
+ || tag.getValues().stream().anyMatch(value -> value.getKey().equals(keyed.getKey()));
+ }).when(tag).isTagged(notNull());
+ return tag;
+ }).when(mock).getTag(notNull(), notNull(), notNull());
+
+ // Once the server is all set up, touch BlockType and ItemType to initialize.
+ // This prevents issues when trying to access dependent methods from a Material constant.
+ try {
+ Class.forName("org.bukkit.inventory.ItemType");
+ Class.forName("org.bukkit.block.BlockType");
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ return mock;
+ }
+
+ public static void unsetBukkitServer() {
+ try {
+ Field server = Bukkit.class.getDeclaredField("server");
+ server.setAccessible(true);
+ server.set(null, null);
+ } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private ServerMocks() {
+ }
+
+}
\ No newline at end of file