diff --git a/pom.xml b/pom.xml
index 268f834..6a7a506 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,13 +41,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/
@@ -58,8 +54,8 @@
2.0.9
- 1.20.4-R0.1-SNAPSHOT
- 2.5.1-SNAPSHOT
+ 1.21.3-R0.1-SNAPSHOT
+ 2.7.1-SNAPSHOT
1.12.0
@@ -71,7 +67,7 @@
-LOCAL
- 2.16.1
+ 2.17.0
BentoBoxWorld_Level
bentobox-world
https://sonarcloud.io
@@ -127,6 +123,26 @@
+
+ spigot-repo
+ https://hub.spigotmc.org/nexus/content/repositories/snapshots
+
+
+ codemc
+ https://repo.codemc.org/repository/maven-snapshots/
+
+
+ codemc-repo
+ https://repo.codemc.org/repository/maven-public/
+
+
+ bentoboxworld
+ https://repo.codemc.org/repository/bentoboxworld/
+
+
+ jitpack.io
+ https://jitpack.io
+
bg-repo
@@ -142,22 +158,6 @@
songoda-plugins
https://repo.songoda.com/repository/minecraft-plugins/
-
- spigot-repo
- https://hub.spigotmc.org/nexus/content/repositories/snapshots
-
-
- codemc-repo
- https://repo.codemc.org/repository/maven-public/
-
-
- codemc-public
- https://repo.codemc.org/repository/maven-public/
-
-
- jitpack.io
- https://jitpack.io
-
@@ -235,7 +235,7 @@
dev.rosewood
rosestacker
- 1.3.0
+ 1.5.27
provided
@@ -274,17 +274,17 @@
org.apache.maven.plugins
maven-clean-plugin
- 3.1.0
+ 3.4.0
org.apache.maven.plugins
maven-resources-plugin
- 3.1.0
+ 3.3.1
org.apache.maven.plugins
maven-compiler-plugin
- 3.8.0
+ 3.13.0
${java.version}
@@ -292,7 +292,7 @@
org.apache.maven.plugins
maven-surefire-plugin
- 3.0.0-M5
+ 3.5.2
${argLine}
@@ -329,12 +329,12 @@
org.apache.maven.plugins
maven-jar-plugin
- 3.1.0
+ 3.4.2
org.apache.maven.plugins
maven-javadoc-plugin
- 3.0.1
+ 3.11.1
none
false
@@ -353,7 +353,7 @@
org.apache.maven.plugins
maven-source-plugin
- 3.0.1
+ 3.3.1
attach-sources
@@ -366,17 +366,17 @@
org.apache.maven.plugins
maven-install-plugin
- 2.5.2
+ 3.1.3
org.apache.maven.plugins
maven-deploy-plugin
- 2.8.2
+ 3.1.3
org.apache.maven.plugins
maven-shade-plugin
- 3.3.1-SNAPSHOT
+ 3.6.0
true
diff --git a/src/main/java/world/bentobox/level/PlaceholderManager.java b/src/main/java/world/bentobox/level/PlaceholderManager.java
index 886a1be..825ede9 100644
--- a/src/main/java/world/bentobox/level/PlaceholderManager.java
+++ b/src/main/java/world/bentobox/level/PlaceholderManager.java
@@ -2,6 +2,7 @@ package world.bentobox.level;
import java.util.Collections;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -111,7 +112,8 @@ public class PlaceholderManager {
rank = Math.max(1, Math.min(rank, Level.TEN));
if (weighted) {
return addon.getManager().getWeightedTopTen(world, Level.TEN).keySet().stream().skip(rank - 1L).limit(1L)
- .findFirst().map(Island::getOwner).map(addon.getPlayers()::getName).orElse("");
+ .findFirst().map(Island::getOwner).filter(Objects::nonNull).map(addon.getPlayers()::getName)
+ .orElse("");
}
@Nullable
UUID owner = addon.getManager().getTopTen(world, Level.TEN).keySet().stream().skip(rank - 1L).limit(1L)
diff --git a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java
index 025381a..29292fa 100644
--- a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java
+++ b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java
@@ -62,7 +62,12 @@ public class IslandLevelCalculator {
Material.LIME_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.ORANGE_SHULKER_BOX,
Material.PINK_SHULKER_BOX, Material.PURPLE_SHULKER_BOX, Material.RED_SHULKER_BOX, Material.RED_SHULKER_BOX,
Material.WHITE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX, Material.COMPOSTER, Material.BARREL,
- Material.DISPENSER, Material.DROPPER, Material.SMOKER, Material.BLAST_FURNACE);
+ Material.DISPENSER, Material.DROPPER, Material.SMOKER, Material.BLAST_FURNACE, Material.BUNDLE,
+ Material.RED_BUNDLE, Material.BLACK_BUNDLE, Material.BLUE_BUNDLE, Material.BROWN_BUNDLE,
+ Material.CYAN_BUNDLE, Material.GRAY_BUNDLE, Material.GREEN_BUNDLE, Material.LIGHT_BLUE_BUNDLE,
+ Material.LIGHT_GRAY_BUNDLE, Material.LIME_BUNDLE, Material.MAGENTA_BUNDLE, Material.ORANGE_BUNDLE,
+ Material.PINK_BUNDLE, Material.PURPLE_BUNDLE, Material.RED_BUNDLE, Material.WHITE_BUNDLE,
+ Material.YELLOW_BUNDLE);
private static final int CHUNKS_TO_SCAN = 100;
private final Level addon;
private final Queue> chunksToCheck;
@@ -469,6 +474,7 @@ public class IslandLevelCalculator {
// Scan chests
if (addon.getSettings().isIncludeChests() && CHESTS.contains(m)) {
+
chestBlocks.add(cp.chunk);
}
// Add the value of the block's material
diff --git a/src/main/java/world/bentobox/level/config/BlockConfig.java b/src/main/java/world/bentobox/level/config/BlockConfig.java
index b4d0025..b999ac3 100644
--- a/src/main/java/world/bentobox/level/config/BlockConfig.java
+++ b/src/main/java/world/bentobox/level/config/BlockConfig.java
@@ -13,6 +13,7 @@ import java.util.Objects;
import org.bukkit.Bukkit;
import org.bukkit.Material;
+import org.bukkit.Registry;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
@@ -96,7 +97,7 @@ public class BlockConfig {
ConfigurationSection blocks = Objects.requireNonNull(blockValues2.getConfigurationSection("blocks"));
Map bv = new EnumMap<>(Material.class);
// Update blockvalues to latest settings
- Arrays.stream(Material.values()).filter(Material::isBlock)
+ Registry.MATERIAL.stream().filter(Material::isBlock)
.filter(m -> !m.name().startsWith("LEGACY_"))
.filter(m -> !m.isAir())
.filter(m -> !m.equals(Material.WATER))
diff --git a/src/main/java/world/bentobox/level/panels/ValuePanel.java b/src/main/java/world/bentobox/level/panels/ValuePanel.java
index 5af78e4..df59b12 100644
--- a/src/main/java/world/bentobox/level/panels/ValuePanel.java
+++ b/src/main/java/world/bentobox/level/panels/ValuePanel.java
@@ -58,6 +58,7 @@ public class ValuePanel
this.activeFilter = Filter.NAME_ASC;
this.materialRecordList = Arrays.stream(Material.values()).
filter(Material::isBlock).
+ filter(Material::isItem). // Remove things like PITCHER_CROP
filter(m -> !m.name().startsWith("LEGACY_")).
filter(this.addon.getBlockConfig()::isNotHiddenBlock).
map(material ->
@@ -584,6 +585,7 @@ public class ValuePanel
return null;
}
+ @SuppressWarnings("deprecation")
int index = this.pageIndex * slot.amountMap().getOrDefault(BLOCK, 1) + slot.slot();
if (index >= this.elementList.size())
diff --git a/src/main/resources/addon.yml b/src/main/resources/addon.yml
index 0ce35f8..72d35e6 100755
--- a/src/main/resources/addon.yml
+++ b/src/main/resources/addon.yml
@@ -2,7 +2,7 @@ name: Level
main: world.bentobox.level.Level
version: ${version}${build.number}
icon: DIAMOND
-api-version: 2.5.1
+api-version: 2.7.1
authors: tastybento
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 42542f9..5876c35 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,7 +1,7 @@
name: BentoBox-Level
main: world.bentobox.level.LevelPladdon
version: ${project.version}${build.number}
-api-version: "1.19"
+api-version: "1.21"
authors: [tastybento]
contributors: ["The BentoBoxWorld Community"]
diff --git a/src/test/java/world/bentobox/level/LevelTest.java b/src/test/java/world/bentobox/level/LevelTest.java
index 7da610f..bce1d53 100644
--- a/src/test/java/world/bentobox/level/LevelTest.java
+++ b/src/test/java/world/bentobox/level/LevelTest.java
@@ -66,6 +66,7 @@ import world.bentobox.level.config.BlockConfig;
import world.bentobox.level.config.ConfigSettings;
import world.bentobox.level.listeners.IslandActivitiesListeners;
import world.bentobox.level.listeners.JoinLeaveListener;
+import world.bentobox.level.mocks.ServerMocks;
/**
* @author tastybento
@@ -147,6 +148,7 @@ public class LevelTest {
*/
@Before
public void setUp() throws Exception {
+ Server server = ServerMocks.newServer();
// Set up plugin
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
when(plugin.getLogger()).thenReturn(Logger.getAnonymousLogger());
@@ -186,7 +188,6 @@ public class LevelTest {
// Server
PowerMockito.mockStatic(Bukkit.class);
- Server server = mock(Server.class);
when(Bukkit.getServer()).thenReturn(server);
when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger());
when(Bukkit.getPluginManager()).thenReturn(mock(PluginManager.class));
@@ -252,6 +253,7 @@ public class LevelTest {
*/
@After
public void tearDown() throws Exception {
+ ServerMocks.unsetBukkitServer();
deleteAll(new File("database"));
}
diff --git a/src/test/java/world/bentobox/level/mocks/ServerMocks.java b/src/test/java/world/bentobox/level/mocks/ServerMocks.java
new file mode 100644
index 0000000..568b529
--- /dev/null
+++ b/src/test/java/world/bentobox/level/mocks/ServerMocks.java
@@ -0,0 +1,118 @@
+package world.bentobox.level.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