Compare commits

..

No commits in common. "develop" and "2.12.0" have entirely different histories.

26 changed files with 1576 additions and 1908 deletions

29
pom.xml
View File

@ -59,7 +59,7 @@
<powermock.version>2.0.9</powermock.version> <powermock.version>2.0.9</powermock.version>
<!-- More visible way how to change dependency versions --> <!-- More visible way how to change dependency versions -->
<spigot.version>1.20.4-R0.1-SNAPSHOT</spigot.version> <spigot.version>1.20.4-R0.1-SNAPSHOT</spigot.version>
<bentobox.version>2.5.1-SNAPSHOT</bentobox.version> <bentobox.version>2.0.0-SNAPSHOT</bentobox.version>
<!-- Warps addon version --> <!-- Warps addon version -->
<warps.version>1.12.0</warps.version> <warps.version>1.12.0</warps.version>
<!-- Visit addon version --> <!-- Visit addon version -->
@ -71,7 +71,7 @@
<!-- Do not change unless you want different name for local builds. --> <!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number> <build.number>-LOCAL</build.number>
<!-- This allows to change between versions. --> <!-- This allows to change between versions. -->
<build.version>2.16.2</build.version> <build.version>2.12.0</build.version>
<sonar.projectKey>BentoBoxWorld_Level</sonar.projectKey> <sonar.projectKey>BentoBoxWorld_Level</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization> <sonar.organization>bentobox-world</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url> <sonar.host.url>https://sonarcloud.io</sonar.host.url>
@ -129,8 +129,8 @@
<repositories> <repositories>
<!--Wild Stacker repo --> <!--Wild Stacker repo -->
<repository> <repository>
<id>bg-repo</id> <id>jitpack.io</id>
<url>https://repo.bg-software.com/repository/api/</url> <url>https://jitpack.io</url>
</repository> </repository>
<!-- RoseStacker repo --> <!-- RoseStacker repo -->
<repository> <repository>
@ -139,8 +139,8 @@
</repository> </repository>
<!-- UltimateStacker repo --> <!-- UltimateStacker repo -->
<repository> <repository>
<id>songoda-plugins</id> <id>songoda-public</id>
<url>https://repo.songoda.com/repository/minecraft-plugins/</url> <url>https://repo.songoda.com/repository/public/</url>
</repository> </repository>
<repository> <repository>
<id>spigot-repo</id> <id>spigot-repo</id>
@ -154,10 +154,6 @@
<id>codemc-public</id> <id>codemc-public</id>
<url>https://repo.codemc.org/repository/maven-public/</url> <url>https://repo.codemc.org/repository/maven-public/</url>
</repository> </repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
@ -212,9 +208,9 @@
</dependency> </dependency>
<!-- Wild Stacker dependency --> <!-- Wild Stacker dependency -->
<dependency> <dependency>
<groupId>com.bgsoftware</groupId> <groupId>com.github.OmerBenGera</groupId>
<artifactId>WildStackerAPI</artifactId> <artifactId>WildStackerAPI</artifactId>
<version>2023.3</version> <version>b18</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- Static analysis --> <!-- Static analysis -->
@ -235,14 +231,13 @@
<dependency> <dependency>
<groupId>dev.rosewood</groupId> <groupId>dev.rosewood</groupId>
<artifactId>rosestacker</artifactId> <artifactId>rosestacker</artifactId>
<version>1.5.27</version> <version>1.3.0</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- Ultimate Stacker dependency -->
<dependency> <dependency>
<groupId>com.craftaro</groupId> <groupId>com.songoda</groupId>
<artifactId>UltimateStacker-API</artifactId> <artifactId>UltimateStacker</artifactId>
<version>1.0.0-20240329.173606-35</version> <version>2.4.0</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -9,14 +9,8 @@ import world.bentobox.bentobox.api.addons.Pladdon;
* *
*/ */
public class LevelPladdon extends Pladdon { public class LevelPladdon extends Pladdon {
private Addon addon;
@Override @Override
public Addon getAddon() { public Addon getAddon() {
if (addon == null) { return new Level();
addon = new Level();
}
return addon;
} }
} }

View File

@ -2,7 +2,6 @@ package world.bentobox.level;
import java.math.BigInteger; import java.math.BigInteger;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.time.Instant;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -32,12 +31,19 @@ import world.bentobox.level.events.IslandPreLevelEvent;
import world.bentobox.level.objects.IslandLevels; import world.bentobox.level.objects.IslandLevels;
import world.bentobox.level.objects.LevelsData; import world.bentobox.level.objects.LevelsData;
import world.bentobox.level.objects.TopTenData; import world.bentobox.level.objects.TopTenData;
import world.bentobox.level.util.CachedData;
public class LevelsManager { public class LevelsManager {
private static final String INTOPTEN = "intopten"; private static final String INTOPTEN = "intopten";
private static final TreeMap<BigInteger, String> LEVELS = new TreeMap<>(); private static final TreeMap<BigInteger, String> LEVELS;
private static final BigInteger THOUSAND = BigInteger.valueOf(1000); private static final BigInteger THOUSAND = BigInteger.valueOf(1000);
static {
LEVELS = new TreeMap<>();
LEVELS.put(THOUSAND, "k");
LEVELS.put(THOUSAND.pow(2), "M");
LEVELS.put(THOUSAND.pow(3), "G");
LEVELS.put(THOUSAND.pow(4), "T");
}
private final Level addon; private final Level addon;
// Database handler for level data // Database handler for level data
@ -46,8 +52,6 @@ public class LevelsManager {
private final Map<String, IslandLevels> levelsCache; private final Map<String, IslandLevels> levelsCache;
// Top ten lists // Top ten lists
private final Map<World, TopTenData> topTenLists; private final Map<World, TopTenData> topTenLists;
// Cache for top tens
private Map<World, CachedData> cache = new HashMap<>();
public LevelsManager(Level addon) { public LevelsManager(Level addon) {
this.addon = addon; this.addon = addon;
@ -59,12 +63,6 @@ public class LevelsManager {
levelsCache = new HashMap<>(); levelsCache = new HashMap<>();
// Initialize top ten lists // Initialize top ten lists
topTenLists = new ConcurrentHashMap<>(); topTenLists = new ConcurrentHashMap<>();
// Units
LEVELS.put(THOUSAND, addon.getSettings().getKilo());
LEVELS.put(THOUSAND.pow(2), addon.getSettings().getMega());
LEVELS.put(THOUSAND.pow(3), addon.getSettings().getGiga());
LEVELS.put(THOUSAND.pow(4), addon.getSettings().getTera());
} }
public void migrate() { public void migrate() {
@ -107,8 +105,7 @@ public class LevelsManager {
* @return true if successful, false if not added * @return true if successful, false if not added
*/ */
private boolean addToTopTen(Island island, long lv) { private boolean addToTopTen(Island island, long lv) {
if (island != null && island.getOwner() != null && island.getWorld() != null if (island != null && island.getOwner() != null && hasTopTenPerm(island.getWorld(), island.getOwner())) {
&& hasTopTenPerm(island.getWorld(), island.getOwner())) {
topTenLists.computeIfAbsent(island.getWorld(), k -> new TopTenData(island.getWorld())).getTopTen() topTenLists.computeIfAbsent(island.getWorld(), k -> new TopTenData(island.getWorld())).getTopTen()
.put(island.getUniqueId(), lv); .put(island.getUniqueId(), lv);
return true; return true;
@ -215,32 +212,15 @@ public class LevelsManager {
* *
* @param world - world where the island is * @param world - world where the island is
* @param targetPlayer - target player UUID * @param targetPlayer - target player UUID
* @param ownerOnly - return level only if the target player is the owner
* @return Level of the player's island or zero if player is unknown or UUID is * @return Level of the player's island or zero if player is unknown or UUID is
* null * null
*/ */
public long getIslandLevel(@NonNull World world, @Nullable UUID targetPlayer) { public long getIslandLevel(@NonNull World world, @Nullable UUID targetPlayer) {
return getIslandLevel(world, targetPlayer, false);
}
/**
* Get level of island from cache for a player.
*
* @param world - world where the island is
* @param targetPlayer - target player UUID
* @param ownerOnly - return level only if the target player is the owner
* @return Level of the player's island or zero if player is unknown or UUID is
* null
*/
public long getIslandLevel(@NonNull World world, @Nullable UUID targetPlayer, boolean ownerOnly) {
if (targetPlayer == null) if (targetPlayer == null)
return 0L; return 0L;
// Get the island // Get the island
Island island = addon.getIslands().getIsland(world, targetPlayer); Island island = addon.getIslands().getIsland(world, targetPlayer);
if (island == null || island.getOwner() == null || (ownerOnly && !island.getOwner().equals(targetPlayer))) { return island == null ? 0L : getLevelsData(island).getLevel();
return 0L;
}
return getLevelsData(island).getLevel();
} }
/** /**
@ -345,6 +325,7 @@ public class LevelsManager {
// Return the unmodifiable map // Return the unmodifiable map
return Collections.unmodifiableMap(weightedTopTen); return Collections.unmodifiableMap(weightedTopTen);
} }
/** /**
@ -358,19 +339,7 @@ public class LevelsManager {
@NonNull @NonNull
public Map<String, Long> getTopTen(@NonNull World world, int size) { public Map<String, Long> getTopTen(@NonNull World world, int size) {
createAndCleanRankings(world); createAndCleanRankings(world);
CachedData cachedData = cache.get(world); // Return the sorted map
Instant now = Instant.now();
if (cachedData != null && cachedData.getLastUpdated().plusSeconds(1).isAfter(now)) {
return cachedData.getCachedMap();
} else {
Map<String, Long> newTopTen = calculateTopTen(world, size);
cache.put(world, new CachedData(newTopTen, now));
return newTopTen;
}
}
private Map<String, Long> calculateTopTen(@NonNull World world, int size) {
return Collections.unmodifiableMap(topTenLists.get(world).getTopTen().entrySet().stream() return Collections.unmodifiableMap(topTenLists.get(world).getTopTen().entrySet().stream()
.filter(l -> l.getValue() > 0).sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) .filter(l -> l.getValue() > 0).sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.limit(size) .limit(size)
@ -430,8 +399,7 @@ public class LevelsManager {
addon.log("Generating rankings"); addon.log("Generating rankings");
handler.loadObjects().forEach(il -> { handler.loadObjects().forEach(il -> {
if (il.getLevel() > 0) { if (il.getLevel() > 0) {
// Load islands, but don't cache them addon.getIslands().getIslandById(il.getUniqueId())
addon.getIslands().getIslandById(il.getUniqueId(), false)
.ifPresent(i -> this.addToTopTen(i, il.getLevel())); .ifPresent(i -> this.addToTopTen(i, il.getLevel()));
} }
}); });
@ -448,9 +416,8 @@ public class LevelsManager {
public void removeEntry(World world, String uuid) { public void removeEntry(World world, String uuid) {
if (topTenLists.containsKey(world)) { if (topTenLists.containsKey(world)) {
topTenLists.get(world).getTopTen().remove(uuid); topTenLists.get(world).getTopTen().remove(uuid);
// Invalidate the cache because of this deletion
cache.remove(world);
} }
} }
/** /**

View File

@ -41,9 +41,6 @@ public class PlaceholderManager {
// Island Level // Island Level
bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_level", bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_level",
user -> addon.getManager().getIslandLevelString(gm.getOverWorld(), user.getUniqueId())); user -> addon.getManager().getIslandLevelString(gm.getOverWorld(), user.getUniqueId()));
// Island Level owner only
bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_level_owner",
user -> String.valueOf(addon.getManager().getIslandLevel(gm.getOverWorld(), user.getUniqueId(), true)));
// Unformatted island level // Unformatted island level
bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_level_raw", bpm.registerPlaceholder(addon, gm.getDescription().getName().toLowerCase() + "_island_level_raw",
user -> String.valueOf(addon.getManager().getIslandLevel(gm.getOverWorld(), user.getUniqueId()))); user -> String.valueOf(addon.getManager().getIslandLevel(gm.getOverWorld(), user.getUniqueId())));

View File

@ -40,6 +40,9 @@ import com.bgsoftware.wildstacker.api.objects.StackedBarrel;
import com.google.common.collect.Multiset; import com.google.common.collect.Multiset;
import com.google.common.collect.Multiset.Entry; import com.google.common.collect.Multiset.Entry;
import com.google.common.collect.Multisets; import com.google.common.collect.Multisets;
import com.songoda.ultimatestacker.UltimateStacker;
import com.songoda.ultimatestacker.core.compatibility.CompatibleMaterial;
import com.songoda.ultimatestacker.stackable.block.BlockStack;
import dev.rosewood.rosestacker.api.RoseStackerAPI; import dev.rosewood.rosestacker.api.RoseStackerAPI;
import us.lynuxcraft.deadsilenceiv.advancedchests.AdvancedChestsAPI; import us.lynuxcraft.deadsilenceiv.advancedchests.AdvancedChestsAPI;
@ -446,33 +449,47 @@ public class IslandLevelCalculator {
// Only count to the highest block in the world for some optimization // Only count to the highest block in the world for some optimization
for (int y = cp.world.getMinHeight(); y < cp.world.getMaxHeight(); y++) { for (int y = cp.world.getMinHeight(); y < cp.world.getMaxHeight(); y++) {
BlockData blockData = cp.chunkSnapshot.getBlockData(x, y, z); BlockData blockData = cp.chunkSnapshot.getBlockData(x, y, z);
Material m = blockData.getMaterial();
boolean belowSeaLevel = seaHeight > 0 && y <= seaHeight; boolean belowSeaLevel = seaHeight > 0 && y <= seaHeight;
// Slabs can be doubled, so check them twice // Slabs can be doubled, so check them twice
if (Tag.SLABS.isTagged(m)) { if (Tag.SLABS.isTagged(blockData.getMaterial())) {
Slab slab = (Slab) blockData; Slab slab = (Slab) blockData;
if (slab.getType().equals(Slab.Type.DOUBLE)) { if (slab.getType().equals(Slab.Type.DOUBLE)) {
checkBlock(m, belowSeaLevel); checkBlock(blockData.getMaterial(), belowSeaLevel);
} }
} }
// Hook for Wild Stackers (Blocks and Spawners Only) - this has to use the real // Hook for Wild Stackers (Blocks and Spawners Only) - this has to use the real
// chunk // chunk
if (addon.isStackersEnabled() && (m.equals(Material.CAULDRON) || m.equals(Material.SPAWNER))) { if (addon.isStackersEnabled() && (blockData.getMaterial().equals(Material.CAULDRON)
|| blockData.getMaterial().equals(Material.SPAWNER))) {
stackedBlocks.add(new Location(cp.world, (double) x + cp.chunkSnapshot.getX() * 16, y, stackedBlocks.add(new Location(cp.world, (double) x + cp.chunkSnapshot.getX() * 16, y,
(double) z + cp.chunkSnapshot.getZ() * 16)); (double) z + cp.chunkSnapshot.getZ() * 16));
} }
if (addon.isUltimateStackerEnabled() && !m.isAir()) { Block block = cp.chunk.getBlock(x, y, z);
Location l = new Location(cp.chunk.getWorld(), x, y, z);
UltimateStackerCalc.addStackers(m, l, results, belowSeaLevel, limitCount(m)); if (addon.isUltimateStackerEnabled()) {
if (!blockData.getMaterial().equals(Material.AIR)) {
BlockStack stack = UltimateStacker.getInstance().getBlockStackManager().getBlock(block,
CompatibleMaterial.getMaterial(block));
if (stack != null) {
int value = limitCount(blockData.getMaterial());
if (belowSeaLevel) {
results.underWaterBlockCount.addAndGet((long) stack.getAmount() * value);
results.uwCount.add(blockData.getMaterial());
} else {
results.rawBlockCount.addAndGet((long) stack.getAmount() * value);
results.mdCount.add(blockData.getMaterial());
}
}
}
} }
// Scan chests // Scan chests
if (addon.getSettings().isIncludeChests() && CHESTS.contains(m)) { if (addon.getSettings().isIncludeChests() && CHESTS.contains(blockData.getMaterial())) {
chestBlocks.add(cp.chunk); chestBlocks.add(cp.chunk);
} }
// Add the value of the block's material // Add the value of the block's material
checkBlock(m, belowSeaLevel); checkBlock(blockData.getMaterial(), belowSeaLevel);
} }
} }
} }
@ -608,32 +625,41 @@ public class IslandLevelCalculator {
// Done // Done
pipeliner.getInProcessQueue().remove(this); pipeliner.getInProcessQueue().remove(this);
// Chunk finished // Chunk finished
// This was the last chunk. Handle stacked blocks, then chests and exit // This was the last chunk
handleStackedBlocks().thenCompose(v -> handleChests()).thenRun(() -> { handleStackedBlocks();
handleChests();
long checkTime = System.currentTimeMillis();
finishTask = Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> {
// Check every half second if all the chests and stacks have been cleared
if ((chestBlocks.isEmpty() && stackedBlocks.isEmpty())
|| System.currentTimeMillis() - checkTime > MAX_AMOUNT) {
this.tidyUp(); this.tidyUp();
this.getR().complete(getResults()); this.getR().complete(getResults());
}); finishTask.cancel();
}
}, 0, 10L);
} }
}); });
} }
private CompletableFuture<Void> handleChests() { private void handleChests() {
List<CompletableFuture<Void>> futures = new ArrayList<>(); Iterator<Chunk> it = chestBlocks.iterator();
for (Chunk v : chestBlocks) { while (it.hasNext()) {
CompletableFuture<Void> future = Util.getChunkAtAsync(v.getWorld(), v.getX(), v.getZ()).thenAccept(c -> { Chunk v = it.next();
Util.getChunkAtAsync(v.getWorld(), v.getX(), v.getZ()).thenAccept(c -> {
scanChests(c); scanChests(c);
it.remove();
}); });
futures.add(future);
} }
// Return a CompletableFuture that completes when all futures are done
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
} }
private CompletableFuture<Void> handleStackedBlocks() { private void handleStackedBlocks() {
// Deal with any stacked blocks // Deal with any stacked blocks
List<CompletableFuture<Void>> futures = new ArrayList<>(); Iterator<Location> it = stackedBlocks.iterator();
for (Location v : stackedBlocks) { while (it.hasNext()) {
CompletableFuture<Void> future = Util.getChunkAtAsync(v).thenAccept(c -> { Location v = it.next();
Util.getChunkAtAsync(v).thenAccept(c -> {
Block stackedBlock = v.getBlock(); Block stackedBlock = v.getBlock();
boolean belowSeaLevel = seaHeight > 0 && v.getBlockY() <= seaHeight; boolean belowSeaLevel = seaHeight > 0 && v.getBlockY() <= seaHeight;
if (WildStackerAPI.getWildStacker().getSystemManager().isStackedBarrel(stackedBlock)) { if (WildStackerAPI.getWildStacker().getSystemManager().isStackedBarrel(stackedBlock)) {
@ -648,12 +674,8 @@ public class IslandLevelCalculator {
checkBlock(stackedBlock.getType(), belowSeaLevel); checkBlock(stackedBlock.getType(), belowSeaLevel);
} }
} }
it.remove();
}); });
futures.add(future);
} }
// Return a CompletableFuture that completes when all futures are done
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
} }
} }

View File

@ -1,29 +0,0 @@
package world.bentobox.level.calculators;
import org.bukkit.Location;
import org.bukkit.Material;
import com.craftaro.ultimatestacker.api.UltimateStackerApi;
import com.craftaro.ultimatestacker.api.utils.Stackable;
import world.bentobox.bentobox.BentoBox;
/**
* Isolates UltimateStacker imports so that they are only loaded if the plugin exists
*/
public class UltimateStackerCalc {
public static void addStackers(Material material, Location location, Results results, boolean belowSeaLevel,
int value) {
Stackable stack = UltimateStackerApi.getBlockStackManager().getBlock(location);
if (stack != null) {
if (belowSeaLevel) {
results.underWaterBlockCount.addAndGet((long) stack.getAmount() * value);
results.uwCount.add(material);
} else {
results.rawBlockCount.addAndGet((long) stack.getAmount() * value);
results.mdCount.add(material);
}
}
}
}

View File

@ -46,20 +46,10 @@ public class AdminSetInitialLevelCommand extends CompositeCommand {
@Override @Override
public boolean execute(User user, String label, List<String> args) { public boolean execute(User user, String label, List<String> args) {
long initialLevel = addon.getManager().getInitialLevel(island); String initialLevel = String.valueOf(addon.getManager().getInitialLevel(island));
long lv = 0; long lv = Long.parseLong(args.get(1));
if (args.get(1).startsWith("+")) {
String change = args.get(1).substring(1);
lv = initialLevel + Long.parseLong(change);
} else if (args.get(1).startsWith("-")) {
String change = args.get(1).substring(1);
lv = initialLevel - Long.parseLong(change);
} else {
lv = Long.parseLong(args.get(1));
}
addon.getManager().setInitialIslandLevel(island, lv); addon.getManager().setInitialIslandLevel(island, lv);
user.sendMessage("admin.level.sethandicap.changed", TextVariables.NUMBER, String.valueOf(initialLevel), user.sendMessage("admin.level.sethandicap.changed", TextVariables.NUMBER, initialLevel, "[new_number]", String.valueOf(lv));
"[new_number]", String.valueOf(lv));
return true; return true;
} }
@ -74,21 +64,11 @@ public class AdminSetInitialLevelCommand extends CompositeCommand {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false; return false;
} }
// Check if this is a add or remove
if (args.get(1).startsWith("+") || args.get(1).startsWith("-")) {
String change = args.get(1).substring(1);
if (!Util.isInteger(change, true)) {
user.sendMessage("admin.level.sethandicap.invalid-level");
return false;
}
// Value is okay
} else {
// Check value // Check value
if (!Util.isInteger(args.get(1), true)) { if (!Util.isInteger(args.get(1), true)) {
user.sendMessage("admin.level.sethandicap.invalid-level"); user.sendMessage("admin.level.sethandicap.invalid-level");
return false; return false;
} }
}
// Check island // Check island
island = getAddon().getIslands().getIsland(getWorld(), targetUUID); island = getAddon().getIslands().getIsland(getWorld(), targetUUID);
if (island == null) { if (island == null) {

View File

@ -111,11 +111,7 @@ public class IslandLevelCommand extends CompositeCommand {
} }
// Send player how many points are required to reach next island level // Send player how many points are required to reach next island level
if (results.getPointsToNextLevel() >= 0) { if (results.getPointsToNextLevel() >= 0) {
user.sendMessage("island.level.required-points-to-next-level", user.sendMessage("island.level.required-points-to-next-level", "[points]", String.valueOf(results.getPointsToNextLevel()));
"[points]", String.valueOf(results.getPointsToNextLevel()),
"[progress]", String.valueOf(this.addon.getSettings().getLevelCost()-results.getPointsToNextLevel()),
"[levelcost]", String.valueOf(this.addon.getSettings().getLevelCost())
);
} }
// Tell other team members // Tell other team members
if (results.getLevel() != oldLevel) { if (results.getLevel() != oldLevel) {

View File

@ -8,14 +8,11 @@ import java.util.Optional;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.PlayerInventory;
import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.Util;
import world.bentobox.level.Level; import world.bentobox.level.Level;
import world.bentobox.level.objects.IslandLevels;
import world.bentobox.level.panels.ValuePanel; import world.bentobox.level.panels.ValuePanel;
import world.bentobox.level.util.Utils; import world.bentobox.level.util.Utils;
@ -115,19 +112,6 @@ public class IslandValueCommand extends CompositeCommand
"[value]", (underWater * value) + ""), "[value]", (underWater * value) + ""),
MATERIAL, Utils.prettifyObject(material, user)); MATERIAL, Utils.prettifyObject(material, user));
} }
// Show how many have been placed and how many are allowed
@NonNull
IslandLevels lvData = this.addon.getManager()
.getLevelsData(getIslands().getPrimaryIsland(getWorld(), user.getUniqueId()));
int count = lvData.getMdCount().getOrDefault(material, 0) + lvData.getUwCount().getOrDefault(material, 0);
user.sendMessage("level.conversations.you-have", TextVariables.NUMBER,
String.valueOf(count));
int limit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(material, -1);
if (limit > 0) {
user.sendMessage("level.conversations.you-can-place", TextVariables.NUMBER,
String.valueOf(limit));
}
} }
else else
{ {

View File

@ -2,12 +2,9 @@ package world.bentobox.level.config;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -29,7 +26,6 @@ public class BlockConfig {
private Map<Material, Integer> blockLimits = new EnumMap<>(Material.class); private Map<Material, Integer> blockLimits = new EnumMap<>(Material.class);
private Map<Material, Integer> blockValues = new EnumMap<>(Material.class); private Map<Material, Integer> blockValues = new EnumMap<>(Material.class);
private final Map<World, Map<Material, Integer>> worldBlockValues = new HashMap<>(); private final Map<World, Map<Material, Integer>> worldBlockValues = new HashMap<>();
private final List<Material> hiddenBlocks;
private Level addon; private Level addon;
/** /**
@ -53,16 +49,6 @@ public class BlockConfig {
if (blockValues.isConfigurationSection("worlds")) { if (blockValues.isConfigurationSection("worlds")) {
loadWorlds(blockValues); loadWorlds(blockValues);
} }
// Hidden
hiddenBlocks = blockValues.getStringList("hidden-blocks").stream().map(name -> {
try {
return Material.valueOf(name.toUpperCase(Locale.ENGLISH));
} catch (Exception e) {
return null;
}
}).filter(Objects::nonNull).toList();
// All done // All done
blockValues.save(file); blockValues.save(file);
} }
@ -173,22 +159,5 @@ public class BlockConfig {
return null; return null;
} }
/**
* Return true if the block should be hidden
* @param m block material
* @return true if hidden
*/
public boolean isHiddenBlock(Material m) {
return hiddenBlocks.contains(m);
}
/**
* Return true if the block should not be hidden
* @param m block material
* @return false if hidden
*/
public boolean isNotHiddenBlock(Material m) {
return !hiddenBlocks.contains(m);
}
} }

View File

@ -3,7 +3,6 @@ package world.bentobox.level.config;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.configuration.ConfigComment; import world.bentobox.bentobox.api.configuration.ConfigComment;
@ -121,17 +120,6 @@ public class ConfigSettings implements ConfigObject {
@ConfigComment("Shows large level values rounded down, e.g., 10,345 -> 10k") @ConfigComment("Shows large level values rounded down, e.g., 10,345 -> 10k")
@ConfigEntry(path = "shorthand") @ConfigEntry(path = "shorthand")
private boolean shorthand = false; private boolean shorthand = false;
@ConfigComment("Shorthand units")
@ConfigEntry(path = "units.kilo")
private String kilo = "k";
@ConfigEntry(path = "units.mega")
private String mega = "M";
@ConfigEntry(path = "units.giga")
private String giga = "G";
@ConfigEntry(path = "units.tera")
private String tera = "T";
@ConfigComment("") @ConfigComment("")
@ConfigComment("Include Shulker Box content in chests in level calculations.") @ConfigComment("Include Shulker Box content in chests in level calculations.")
@ConfigComment("Will count blocks in Shulker Boxes inside of chests.") @ConfigComment("Will count blocks in Shulker Boxes inside of chests.")
@ -431,60 +419,4 @@ public class ConfigSettings implements ConfigObject {
public void setDisabledPluginHooks(List<String> disabledPluginHooks) { public void setDisabledPluginHooks(List<String> disabledPluginHooks) {
this.disabledPluginHooks = disabledPluginHooks; this.disabledPluginHooks = disabledPluginHooks;
} }
/**
* @return the kilo
*/
public String getKilo() {
return Objects.requireNonNullElse(kilo, "k");
}
/**
* @param kilo the kilo to set
*/
public void setKilo(String kilo) {
this.kilo = kilo;
}
/**
* @return the mega
*/
public String getMega() {
return Objects.requireNonNullElse(mega, "M");
}
/**
* @param mega the mega to set
*/
public void setMega(String mega) {
this.mega = mega;
}
/**
* @return the giga
*/
public String getGiga() {
return Objects.requireNonNullElse(giga, "G");
}
/**
* @param giga the giga to set
*/
public void setGiga(String giga) {
this.giga = giga;
}
/**
* @return the tera
*/
public String getTera() {
return Objects.requireNonNullElse(tera, "T");
}
/**
* @param tera the tera to set
*/
public void setTera(String tera) {
this.tera = tera;
}
} }

View File

@ -62,7 +62,7 @@ public class IslandLevels implements DataObject {
private Map<Material, Integer> uwCount; private Map<Material, Integer> uwCount;
/** /**
* MaterialData count - count of all blocks excluding under water * MaterialData count - count of all blocks
*/ */
@Expose @Expose
private Map<Material, Integer> mdCount; private Map<Material, Integer> mdCount;
@ -162,7 +162,6 @@ public class IslandLevels implements DataObject {
} }
/** /**
* The count of underwater blocks
* @return the uwCount * @return the uwCount
*/ */
public Map<Material, Integer> getUwCount() { public Map<Material, Integer> getUwCount() {
@ -170,7 +169,6 @@ public class IslandLevels implements DataObject {
} }
/** /**
* Underwater blocks
* @param uwCount the uwCount to set * @param uwCount the uwCount to set
*/ */
public void setUwCount(Map<Material, Integer> uwCount) { public void setUwCount(Map<Material, Integer> uwCount) {
@ -178,7 +176,6 @@ public class IslandLevels implements DataObject {
} }
/** /**
* All blocks count except for underwater blocks
* @return the mdCount * @return the mdCount
*/ */
public Map<Material, Integer> getMdCount() { public Map<Material, Integer> getMdCount() {
@ -186,7 +183,6 @@ public class IslandLevels implements DataObject {
} }
/** /**
* All blocks except for underwater blocks
* @param mdCount the mdCount to set * @param mdCount the mdCount to set
*/ */
public void setMdCount(Map<Material, Integer> mdCount) { public void setMdCount(Map<Material, Integer> mdCount) {

View File

@ -59,7 +59,7 @@ public class DetailsPanel {
} }
// By default no-filters are active. // By default no-filters are active.
this.activeTab = Tab.VALUE_BLOCKS; this.activeTab = Tab.ALL_BLOCKS;
this.activeFilter = Filter.NAME; this.activeFilter = Filter.NAME;
this.materialCountList = new ArrayList<>(Material.values().length); this.materialCountList = new ArrayList<>(Material.values().length);
@ -111,31 +111,6 @@ public class DetailsPanel {
this.materialCountList.clear(); this.materialCountList.clear();
switch (this.activeTab) { switch (this.activeTab) {
case VALUE_BLOCKS -> {
Map<Material, Integer> materialCountMap = new EnumMap<>(Material.class);
materialCountMap.putAll(this.levelsData.getMdCount());
// Add underwater blocks.
this.levelsData.getUwCount().forEach((material, count) -> materialCountMap.put(material,
materialCountMap.computeIfAbsent(material, key -> 0) + count));
// Remove zero value blocks
materialCountMap.entrySet().removeIf(en -> {
Integer value = this.addon.getBlockConfig().getValue(world, en.getKey());
return value == null || value == 0;
});
// Remove any hidden blocks
materialCountMap.keySet().removeIf(this.addon.getBlockConfig()::isHiddenBlock);
materialCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey())).forEachOrdered(entry -> {
if (entry.getValue() > 0) {
this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()));
}
});
}
case ALL_BLOCKS -> { case ALL_BLOCKS -> {
Map<Material, Integer> materialCountMap = new EnumMap<>(Material.class); Map<Material, Integer> materialCountMap = new EnumMap<>(Material.class);
@ -145,24 +120,16 @@ public class DetailsPanel {
this.levelsData.getUwCount().forEach((material, count) -> materialCountMap.put(material, this.levelsData.getUwCount().forEach((material, count) -> materialCountMap.put(material,
materialCountMap.computeIfAbsent(material, key -> 0) + count)); materialCountMap.computeIfAbsent(material, key -> 0) + count));
// Remove any hidden blocks
materialCountMap.keySet().removeIf(this.addon.getBlockConfig()::isHiddenBlock);
materialCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey())) materialCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey()))
.forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())));
} }
case ABOVE_SEA_LEVEL -> this.levelsData.getMdCount().entrySet().stream() case ABOVE_SEA_LEVEL -> this.levelsData.getMdCount().entrySet().stream().sorted((Map.Entry.comparingByKey()))
.filter(en -> this.addon.getBlockConfig().isNotHiddenBlock(en.getKey()))
.sorted((Map.Entry.comparingByKey()))
.forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())));
case UNDERWATER -> this.levelsData.getUwCount().entrySet().stream() case UNDERWATER -> this.levelsData.getUwCount().entrySet().stream().sorted((Map.Entry.comparingByKey()))
.filter(en -> this.addon.getBlockConfig().isNotHiddenBlock(en.getKey()))
.sorted((Map.Entry.comparingByKey()))
.forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())));
case SPAWNER -> { case SPAWNER -> {
if (this.addon.getBlockConfig().isNotHiddenBlock(Material.SPAWNER)) {
int aboveWater = this.levelsData.getMdCount().getOrDefault(Material.SPAWNER, 0); int aboveWater = this.levelsData.getMdCount().getOrDefault(Material.SPAWNER, 0);
int underWater = this.levelsData.getUwCount().getOrDefault(Material.SPAWNER, 0); int underWater = this.levelsData.getUwCount().getOrDefault(Material.SPAWNER, 0);
@ -170,7 +137,6 @@ public class DetailsPanel {
this.materialCountList.add(new Pair<>(Material.SPAWNER, underWater + aboveWater)); this.materialCountList.add(new Pair<>(Material.SPAWNER, underWater + aboveWater));
} }
} }
}
Comparator<Pair<Material, Integer>> sorter; Comparator<Pair<Material, Integer>> sorter;
@ -254,7 +220,7 @@ public class DetailsPanel {
builder.description(this.user.getTranslation(this.world, template.description())); builder.description(this.user.getTranslation(this.world, template.description()));
} }
Tab tab = Enums.getIfPresent(Tab.class, String.valueOf(template.dataMap().get("tab"))).or(Tab.VALUE_BLOCKS); Tab tab = Enums.getIfPresent(Tab.class, String.valueOf(template.dataMap().get("tab"))).or(Tab.ALL_BLOCKS);
// Get only possible actions, by removing all inactive ones. // Get only possible actions, by removing all inactive ones.
List<ItemTemplateRecord.ActionRecords> activeActions = new ArrayList<>(template.actions()); List<ItemTemplateRecord.ActionRecords> activeActions = new ArrayList<>(template.actions());
@ -638,10 +604,6 @@ public class DetailsPanel {
* All block Tab * All block Tab
*/ */
ALL_BLOCKS, ALL_BLOCKS,
/**
* Blocks that have value
*/
VALUE_BLOCKS,
/** /**
* Above Sea level Tab. * Above Sea level Tab.
*/ */

View File

@ -58,9 +58,7 @@ public class ValuePanel
this.activeFilter = Filter.NAME_ASC; this.activeFilter = Filter.NAME_ASC;
this.materialRecordList = Arrays.stream(Material.values()). this.materialRecordList = Arrays.stream(Material.values()).
filter(Material::isBlock). filter(Material::isBlock).
filter(Material::isItem). // Remove things like PITCHER_CROP
filter(m -> !m.name().startsWith("LEGACY_")). filter(m -> !m.name().startsWith("LEGACY_")).
filter(this.addon.getBlockConfig()::isNotHiddenBlock).
map(material -> map(material ->
{ {
Integer value = this.addon.getBlockConfig().getValue(this.world, material); Integer value = this.addon.getBlockConfig().getValue(this.world, material);
@ -585,7 +583,6 @@ public class ValuePanel
return null; return null;
} }
@SuppressWarnings("deprecation")
int index = this.pageIndex * slot.amountMap().getOrDefault(BLOCK, 1) + slot.slot(); int index = this.pageIndex * slot.amountMap().getOrDefault(BLOCK, 1) + slot.slot();
if (index >= this.elementList.size()) if (index >= this.elementList.size())

View File

@ -1,30 +0,0 @@
package world.bentobox.level.util;
import java.time.Instant;
import java.util.Map;
/**
* Cache for top tens
*/
public class CachedData {
private Map<String, Long> cachedMap;
private Instant lastUpdated;
public CachedData(Map<String, Long> cachedMap, Instant lastUpdated) {
this.cachedMap = cachedMap;
this.lastUpdated = lastUpdated;
}
public Map<String, Long> getCachedMap() {
return cachedMap;
}
public Instant getLastUpdated() {
return lastUpdated;
}
public void updateCache(Map<String, Long> newMap, Instant newUpdateTime) {
this.cachedMap = newMap;
this.lastUpdated = newUpdateTime;
}
}

View File

@ -2,7 +2,7 @@ name: Level
main: world.bentobox.level.Level main: world.bentobox.level.Level
version: ${version}${build.number} version: ${version}${build.number}
icon: DIAMOND icon: DIAMOND
api-version: 2.5.1 api-version: 1.16.5
authors: tastybento authors: tastybento

View File

@ -10,10 +10,6 @@
limits: limits:
COBBLESTONE: 10000 COBBLESTONE: 10000
NETHERRACK: 1000 NETHERRACK: 1000
# These blocks will never be shown in the GUI even if they have value
hidden-blocks:
- BEDROCK
- AIR
blocks: blocks:
ACACIA_BUTTON: 1 ACACIA_BUTTON: 1
ACACIA_DOOR: 2 ACACIA_DOOR: 2

View File

@ -19,7 +19,7 @@ calculation-timeout: 5
# #
# Zero island levels on new island or island reset # Zero island levels on new island or island reset
# If true, Level will calculate the starter island's level and remove it from any future level calculations. # If true, Level will calculate the starter island's level and remove it from any future level calculations.
# If false, the player's starter island blocks will count towards their level. # If this is false, the player's starter island blocks will count towards their level.
# This will reduce CPU if false. # This will reduce CPU if false.
zero-new-island-levels: true zero-new-island-levels: true
# #
@ -72,18 +72,3 @@ sumteamdeaths: false
# Shorthand island level # Shorthand island level
# Shows large level values rounded down, e.g., 10,345 -> 10k # Shows large level values rounded down, e.g., 10,345 -> 10k
shorthand: false shorthand: false
units:
# Shorthand units
kilo: k
mega: M
giga: G
tera: T
#
# Include Shulker Box content in chests in level calculations.
# Will count blocks in Shulker Boxes inside of chests.
# NOTE: include-chests needs to be enabled for this to work!.
include-shulkers-in-chest: false
#
# Disables hooking with other plugins.
# Example: disabled-plugin-hooks: [UltimateStacker, RoseStacker]
disabled-plugin-hooks: []

View File

@ -8,12 +8,8 @@ admin:
parameters: "<player>" parameters: "<player>"
description: "calculate the island level for player" description: "calculate the island level for player"
sethandicap: sethandicap:
parameters: <player> [+/-]<handicap> parameters: <player> <handicap>
description: | description: "set the island handicap, usually the level of the starter island"
set or change the island *handicap*
e.g. +10 will remove 10 levels,
30 will set handicap to 30,
-20 will add 20 levels
changed: "&a Initial island handicap changed from [number] to [new_number]." changed: "&a Initial island handicap changed from [number] to [new_number]."
invalid-level: "&c Invalid handicap. Use an integer." invalid-level: "&c Invalid handicap. Use an integer."
levelstatus: levelstatus:
@ -46,7 +42,7 @@ island:
estimated-wait: "&a Estimated wait: [number] seconds" estimated-wait: "&a Estimated wait: [number] seconds"
in-queue: "&a You are number [number] in the queue" in-queue: "&a You are number [number] in the queue"
island-level-is: "&a Island level is &b[level]" island-level-is: "&a Island level is &b[level]"
required-points-to-next-level: "&a Level progress: &6 [progress]&b /&e [levelcost] &a points" required-points-to-next-level: "&a [points] points required until the next level"
deaths: "&c([number] deaths)" deaths: "&c([number] deaths)"
cooldown: "&c You must wait &b[time] &c seconds until you can do that again" cooldown: "&c You must wait &b[time] &c seconds until you can do that again"
in-progress: "&6 Island level calculation is in progress..." in-progress: "&6 Island level calculation is in progress..."
@ -116,11 +112,6 @@ level:
limit: "&7 Block limit: &e [number]" limit: "&7 Block limit: &e [number]"
count: "&7 Number of blocks: &e [number]" count: "&7 Number of blocks: &e [number]"
calculated: "&7 Calculated value: &e [number]" calculated: "&7 Calculated value: &e [number]"
value_blocks:
name: "&f&l All Blocks With Value"
description: |-
&7 Display all blocks
&7 with value on island.
all_blocks: all_blocks:
name: "&f&l All Blocks" name: "&f&l All Blocks"
description: |- description: |-
@ -220,7 +211,3 @@ level:
value-underwater: "&7 The value of '[material]' below sea-level: &e[value]" value-underwater: "&7 The value of '[material]' below sea-level: &e[value]"
# Message that is sent to user when he does not hold any items in hand. # Message that is sent to user when he does not hold any items in hand.
empty-hand: "&c There are no blocks in your hand" empty-hand: "&c There are no blocks in your hand"
# Message when showing how many have been placed of a block
you-have: "&7 You have [number] at last count."
# Message about the limit
you-can-place: "&7 You can place up to [number] and have them count"

210
src/main/resources/locales/zh-CN.yml Normal file → Executable file
View File

@ -1,93 +1,79 @@
---
admin: admin:
level: level:
parameters: <player> parameters: "<player>"
description: 计算指定玩家的岛屿等级 description: 计算指定玩家的岛屿等级
sethandicap: sethandicap:
parameters: <player> <handicap> parameters: "<player> <handicap>"
description: 设置偏差值, 通常用于抵消初始岛屿等级, 来保证岛屿等级从零开始. 实际岛屿等级 - <handicap> = 最终的岛屿等级 description: 设置偏差值,通常用于抵消初始岛屿等级,来保证岛屿等级从零开始。实际岛屿等级 - <handicap> = 最终的岛屿等级
changed: '&a岛屿的偏差值从[number]更改为[new_number]' changed: "&a 岛屿的偏差值从 [number] 更改为 [new_number]"
invalid-level: '&c偏差值无效, 请使用整数' invalid-level: "&c 偏差值无效,请使用整数"
levelstatus: levelstatus:
description: 显示等级计算队列中的岛屿 description: 显示等级计算队列中的岛屿
islands-in-queue: '&a列队中的岛屿: [number]' islands-in-queue: "&a 列队中的岛屿:[number]"
top: top:
description: 显示前十名 description: 显示前十名
unknown-world: '&c未知的世界!' unknown-world: "&c 未知的世界!"
display: '&f[rank]. &a[name] &7- &b[level]' display: "&f[rank]. &a[name] &7- &b[level]"
remove: remove:
description: 将玩家移出前十名 description: 将玩家移出前十名
parameters: <player> parameters: "<player>"
stats:
description: 显示该服务器上岛屿的统计数据
title: 服务器岛屿数据
world: '&a[name]'
no-data: '&c没有数据.'
average-level: '平均岛屿等级: [number]'
median-level: '中位数岛屿等级: [number]'
mode-level: '众数岛屿等级: [number]'
highest-level: '最高岛屿等级: [number]'
lowest-level: '最低岛屿等级: [number]'
distribution: '岛屿等级分布:'
islands: 个岛屿
island: island:
level: level:
parameters: '[player]' parameters: "[player]"
description: 计算你或指定玩家 [player] 的岛屿等级 description: 计算你或指定玩家 [player] 的岛屿等级
calculating: '&a等级计算中...' calculating: "&a 等级计算中..."
estimated-wait: '&a预计等待时间: [number]秒' estimated-wait: "&a 预计等待时间:[number] 秒"
in-queue: '&a你处于队列中第[number]个' in-queue: "&a 你处于队列中第 [number] 个"
island-level-is: '&a岛屿等级为: &b[level]' island-level-is: "&a 岛屿等级为 &b[level]"
required-points-to-next-level: '&a还需[points]点数才能到达下一级' required-points-to-next-level: "&a 还需 [points] 点数才能到达下一级"
deaths: '&c([number]次死亡)' deaths: "&c([number] 次死亡)"
cooldown: '&c还需等待&b[time]&c秒才能再次使用该指令' cooldown: "&c 还需等待 &b[time] &c秒才能再次使用该指令"
in-progress: '&6岛屿等级正在计算中...' in-progress: "&6 岛级等级正在计算中..."
time-out: '&c等级计算超时, 请稍后再试' time-out: "&c 等级计算超时。请稍后再试"
top: top:
description: 显示前十名 description: 显示前十名
gui-title: '&a前十' gui-title: "&a 前十"
gui-heading: '&6[name]: &B[rank]' gui-heading: "&6[name]: &B[rank]"
island-level: '&b等级: [level]' island-level: "&b 等级 [level]"
warp-to: '&a正在传送到[name]的岛屿' warp-to: "&a 正在传送到 [name] 的岛屿"
level-details: level-details:
above-sea-level-blocks: 海平面以上的方块 above-sea-level-blocks: 海平面以上的方块
spawners: 刷怪笼 spawners: 刷怪笼
underwater-blocks: 水下的方块 underwater-blocks: 水下的方块
all-blocks: 所有方块 all-blocks: 所有方块
no-island: '&c没有岛屿!' no-island: "&c 没有岛屿!"
names-island: '[name]的岛屿' names-island: "[name] 的岛屿"
syntax: '[name] x [number]' syntax: "[name] x [number]"
hint: '&c运行level指令查看方块报告' hint: "&c 运行level指令查看方块报告"
level: level:
commands: commands:
value: value:
parameters: '[hand|<material>]' parameters: "[hand|<material>]"
description: 显示方块的价值. 在末尾添加'hand'可显示手中方块的价值 description: 显示方块的价值。在末尾添加 'hand' 可显示手中方块的价值
gui: gui:
titles: titles:
top: '&0&l岛屿排行榜' top: "&0&l 岛屿排行榜"
detail-panel: '&0&l[name]的岛屿' detail-panel: "&0&l [name] 的岛屿"
value-panel: '&0&l方块价值' value-panel: "&0&l 方块价值"
buttons: buttons:
island: island:
empty: '&f&l第[name]名' empty: "&f&l 第 [name] 名"
name: '&f&l[name]' name: "&f&l [name]"
description: |- description: |-
[owner] [owner]
[members] [members]
[place] [place]
[level] [level]
owners-island: '[player]的岛屿' owners-island: "[player] 的岛屿"
owner: '&7&l岛主: &r&b[player]' owner: "&7&l 岛主:&r&b [player]"
members-title: '&7&l成员: ' members-title: "&7&l 成员:"
member: '&b- [player]' member: "&b - [player]"
unknown: 未知 unknown: 未知
place: '&7第&7&o[number]&r&7名' place: "&7第 &7&o[number] &r&7名"
level: '&7等级: &o[number]' level: "&7 等级: &o [number]"
material: material:
name: '&f&l [number] x [material]' name: "&f&l [number] x [material]"
description: |- description: |-
[description] [description]
[count] [count]
@ -95,79 +81,83 @@ level:
[calculated] [calculated]
[limit] [limit]
[id] [id]
id: '&7方块ID: &e[id]' id: "&7 方块ID&e [id]"
value: '&7方块价值: &e[number]' value: "&7 方块价值:&e [number]"
limit: '&7方块限制: &e[number]' limit: "&7 方块限制:&e [number]"
count: '&7方块数量: &e[number]' count: "&7 方块数量:&e [number]"
calculated: '&7计算值: &e[number]' calculated: "&7 计算值:&e [number]"
all_blocks: all_blocks:
name: '&f&l所有方块' name: "&f&l 所有方块"
description: '&7显示岛屿上所有的方块' description: "&7 显示岛屿上所有的方块"
above_sea_level: above_sea_level:
name: '&f&l海平面以上的方块' name: "&f&l 海平面以上的方块"
description: '&7只显示所有海平面以上的方块' description: |-
&7 只显示所有
&7 海平面以上的方块
underwater: underwater:
name: '&f&l海平面以下的方块' name: "&f&l 海平面以下的方块"
description: 只显示所有海平面以下的方块 description: |-
&7 只显示所有
&7 海平面以下的方块
spawner: spawner:
name: '&f&l刷怪笼' name: "&f&l 刷怪笼"
description: '&7只显示刷怪笼' description: "&7 只显示刷怪笼"
filters: filters:
name: name:
name: '&f&l按名称排序' name: "&f&l 按名称排序"
description: '&7通过名称排序所有的方块' description: "&7 通过名称排序所有的方块"
value: value:
name: '&f&l按价值排序' name: "&f&l 按价值排序"
description: '&7通过价值排序所有的方块' description: "&7 通过价值排序所有的方块"
count: count:
name: '&f&l按数量排序' name: "&f&l 按数量排序"
description: '&7通过数量排序所有方块' description: "&7 通过数量排序所有方块"
value: value:
name: '&f&l[material]' name: "&f&l [material]"
description: |- description: |-
[description] [description]
[value] [value]
[underwater] [underwater]
[limit] [limit]
[id] [id]
id: '&7方块ID: &e[id]' id: "&7 方块ID&e [id]"
value: '&7方块价值: &e[number]' value: "&7 方块价值:&e [number]"
underwater: '&7海平面以下方块的价值: &e[number]' underwater: "&7 海平面以下方块的价值:&e [number]"
limit: '&7方块限制: &e[number]' limit: "&7 方块限制:&e [number]"
previous: previous:
name: '&f&l上一页' name: "&f&l 上一页"
description: '&7切换到第[number]页' description: "&7 切换到第 [number] 页"
next: next:
name: '&f&l下一页' name: "&f&l 下一页"
description: '&7切换到第[number]页' description: "&7 切换到第 [number] 页"
search: search:
name: '&f&l搜索' name: "&f&l 搜索"
description: '&7搜索特定的内容' description: "&7 搜索特定的内容"
search: '&b搜索值: [value]' search: "&b 搜索值:[value]"
tips: tips:
click-to-view: '&e点击 &7查看' click-to-view: "&e 点击 &7 查看"
click-to-previous: '&e点击 &7查看上一页' click-to-previous: "&e 点击 &7 查看上一页"
click-to-next: '&e点击 &7查看下一页' click-to-next: "&e 点击 &7 查看下一页"
click-to-select: '&e点击 &7选择' click-to-select: "&e 点击 &7 选择"
left-click-to-cycle-up: '&e左键 &7向上循环' left-click-to-cycle-up: "&e 左键点击 &7 向上循环"
right-click-to-cycle-down: '&e右键 &7向下循环' right-click-to-cycle-down: "&e 右键点击 &7 向下循环"
left-click-to-change: '&e左键 &7编辑' left-click-to-change: "&e 左键点击 &7 编辑"
right-click-to-clear: '&e右键 &7清除' right-click-to-clear: "&e 右键点击 &7 清除"
click-to-asc: '&e点击 &7以升序排序' click-to-asc: "&e 点击 &7 以升序排序"
click-to-desc: '&e点击 &7以降序排序' click-to-desc: "&e 点击 &7 以降序排序"
click-to-warp: '&e点击 &7去岛屿传送点' click-to-warp: "&e 点击 &7 去岛屿传送点"
click-to-visit: '&e点击 &7参观' click-to-visit: "&e 点击 &7 参观"
right-click-to-visit: '&e右键 &7查看' right-click-to-visit: "&e 右键点击 &7 查看"
conversations: conversations:
prefix: '&l&6[BentoBox]: &r' prefix: "&l&6 [BentoBox]: &r"
no-data: '&c运行level指令查看方块报告' no-data: "&c 运行level指令查看方块报告"
cancel-string: cancel cancel-string: cancel
exit-string: cancel, exit, quit exit-string: cancel, exit, quit
write-search: '&e请输入要搜索的值. (输入''cancel''退出)' write-search: "&e 请输入要搜索的值. (输入 'cancel' 退出)"
search-updated: '&a搜索值已更新' search-updated: "&a 搜索值已更新"
cancelled: '&c对话已取消' cancelled: "&c 对话已取消!"
no-value: '&c这件物品一文不值' no-value: "&c 这件物品一文不值"
unknown-item: '&c物品''[material]''在游戏中不存在' unknown-item: "&c 物品 '[material]' 在游戏中不存在"
value: '&7物品''[material]''的价值: &e[value]' value: "&7 物品 '[material]' 的价值:&e[value]"
value-underwater: '&7物品''[material]''在海平面以下的价值: &e[value]' value-underwater: "&7 物品 '[material]' 在海平面以下的价值:&e[value]"
empty-hand: '&c你的手中没有拿着方块' empty-hand: "&c 你的手中没有拿着方块"

View File

@ -24,28 +24,6 @@ detail_panel:
1: 1:
# Column number # Column number
2: 2:
# Icon is a Bukkit Material.
icon: ICE
# Title of the button shown to the user. This is a reference and the reference will be translatable in the locale file
title: level.gui.buttons.value_blocks.name
# Description of the button shown to the user in the lore. This is a reference and the reference will be translatable in the locale file
description: level.gui.buttons.value_blocks.description
# The data section is a key-value list of data relavent for this button. It is interpreted by the code implemented the panel.
# The convention is to specify the type and the panel tab that will open if pressed. These are Enums in the code.
data:
# Type button will go to the ALL_BLOCKS tab when clicked.
type: TAB
tab: VALUE_BLOCKS
# Actions cover what happens if the button is clicked or the mouse is moved over it. There can be multiple actions possible for different
# click-types.
actions:
# Each action has an arbitrary descriptive name to define it.
view:
# The click-type is the same as the bukkit {@link org.bukkit.event.inventory.ClickType}. UNKNOWN is the default.
click-type: unknown
# tooltip is a locale reference that will be translated for the user and shown when they hover over the button.
tooltip: level.gui.tips.click-to-view
3:
# Icon is a Bukkit Material. # Icon is a Bukkit Material.
icon: STONE icon: STONE
# Title of the button shown to the user. This is a reference and the reference will be translatable in the locale file # Title of the button shown to the user. This is a reference and the reference will be translatable in the locale file
@ -67,7 +45,7 @@ detail_panel:
click-type: unknown click-type: unknown
# tooltip is a locale reference that will be translated for the user and shown when they hover over the button. # tooltip is a locale reference that will be translated for the user and shown when they hover over the button.
tooltip: level.gui.tips.click-to-view tooltip: level.gui.tips.click-to-view
4: 3:
icon: GRASS_BLOCK icon: GRASS_BLOCK
title: level.gui.buttons.above_sea_level.name title: level.gui.buttons.above_sea_level.name
description: level.gui.buttons.above_sea_level.description description: level.gui.buttons.above_sea_level.description
@ -78,7 +56,7 @@ detail_panel:
view: view:
click-type: unknown click-type: unknown
tooltip: level.gui.tips.click-to-view tooltip: level.gui.tips.click-to-view
5: 4:
icon: WATER_BUCKET icon: WATER_BUCKET
title: level.gui.buttons.underwater.name title: level.gui.buttons.underwater.name
description: level.gui.buttons.underwater.description description: level.gui.buttons.underwater.description
@ -89,7 +67,7 @@ detail_panel:
view: view:
click-type: unknown click-type: unknown
tooltip: level.gui.tips.click-to-view tooltip: level.gui.tips.click-to-view
6: 5:
icon: SPAWNER icon: SPAWNER
title: level.gui.buttons.spawner.name title: level.gui.buttons.spawner.name
description: level.gui.buttons.spawner.description description: level.gui.buttons.spawner.description
@ -132,11 +110,18 @@ detail_panel:
8: material_button 8: material_button
3: 3:
1: 1:
# In this case, the icon is defined as a TIPPED_ARROW with a color. # In this case, the icon is defined as a TIPPED_ARROW with and enchantment applied. The format for the enchantment is
# CustomPotionColor uses the Decimal description of a Color, just as leather armor does. # define in {@link world.bentobox.bentobox.util.ItemParser} and available for POTIONS or TIPPED_ARROWs.
# All you need to do is take a hex code of a color (like #ff00aa) which represents red, # Format TIPPED_ARROW:NAME:<LEVEL>:<EXTENDED>:<SPLASH/LINGER>:QTY
# green, blue as 2 hex digits each and convert that number into a decimal, using a hex to decimal calculator. # LEVEL, EXTENDED, SPLASH, LINGER are optional.
icon: tipped_arrow{CustomPotionColor:11546150} # LEVEL is a number, 1 or 2
# LINGER is for V1.9 servers and later
# Examples:
# TIPPED_ARROW:STRENGTH:1:EXTENDED:SPLASH:1
# TIPPED_ARROW:INSTANT_DAMAGE:2::LINGER:2
# TIPPED_ARROW:JUMP:2:NOTEXTENDED:NOSPLASH:1
# TIPPED_ARROW:WEAKNESS::::1 - any weakness enchantment
icon: TIPPED_ARROW:INSTANT_HEAL::::1
title: level.gui.buttons.previous.name title: level.gui.buttons.previous.name
description: level.gui.buttons.previous.description description: level.gui.buttons.previous.description
data: data:
@ -154,7 +139,7 @@ detail_panel:
7: material_button 7: material_button
8: material_button 8: material_button
9: 9:
icon: tipped_arrow{CustomPotionColor:8439583} icon: TIPPED_ARROW:JUMP::::1
title: level.gui.buttons.next.name title: level.gui.buttons.next.name
description: level.gui.buttons.next.description description: level.gui.buttons.next.description
data: data:

View File

@ -64,7 +64,7 @@ value_panel:
8: material_button 8: material_button
3: 3:
1: 1:
icon: tipped_arrow{CustomPotionColor:11546150} icon: TIPPED_ARROW:INSTANT_HEAL::::1
title: level.gui.buttons.previous.name title: level.gui.buttons.previous.name
description: level.gui.buttons.previous.description description: level.gui.buttons.previous.description
data: data:
@ -82,7 +82,7 @@ value_panel:
7: material_button 7: material_button
8: material_button 8: material_button
9: 9:
icon: tipped_arrow{CustomPotionColor:8439583} icon: TIPPED_ARROW:JUMP::::1
title: level.gui.buttons.next.name title: level.gui.buttons.next.name
description: level.gui.buttons.next.description description: level.gui.buttons.next.description
data: data:

View File

@ -61,7 +61,6 @@ import world.bentobox.bentobox.managers.FlagsManager;
import world.bentobox.bentobox.managers.IslandWorldManager; import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.managers.IslandsManager; import world.bentobox.bentobox.managers.IslandsManager;
import world.bentobox.bentobox.managers.PlaceholdersManager; import world.bentobox.bentobox.managers.PlaceholdersManager;
import world.bentobox.bentobox.util.Util;
import world.bentobox.level.config.BlockConfig; import world.bentobox.level.config.BlockConfig;
import world.bentobox.level.config.ConfigSettings; import world.bentobox.level.config.ConfigSettings;
import world.bentobox.level.listeners.IslandActivitiesListeners; import world.bentobox.level.listeners.IslandActivitiesListeners;
@ -73,7 +72,7 @@ import world.bentobox.level.listeners.JoinLeaveListener;
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PrepareForTest({ Bukkit.class, BentoBox.class, User.class, Util.class }) @PrepareForTest({ Bukkit.class, BentoBox.class, User.class })
public class LevelTest { public class LevelTest {
private static File jFile; private static File jFile;
@ -190,11 +189,6 @@ public class LevelTest {
when(Bukkit.getServer()).thenReturn(server); when(Bukkit.getServer()).thenReturn(server);
when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger());
when(Bukkit.getPluginManager()).thenReturn(mock(PluginManager.class)); when(Bukkit.getPluginManager()).thenReturn(mock(PluginManager.class));
when(Bukkit.getBukkitVersion()).thenReturn("");
// Util
PowerMockito.mockStatic(Util.class, Mockito.RETURNS_MOCKS);
when(Util.inTest()).thenReturn(true);
// Addon // Addon
addon = new Level(); addon = new Level();
@ -227,6 +221,7 @@ public class LevelTest {
when(fm.getFlags()).thenReturn(Collections.emptyList()); when(fm.getFlags()).thenReturn(Collections.emptyList());
// Bukkit // Bukkit
PowerMockito.mockStatic(Bukkit.class);
when(Bukkit.getScheduler()).thenReturn(scheduler); when(Bukkit.getScheduler()).thenReturn(scheduler);
ItemMeta meta = mock(ItemMeta.class); ItemMeta meta = mock(ItemMeta.class);
ItemFactory itemFactory = mock(ItemFactory.class); ItemFactory itemFactory = mock(ItemFactory.class);

View File

@ -94,7 +94,7 @@ public class LevelsManagerTest {
private World world; private World world;
@Mock @Mock
private Player player; private Player player;
@Mock
private ConfigSettings settings; private ConfigSettings settings;
@Mock @Mock
private User user; private User user;
@ -165,7 +165,6 @@ public class LevelsManagerTest {
when(im.hasIsland(eq(world), any(UUID.class))).thenReturn(true); when(im.hasIsland(eq(world), any(UUID.class))).thenReturn(true);
when(im.getIsland(world, uuid)).thenReturn(island); when(im.getIsland(world, uuid)).thenReturn(island);
when(im.getIslandById(anyString())).thenReturn(Optional.of(island)); when(im.getIslandById(anyString())).thenReturn(Optional.of(island));
when(im.getIslandById(anyString(), eq(false))).thenReturn(Optional.of(island));
// Player // Player
when(player.getUniqueId()).thenReturn(uuid); when(player.getUniqueId()).thenReturn(uuid);
@ -175,7 +174,6 @@ public class LevelsManagerTest {
when(world.getName()).thenReturn("bskyblock-world"); when(world.getName()).thenReturn("bskyblock-world");
// Settings // Settings
settings = new ConfigSettings();
when(addon.getSettings()).thenReturn(settings); when(addon.getSettings()).thenReturn(settings);
// User // User
@ -334,7 +332,7 @@ public class LevelsManagerTest {
@Test @Test
public void testFormatLevel() { public void testFormatLevel() {
assertEquals("123456789", lm.formatLevel(123456789L)); assertEquals("123456789", lm.formatLevel(123456789L));
settings.setShorthand(true); when(settings.isShorthand()).thenReturn(true);
assertEquals("123.5M", lm.formatLevel(123456789L)); assertEquals("123.5M", lm.formatLevel(123456789L));
assertEquals("1.2k", lm.formatLevel(1234L)); assertEquals("1.2k", lm.formatLevel(1234L));
assertEquals("123.5G", lm.formatLevel(123456789352L)); assertEquals("123.5G", lm.formatLevel(123456789352L));
@ -397,8 +395,8 @@ public class LevelsManagerTest {
lm.loadTopTens(); lm.loadTopTens();
PowerMockito.verifyStatic(Bukkit.class); // 1 PowerMockito.verifyStatic(Bukkit.class); // 1
Bukkit.getScheduler(); Bukkit.getScheduler();
verify(scheduler).runTaskAsynchronously(eq(plugin), task.capture()); // Capture the task in the scheduler verify(scheduler).runTaskAsynchronously(eq(plugin), task.capture());
task.getValue().run(); // run it task.getValue().run();
verify(addon).log("Generating rankings"); verify(addon).log("Generating rankings");
verify(addon).log("Generated rankings for bskyblock-world"); verify(addon).log("Generated rankings for bskyblock-world");

View File

@ -9,9 +9,9 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -138,7 +138,7 @@ public class PlaceholderManagerTest {
when(im.getIsland(any(World.class), any(User.class))).thenReturn(island); when(im.getIsland(any(World.class), any(User.class))).thenReturn(island);
when(im.getIslandAt(any(Location.class))).thenReturn(Optional.of(island)); when(im.getIslandAt(any(Location.class))).thenReturn(Optional.of(island));
when(im.getIslandById(anyString())).thenAnswer((Answer<Optional<Island>>) invocation -> Optional.of(islands.get(invocation.getArgument(0, String.class)))); when(im.getIslandById(anyString())).thenAnswer((Answer<Optional<Island>>) invocation -> Optional.of(islands.get(invocation.getArgument(0, String.class))));
when(im.getIslands(any(), any(UUID.class))).thenReturn(new ArrayList<>(islands.values())); when(im.getIslands(any(), any(UUID.class))).thenReturn(new HashSet<>(islands.values()));
when(addon.getIslands()).thenReturn(im); when(addon.getIslands()).thenReturn(im);
// Levels Manager // Levels Manager

View File

@ -10,7 +10,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -120,8 +120,8 @@ public class AdminTopRemoveCommandTest {
when(island.getOwner()).thenReturn(uuid); when(island.getOwner()).thenReturn(uuid);
// Island Manager // Island Manager
when(plugin.getIslands()).thenReturn(im); when(plugin.getIslands()).thenReturn(im);
when(im.getIslands(any(), any(User.class))).thenReturn(List.of(island)); when(im.getIslands(any(), any(User.class))).thenReturn(Set.of(island));
when(im.getIslands(any(), any(UUID.class))).thenReturn(List.of(island)); when(im.getIslands(any(), any(UUID.class))).thenReturn(Set.of(island));
// Bukkit // Bukkit
PowerMockito.mockStatic(Bukkit.class); PowerMockito.mockStatic(Bukkit.class);