mirror of
https://github.com/BentoBoxWorld/Level.git
synced 2025-01-23 16:01:31 +01:00
Complete rewrite to enable pipelining.
Adds ability to scan chest contents.
This commit is contained in:
parent
c19ae41cbb
commit
08b7c99c3f
2
pom.xml
2
pom.xml
@ -65,7 +65,7 @@
|
||||
<!-- Do not change unless you want different name for local builds. -->
|
||||
<build.number>-LOCAL</build.number>
|
||||
<!-- This allows to change between versions. -->
|
||||
<build.version>2.1.0</build.version>
|
||||
<build.version>2.2.0</build.version>
|
||||
</properties>
|
||||
|
||||
<!-- Profiles will allow to automatically change build version. -->
|
||||
|
@ -2,151 +2,53 @@ package world.bentobox.level;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.api.addons.Addon;
|
||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||
import world.bentobox.bentobox.api.configuration.Config;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.Database;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.level.calculators.CalcIslandLevel;
|
||||
import world.bentobox.level.commands.admin.AdminLevelCommand;
|
||||
import world.bentobox.level.commands.admin.AdminTopCommand;
|
||||
import world.bentobox.level.commands.island.IslandLevelCommand;
|
||||
import world.bentobox.level.commands.island.IslandTopCommand;
|
||||
import world.bentobox.level.commands.island.IslandValueCommand;
|
||||
import world.bentobox.level.calculators.IslandLevelCalculator;
|
||||
import world.bentobox.level.calculators.Pipeliner;
|
||||
import world.bentobox.level.commands.AdminLevelCommand;
|
||||
import world.bentobox.level.commands.AdminLevelStatusCommand;
|
||||
import world.bentobox.level.commands.AdminTopCommand;
|
||||
import world.bentobox.level.commands.IslandLevelCommand;
|
||||
import world.bentobox.level.commands.IslandTopCommand;
|
||||
import world.bentobox.level.commands.IslandValueCommand;
|
||||
import world.bentobox.level.config.BlockConfig;
|
||||
import world.bentobox.level.config.ConfigSettings;
|
||||
import world.bentobox.level.listeners.IslandTeamListeners;
|
||||
import world.bentobox.level.listeners.IslandActivitiesListeners;
|
||||
import world.bentobox.level.listeners.JoinLeaveListener;
|
||||
import world.bentobox.level.objects.LevelsData;
|
||||
import world.bentobox.level.requests.LevelRequestHandler;
|
||||
import world.bentobox.level.requests.TopTenRequestHandler;
|
||||
|
||||
/**
|
||||
* Addon to BSkyBlock/AcidIsland that enables island level scoring and top ten functionality
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
public class Level extends Addon {
|
||||
|
||||
private static Addon addon;
|
||||
|
||||
// Settings
|
||||
private ConfigSettings settings;
|
||||
private Config<ConfigSettings> configObject = new Config<>(this, ConfigSettings.class);
|
||||
|
||||
/**
|
||||
* @param settings the settings to set
|
||||
*/
|
||||
public void setSettings(ConfigSettings settings) {
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
// Database handler for level data
|
||||
private Database<LevelsData> handler;
|
||||
|
||||
// A cache of island levels.
|
||||
private Map<UUID, LevelsData> levelsCache;
|
||||
|
||||
// The Top Ten object
|
||||
private TopTen topTen;
|
||||
|
||||
// Level calculator
|
||||
private LevelPresenter levelPresenter;
|
||||
|
||||
|
||||
|
||||
private BlockConfig blockConfig;
|
||||
|
||||
/**
|
||||
* Calculates a user's island
|
||||
* @param world - the world where this island is
|
||||
* @param user - the user who is asking, or null if none
|
||||
* @param playerUUID - the target island member's UUID
|
||||
*/
|
||||
public void calculateIslandLevel(World world, @Nullable User user, @NonNull UUID playerUUID) {
|
||||
levelPresenter.calculateIslandLevel(world, user, playerUUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get level from cache for a player.
|
||||
* @param targetPlayer - target player UUID
|
||||
* @return Level of player or zero if player is unknown or UUID is null
|
||||
*/
|
||||
public long getIslandLevel(World world, @Nullable UUID targetPlayer) {
|
||||
if (targetPlayer == null) return 0L;
|
||||
LevelsData ld = getLevelsData(targetPlayer);
|
||||
return ld == null ? 0L : ld.getLevel(world);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a player from the cache or database
|
||||
* @param targetPlayer - UUID of target player
|
||||
* @return LevelsData object or null if not found
|
||||
*/
|
||||
@Nullable
|
||||
public LevelsData getLevelsData(@NonNull UUID targetPlayer) {
|
||||
// Get from database if not in cache
|
||||
if (!levelsCache.containsKey(targetPlayer) && handler.objectExists(targetPlayer.toString())) {
|
||||
LevelsData ld = handler.loadObject(targetPlayer.toString());
|
||||
if (ld != null) {
|
||||
levelsCache.put(targetPlayer, ld);
|
||||
} else {
|
||||
handler.deleteID(targetPlayer.toString());
|
||||
}
|
||||
}
|
||||
// Return cached value or null
|
||||
return levelsCache.get(targetPlayer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the settings
|
||||
*/
|
||||
public ConfigSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
public TopTen getTopTen() {
|
||||
return topTen;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the levelPresenter
|
||||
*/
|
||||
public LevelPresenter getLevelPresenter() {
|
||||
return levelPresenter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable(){
|
||||
// Save the cache
|
||||
if (levelsCache != null) {
|
||||
save();
|
||||
}
|
||||
if (topTen != null) {
|
||||
topTen.saveTopTen();
|
||||
}
|
||||
}
|
||||
private Pipeliner pipeliner;
|
||||
private LevelsManager manager;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Save the default config from config.yml
|
||||
saveDefaultConfig();
|
||||
// Load settings from config.yml. This will check if there are any issues with it too.
|
||||
if (loadSettings()) {
|
||||
// Disable
|
||||
logError("Level settings could not load! Addon disabled.");
|
||||
setState(State.DISABLED);
|
||||
} else {
|
||||
configObject.saveConfigObject(settings);
|
||||
}
|
||||
}
|
||||
@ -154,41 +56,105 @@ public class Level extends Addon {
|
||||
private boolean loadSettings() {
|
||||
// Load settings again to get worlds
|
||||
settings = configObject.loadConfigObject();
|
||||
if (settings == null) {
|
||||
// Disable
|
||||
logError("Level settings could not load! Addon disabled.");
|
||||
setState(State.DISABLED);
|
||||
return false;
|
||||
}
|
||||
// Check for legacy blocks and limits etc.
|
||||
if (getConfig().isConfigurationSection("blocks")
|
||||
|| getConfig().isConfigurationSection("limits")
|
||||
|| getConfig().isConfigurationSection("worlds")) {
|
||||
logWarning("Converting old config.yml format - shifting blocks, limits and worlds to blockconfig.yml");
|
||||
File blockConfigFile = new File(this.getDataFolder(), "blockconfig.yml");
|
||||
if (blockConfigFile.exists()) {
|
||||
logError("blockconfig.yml already exists! Saving config as blockconfig.yml.2");
|
||||
blockConfigFile = new File(this.getDataFolder(), "blockconfig.yml.2");
|
||||
}
|
||||
YamlConfiguration blockConfig = new YamlConfiguration();
|
||||
copyConfigSection(blockConfig, "limits");
|
||||
copyConfigSection(blockConfig, "blocks");
|
||||
copyConfigSection(blockConfig, "worlds");
|
||||
try {
|
||||
blockConfig.save(blockConfigFile);
|
||||
} catch (IOException e) {
|
||||
logError("Could not save! " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return settings == null;
|
||||
}
|
||||
|
||||
private void copyConfigSection(YamlConfiguration blockConfig, String sectionName) {
|
||||
if (!getConfig().isConfigurationSection(sectionName)) return;
|
||||
ConfigurationSection section = getConfig().getConfigurationSection(sectionName);
|
||||
for (String k:section.getKeys(true)) {
|
||||
blockConfig.set(sectionName + "." + k, section.get(k));
|
||||
@Override
|
||||
public void onEnable() {
|
||||
loadBlockSettings();
|
||||
// Start pipeline
|
||||
pipeliner = new Pipeliner(this);
|
||||
// Start Manager
|
||||
manager = new LevelsManager(this);
|
||||
manager.loadTopTens();
|
||||
// Register listeners
|
||||
this.registerListener(new IslandActivitiesListeners(this));
|
||||
this.registerListener(new JoinLeaveListener(this));
|
||||
// Register commands for GameModes
|
||||
getPlugin().getAddonsManager().getGameModeAddons().stream()
|
||||
.filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName()))
|
||||
.forEach(gm -> {
|
||||
log("Level hooking into " + gm.getDescription().getName());
|
||||
registerCommands(gm);
|
||||
registerPlaceholders(gm);
|
||||
});
|
||||
// Register request handlers
|
||||
registerRequestHandler(new LevelRequestHandler(this));
|
||||
registerRequestHandler(new TopTenRequestHandler(this));
|
||||
|
||||
// Check if WildStackers is enabled on the server
|
||||
// I only added support for counting blocks into the island level
|
||||
// Someone else can PR if they want spawners added to the Leveling system :)
|
||||
IslandLevelCalculator.stackersEnabled = Bukkit.getPluginManager().getPlugin("WildStacker") != null;
|
||||
|
||||
}
|
||||
|
||||
private void registerPlaceholders(GameModeAddon gm) {
|
||||
if (getPlugin().getPlaceholdersManager() == null) return;
|
||||
// Island Level
|
||||
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||
gm.getDescription().getName().toLowerCase() + "_island_level",
|
||||
user -> getManager().getIslandLevelString(gm.getOverWorld(), user.getUniqueId()));
|
||||
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||
gm.getDescription().getName().toLowerCase() + "_points_to_next_level",
|
||||
user -> getManager().getPointsToNextString(gm.getOverWorld(), user.getUniqueId()));
|
||||
|
||||
// Visited Island Level
|
||||
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||
gm.getDescription().getName().toLowerCase() + "_visited_island_level", user -> getVisitedIslandLevel(gm, user));
|
||||
|
||||
// Register Top Ten Placeholders
|
||||
for (int i = 1; i < 11; i++) {
|
||||
final int rank = i;
|
||||
// Name
|
||||
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||
gm.getDescription().getName().toLowerCase() + "_top_name_" + i, u -> getRankName(gm.getOverWorld(), rank));
|
||||
// Level
|
||||
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||
gm.getDescription().getName().toLowerCase() + "_top_value_" + i, u -> getRankLevel(gm.getOverWorld(), rank));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String getRankName(World world, int rank) {
|
||||
if (rank < 1) rank = 1;
|
||||
if (rank > 10) rank = 10;
|
||||
return getPlayers().getName(getManager().getTopTen(world, 10).keySet().stream().skip(rank - 1).limit(1).findFirst().orElse(null));
|
||||
}
|
||||
|
||||
private String getRankLevel(World world, int rank) {
|
||||
if (rank < 1) rank = 1;
|
||||
if (rank > 10) rank = 10;
|
||||
return getManager().formatLevel(getManager().getTopTen(world, 10).values().stream().skip(rank - 1).limit(1).findFirst().orElse(null));
|
||||
}
|
||||
|
||||
private String getVisitedIslandLevel(GameModeAddon gm, User user) {
|
||||
if (!gm.inWorld(user.getLocation())) return "";
|
||||
return getIslands().getIslandAt(user.getLocation())
|
||||
.map(island -> getManager().getIslandLevelString(gm.getOverWorld(), island.getOwner()))
|
||||
.orElse("0");
|
||||
}
|
||||
|
||||
|
||||
private void registerCommands(GameModeAddon gm) {
|
||||
gm.getAdminCommand().ifPresent(adminCommand -> {
|
||||
new AdminLevelCommand(this, adminCommand);
|
||||
new AdminTopCommand(this, adminCommand);
|
||||
new AdminLevelStatusCommand(this, adminCommand);
|
||||
});
|
||||
gm.getPlayerCommand().ifPresent(playerCmd -> {
|
||||
new IslandLevelCommand(this, playerCmd);
|
||||
new IslandTopCommand(this, playerCmd);
|
||||
new IslandValueCommand(this, playerCmd);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (manager != null) {
|
||||
manager.save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void loadBlockSettings() {
|
||||
@ -210,165 +176,6 @@ public class Level extends Addon {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
addon = this;
|
||||
loadBlockSettings();
|
||||
// Get the BSkyBlock database
|
||||
// Set up the database handler to store and retrieve Island classes
|
||||
// Note that these are saved by the BSkyBlock database
|
||||
handler = new Database<>(this, LevelsData.class);
|
||||
// Initialize the cache
|
||||
levelsCache = new HashMap<>();
|
||||
// Load the calculator
|
||||
levelPresenter = new LevelPresenter(this, this.getPlugin());
|
||||
// Start the top ten and register it for clicks
|
||||
topTen = new TopTen(this);
|
||||
registerListener(topTen);
|
||||
// Register commands for GameModes
|
||||
getPlugin().getAddonsManager().getGameModeAddons().stream()
|
||||
.filter(gm -> settings
|
||||
.getGameModes()
|
||||
.contains(gm
|
||||
.getDescription()
|
||||
.getName()))
|
||||
.forEach(gm -> {
|
||||
log("Level hooking into " + gm.getDescription().getName());
|
||||
registerCommands(gm);
|
||||
// Register placeholders
|
||||
if (getPlugin().getPlaceholdersManager() != null) {
|
||||
registerPlaceholders(gm);
|
||||
}
|
||||
});
|
||||
|
||||
// Register new island listener
|
||||
registerListener(new IslandTeamListeners(this));
|
||||
registerListener(new JoinLeaveListener(this));
|
||||
|
||||
// Register request handlers
|
||||
registerRequestHandler(new LevelRequestHandler(this));
|
||||
registerRequestHandler(new TopTenRequestHandler(this));
|
||||
|
||||
// Check if WildStackers is enabled on the server
|
||||
// I only added support for counting blocks into the island level
|
||||
// Someone else can PR if they want spawners added to the Leveling system :)
|
||||
CalcIslandLevel.stackersEnabled = Bukkit.getPluginManager().getPlugin("WildStacker") != null;
|
||||
|
||||
// Done
|
||||
}
|
||||
|
||||
private void registerPlaceholders(GameModeAddon gm) {
|
||||
// Island Level
|
||||
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||
gm.getDescription().getName().toLowerCase() + "_island_level",
|
||||
user -> getLevelPresenter().getLevelString(getIslandLevel(gm.getOverWorld(), user.getUniqueId())));
|
||||
|
||||
// Visited Island Level
|
||||
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||
gm.getDescription().getName().toLowerCase() + "_visited_island_level", user -> getVisitedIslandLevel(gm, user));
|
||||
|
||||
// Top Ten
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
final int rank = i;
|
||||
// Value
|
||||
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||
gm.getDescription().getName().toLowerCase() + "_top_value_" + rank, u -> String.valueOf(getTopTen().getTopTenLevel(gm.getOverWorld(), rank)));
|
||||
|
||||
// Name
|
||||
getPlugin().getPlaceholdersManager().registerPlaceholder(this,
|
||||
gm.getDescription().getName().toLowerCase() + "_top_name_" + rank,
|
||||
u -> getPlayers().getName(getTopTen().getTopTenUUID(gm.getOverWorld(), rank)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String getVisitedIslandLevel(GameModeAddon gm, User user) {
|
||||
if (!gm.inWorld(user.getLocation())) return "";
|
||||
return getIslands().getIslandAt(user.getLocation())
|
||||
.map(island -> getLevelPresenter().getLevelString(getIslandLevel(gm.getOverWorld(), island.getOwner())))
|
||||
.orElse("0");
|
||||
}
|
||||
|
||||
private void registerCommands(GameModeAddon gm) {
|
||||
gm.getAdminCommand().ifPresent(adminCommand -> {
|
||||
new AdminLevelCommand(this, adminCommand);
|
||||
new AdminTopCommand(this, adminCommand);
|
||||
});
|
||||
gm.getPlayerCommand().ifPresent(playerCmd -> {
|
||||
new IslandLevelCommand(this, playerCmd);
|
||||
new IslandTopCommand(this, playerCmd);
|
||||
new IslandValueCommand(this, playerCmd);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the levels to the database
|
||||
*/
|
||||
private void save(){
|
||||
// Remove any potential null values from the cache
|
||||
levelsCache.values().removeIf(Objects::isNull);
|
||||
levelsCache.values().forEach(handler::saveObjectAsync);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the player's level to a value
|
||||
* @param world - world
|
||||
* @param targetPlayer - target player
|
||||
* @param level - level
|
||||
*/
|
||||
public void setIslandLevel(World world, UUID targetPlayer, long level) {
|
||||
if (world == null || targetPlayer == null) {
|
||||
this.logError("Level: request to store a null " + world + " " + targetPlayer);
|
||||
return;
|
||||
}
|
||||
LevelsData ld = getLevelsData(targetPlayer);
|
||||
if (ld == null) {
|
||||
ld = new LevelsData(targetPlayer, level, world);
|
||||
} else {
|
||||
ld.setLevel(world, level);
|
||||
}
|
||||
// Add to cache
|
||||
levelsCache.put(targetPlayer, ld);
|
||||
topTen.addEntry(world, targetPlayer, getIslandLevel(world, targetPlayer));
|
||||
handler.saveObjectAsync(ld);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeros the initial island level
|
||||
* @param island - island
|
||||
* @param level - initial calculated island level
|
||||
*/
|
||||
public void setInitialIslandLevel(@NonNull Island island, long level) {
|
||||
if (island.getWorld() == null || island.getOwner() == null) {
|
||||
this.logError("Level: request to store a null (initial) " + island.getWorld() + " " + island.getOwner());
|
||||
return;
|
||||
}
|
||||
setIslandLevel(island.getWorld(), island.getOwner(), 0L);
|
||||
levelsCache.get(island.getOwner()).setInitialLevel(island.getWorld(), level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the initial island level
|
||||
* @param island - island
|
||||
* @return level or 0 by default
|
||||
*/
|
||||
public long getInitialIslandLevel(@NonNull Island island) {
|
||||
// Remove any potential null values from the cache
|
||||
levelsCache.values().removeIf(Objects::isNull);
|
||||
return levelsCache.containsKey(island.getOwner()) ? levelsCache.get(island.getOwner()).getInitialLevel(island.getWorld()) : 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return database handler
|
||||
*/
|
||||
@Nullable
|
||||
public Database<LevelsData> getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public static Addon getInstance() {
|
||||
return addon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the blockConfig
|
||||
@ -377,4 +184,34 @@ public class Level extends Addon {
|
||||
return blockConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the settings
|
||||
*/
|
||||
public ConfigSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the pipeliner
|
||||
*/
|
||||
public Pipeliner getPipeliner() {
|
||||
return pipeliner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the manager
|
||||
*/
|
||||
public LevelsManager getManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the config settings - used for tests only
|
||||
* @param configSettings - config settings
|
||||
*/
|
||||
void setSettings(ConfigSettings configSettings) {
|
||||
this.settings = configSettings;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,136 +0,0 @@
|
||||
package world.bentobox.level;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.World;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.level.calculators.PlayerLevel;
|
||||
|
||||
public class LevelPresenter {
|
||||
|
||||
private static final BigInteger THOUSAND = BigInteger.valueOf(1000);
|
||||
private static final TreeMap<BigInteger, String> LEVELS;
|
||||
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 int levelWait;
|
||||
private final Level addon;
|
||||
private final BentoBox plugin;
|
||||
|
||||
// Level calc cool down
|
||||
private final HashMap<UUID, Long> levelWaitTime = new HashMap<>();
|
||||
|
||||
public LevelPresenter(Level addon, BentoBox plugin) {
|
||||
this.addon = addon;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the island level
|
||||
* @param world - world to check
|
||||
* @param sender - asker of the level info
|
||||
* @param targetPlayer - target player
|
||||
*/
|
||||
public void calculateIslandLevel(World world, final User sender, UUID targetPlayer) {
|
||||
// Get permission prefix for this world
|
||||
String permPrefix = plugin.getIWM().getPermissionPrefix(world);
|
||||
// Check if target has island
|
||||
boolean inTeam = false;
|
||||
if (!plugin.getIslands().hasIsland(world, targetPlayer)) {
|
||||
// Player may be in a team
|
||||
if (plugin.getIslands().inTeam(world, targetPlayer)) {
|
||||
targetPlayer = plugin.getIslands().getOwner(world, targetPlayer);
|
||||
inTeam = true;
|
||||
} else {
|
||||
if (sender != null) sender.sendMessage("general.errors.player-has-no-island");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Player asking for their own island calc
|
||||
if (sender == null || inTeam || !sender.isPlayer() || sender.getUniqueId().equals(targetPlayer) || sender.isOp() || sender.hasPermission(permPrefix + "mod.info")) {
|
||||
// Newer better system - uses chunks
|
||||
if (sender == null || !onLevelWaitTime(sender) || levelWait <= 0 || sender.isOp() || sender.hasPermission(permPrefix + "mod.info")) {
|
||||
if (sender != null) {
|
||||
sender.sendMessage("island.level.calculating");
|
||||
setLevelWaitTime(sender);
|
||||
}
|
||||
new PlayerLevel(addon, plugin.getIslands().getIsland(world, targetPlayer), targetPlayer, sender);
|
||||
} else {
|
||||
// Cooldown
|
||||
sender.sendMessage("island.level.cooldown", "[time]", String.valueOf(getLevelWaitTime(sender)));
|
||||
}
|
||||
|
||||
} else {
|
||||
long lvl = addon.getIslandLevel(world, targetPlayer);
|
||||
|
||||
sender.sendMessage("island.level.island-level-is","[level]", getLevelString(lvl));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string representation of the level. May be converted to shorthand notation, e.g., 104556 = 10.5k
|
||||
* @param lvl - long value to represent
|
||||
* @return string of the level.
|
||||
*/
|
||||
public String getLevelString(long lvl) {
|
||||
String level = String.valueOf(lvl);
|
||||
// Asking for the level of another player
|
||||
if(addon.getSettings().isShorthand()) {
|
||||
BigInteger levelValue = BigInteger.valueOf(lvl);
|
||||
|
||||
Map.Entry<BigInteger, String> stage = LEVELS.floorEntry(levelValue);
|
||||
|
||||
if (stage != null) { // level > 1000
|
||||
// 1 052 -> 1.0k
|
||||
// 1 527 314 -> 1.5M
|
||||
// 3 874 130 021 -> 3.8G
|
||||
// 4 002 317 889 -> 4.0T
|
||||
level = new DecimalFormat("#.#").format(levelValue.divide(stage.getKey().divide(THOUSAND)).doubleValue()/1000.0) + stage.getValue();
|
||||
}
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets cool down for the level command
|
||||
*
|
||||
* @param user - user
|
||||
*/
|
||||
private void setLevelWaitTime(final User user) {
|
||||
levelWaitTime.put(user.getUniqueId(), Calendar.getInstance().getTimeInMillis() + levelWait * 1000);
|
||||
}
|
||||
|
||||
private boolean onLevelWaitTime(final User sender) {
|
||||
if (levelWaitTime.containsKey(sender.getUniqueId())) {
|
||||
return levelWaitTime.get(sender.getUniqueId()) > Calendar.getInstance().getTimeInMillis();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private long getLevelWaitTime(final User sender) {
|
||||
if (levelWaitTime.containsKey(sender.getUniqueId())) {
|
||||
if (levelWaitTime.get(sender.getUniqueId()) > Calendar.getInstance().getTimeInMillis()) {
|
||||
return (levelWaitTime.get(sender.getUniqueId()) - Calendar.getInstance().getTimeInMillis()) / 1000;
|
||||
}
|
||||
|
||||
return 0L;
|
||||
}
|
||||
|
||||
return 0L;
|
||||
}
|
||||
}
|
375
src/main/java/world/bentobox/level/LevelsManager.java
Normal file
375
src/main/java/world/bentobox/level/LevelsManager.java
Normal file
@ -0,0 +1,375 @@
|
||||
package world.bentobox.level;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.World;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.api.events.addon.AddonEvent;
|
||||
import world.bentobox.bentobox.api.panels.PanelItem;
|
||||
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
|
||||
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.Database;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.level.calculators.Results;
|
||||
import world.bentobox.level.events.IslandLevelCalculatedEvent;
|
||||
import world.bentobox.level.events.IslandPreLevelEvent;
|
||||
import world.bentobox.level.objects.LevelsData;
|
||||
import world.bentobox.level.objects.TopTenData;
|
||||
|
||||
public class LevelsManager {
|
||||
private static final String INTOPTEN = "intopten";
|
||||
private static final TreeMap<BigInteger, String> LEVELS;
|
||||
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 Level addon;
|
||||
|
||||
|
||||
// Database handler for level data
|
||||
private final Database<LevelsData> handler;
|
||||
// A cache of island levels.
|
||||
private final Map<UUID, LevelsData> levelsCache;
|
||||
|
||||
private final int[] SLOTS = new int[] {4, 12, 14, 19, 20, 21, 22, 23, 24, 25};
|
||||
private final Database<TopTenData> topTenHandler;
|
||||
// Top ten lists
|
||||
private Map<World,TopTenData> topTenLists;
|
||||
|
||||
|
||||
public LevelsManager(Level addon) {
|
||||
this.addon = addon;
|
||||
// Get the BentoBox database
|
||||
// Set up the database handler to store and retrieve data
|
||||
// Note that these are saved by the BentoBox database
|
||||
handler = new Database<>(addon, LevelsData.class);
|
||||
// Top Ten handler
|
||||
topTenHandler = new Database<>(addon, TopTenData.class);
|
||||
// Initialize the cache
|
||||
levelsCache = new HashMap<>();
|
||||
// Initialize top ten lists
|
||||
topTenLists = new HashMap<>();
|
||||
}
|
||||
|
||||
private void addToTopTen(@NonNull World world, @NonNull UUID targetPlayer, long lv) {
|
||||
topTenLists.computeIfAbsent(world, k -> new TopTenData(world));
|
||||
if (hasTopTenPerm(world, targetPlayer)) {
|
||||
topTenLists.get(world).getTopTen().put(targetPlayer, lv);
|
||||
} else {
|
||||
topTenLists.get(world).getTopTen().remove(targetPlayer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the island level, set all island member's levels to the result and try to add the owner to the top ten
|
||||
* @param targetPlayer - uuid of targeted player - owner or team member
|
||||
* @param island - island to calculate
|
||||
* @return completable future with the results of the calculation
|
||||
*/
|
||||
public CompletableFuture<Results> calculateLevel(UUID targetPlayer, Island island) {
|
||||
CompletableFuture<Results> result = new CompletableFuture<>();
|
||||
// Fire pre-level calc event
|
||||
IslandPreLevelEvent e = new IslandPreLevelEvent(targetPlayer, island);
|
||||
Bukkit.getPluginManager().callEvent(e);
|
||||
if (e.isCancelled()) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
// Add island to the pipeline
|
||||
addon.getPipeliner().addIsland(island).thenAccept(r -> {
|
||||
// Results are irrelevant because the island is unowned or deleted, or IslandLevelCalcEvent is cancelled
|
||||
if (r == null || fireIslandLevelCalcEvent(targetPlayer, island, r)) {
|
||||
result.complete(null);
|
||||
}
|
||||
// Save result
|
||||
island.getMemberSet().forEach(uuid -> setIslandLevel(island.getWorld(), uuid, r.getLevel()));
|
||||
addToTopTen(island.getWorld(), island.getOwner(), r.getLevel());
|
||||
result.complete(r);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean fireIslandLevelCalcEvent(UUID targetPlayer, Island island, Results results) {
|
||||
// Fire post calculation event
|
||||
IslandLevelCalculatedEvent ilce = new IslandLevelCalculatedEvent(targetPlayer, island, results);
|
||||
Bukkit.getPluginManager().callEvent(ilce);
|
||||
// This exposes these values to plugins via the event
|
||||
Map<String, Object> keyValues = new HashMap<>();
|
||||
keyValues.put("eventName", "IslandLevelCalculatedEvent");
|
||||
keyValues.put("targetPlayer", targetPlayer);
|
||||
keyValues.put("islandUUID", island.getUniqueId());
|
||||
keyValues.put("level", results.getLevel());
|
||||
keyValues.put("pointsToNextLevel", results.getPointsToNextLevel());
|
||||
keyValues.put("deathHandicap", results.getDeathHandicap());
|
||||
keyValues.put("initialLevel", results.getInitialLevel());
|
||||
new AddonEvent().builder().addon(addon).keyValues(keyValues).build();
|
||||
results = ilce.getResults();
|
||||
return ilce.isCancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string representation of the level. May be converted to shorthand notation, e.g., 104556 = 10.5k
|
||||
* @param lvl - long value to represent
|
||||
* @return string of the level.
|
||||
*/
|
||||
public String formatLevel(long lvl) {
|
||||
String level = String.valueOf(lvl);
|
||||
// Asking for the level of another player
|
||||
if(addon.getSettings().isShorthand()) {
|
||||
BigInteger levelValue = BigInteger.valueOf(lvl);
|
||||
|
||||
Map.Entry<BigInteger, String> stage = LEVELS.floorEntry(levelValue);
|
||||
|
||||
if (stage != null) { // level > 1000
|
||||
// 1 052 -> 1.0k
|
||||
// 1 527 314 -> 1.5M
|
||||
// 3 874 130 021 -> 3.8G
|
||||
// 4 002 317 889 -> 4.0T
|
||||
level = new DecimalFormat("#.#").format(levelValue.divide(stage.getKey().divide(THOUSAND)).doubleValue()/1000.0) + stage.getValue();
|
||||
}
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the Top Ten list
|
||||
* @param world - world
|
||||
* @param user - the requesting player
|
||||
*/
|
||||
public void getGUI(World world, final User user) {
|
||||
// Check world
|
||||
Map<UUID, Long> topTen = getTopTen(world, 10);
|
||||
|
||||
PanelBuilder panel = new PanelBuilder()
|
||||
.name(user.getTranslation("island.top.gui-title"))
|
||||
.user(user);
|
||||
int i = 0;
|
||||
for (Entry<UUID, Long> m : topTen.entrySet()) {
|
||||
panel.item(SLOTS[i], getHead(i + 1, m.getValue(), m.getKey(), user, world));
|
||||
}
|
||||
panel.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the head panel item
|
||||
* @param rank - the top ten rank of this player/team. Can be used in the name of the island for vanity.
|
||||
* @param level - the level of the island
|
||||
* @param playerUUID - the UUID of the top ten player
|
||||
* @param asker - the asker of the top ten
|
||||
* @return PanelItem
|
||||
*/
|
||||
private PanelItem getHead(int rank, long level, UUID playerUUID, User asker, World world) {
|
||||
final String name = addon.getPlayers().getName(playerUUID);
|
||||
List<String> description = new ArrayList<>();
|
||||
description.add(asker.getTranslation("island.top.gui-heading", "[name]", name, "[rank]", String.valueOf(rank)));
|
||||
description.add(asker.getTranslation("island.top.island-level","[level]", formatLevel(level)));
|
||||
if (addon.getIslands().inTeam(world, playerUUID)) {
|
||||
List<String> memberList = new ArrayList<>();
|
||||
for (UUID members : addon.getIslands().getMembers(world, playerUUID)) {
|
||||
memberList.add(ChatColor.AQUA + addon.getPlayers().getName(members));
|
||||
}
|
||||
description.addAll(memberList);
|
||||
}
|
||||
|
||||
PanelItemBuilder builder = new PanelItemBuilder()
|
||||
.icon(name)
|
||||
.name(name)
|
||||
.description(description);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the initial level of the island. Used to zero island levels
|
||||
* @param island - island
|
||||
* @return initial level of island
|
||||
*/
|
||||
public long getInitialLevel(Island island) {
|
||||
@Nullable
|
||||
LevelsData ld = getLevelsData(island.getOwner());
|
||||
return ld == null ? 0 : ld.getInitialLevel(island.getWorld());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get level from cache for a player.
|
||||
* @param world - world where the island is
|
||||
* @param targetPlayer - target player UUID
|
||||
* @return Level of player or zero if player is unknown or UUID is null
|
||||
*/
|
||||
public long getIslandLevel(@NonNull World world, @Nullable UUID targetPlayer) {
|
||||
if (targetPlayer == null) return 0L;
|
||||
LevelsData ld = getLevelsData(targetPlayer);
|
||||
return ld == null ? 0L : ld.getLevel(world);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a formatted string of the target player's island level
|
||||
* @param world - world where the island is
|
||||
* @param targetPlayer - target player's UUID
|
||||
* @return Formatted level of player or zero if player is unknown or UUID is null
|
||||
*/
|
||||
public String getIslandLevelString(@NonNull World world, @Nullable UUID targetPlayer) {
|
||||
return formatLevel(getIslandLevel(world, targetPlayer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a player from the cache or database
|
||||
* @param targetPlayer - UUID of target player
|
||||
* @return LevelsData object or null if not found
|
||||
*/
|
||||
@Nullable
|
||||
public LevelsData getLevelsData(@NonNull UUID targetPlayer) {
|
||||
// Get from database if not in cache
|
||||
if (!levelsCache.containsKey(targetPlayer) && handler.objectExists(targetPlayer.toString())) {
|
||||
LevelsData ld = handler.loadObject(targetPlayer.toString());
|
||||
if (ld != null) {
|
||||
levelsCache.put(targetPlayer, ld);
|
||||
} else {
|
||||
handler.deleteID(targetPlayer.toString());
|
||||
}
|
||||
}
|
||||
// Return cached value or null
|
||||
return levelsCache.get(targetPlayer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of points required until the next level since the last level calc
|
||||
* @param world - world where the island is
|
||||
* @param targetPlayer - target player UUID
|
||||
* @return string with the number required or blank if the player is unknown
|
||||
*/
|
||||
public String getPointsToNextString(@NonNull World world, @Nullable UUID targetPlayer) {
|
||||
if (targetPlayer == null) return "";
|
||||
LevelsData ld = getLevelsData(targetPlayer);
|
||||
return ld == null ? "" : String.valueOf(ld.getPointsToNextLevel(world));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the top ten for this world. Returns offline players or players with the intopten permission.
|
||||
* @param world - world requested
|
||||
* @param size - size of the top ten
|
||||
* @return sorted top ten map
|
||||
*/
|
||||
public Map<UUID, Long> getTopTen(World world, int size) {
|
||||
topTenLists.computeIfAbsent(world, k -> new TopTenData(k));
|
||||
// Remove player from top ten if they are online and do not have the perm
|
||||
topTenLists.get(world).getTopTen().keySet().removeIf(u -> !hasTopTenPerm(world, u));
|
||||
// Return the sorted map
|
||||
return Collections.unmodifiableMap(topTenLists.get(world).getTopTen().entrySet().stream()
|
||||
.filter(l -> l.getValue() > 0)
|
||||
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).limit(size)
|
||||
.collect(Collectors.toMap(
|
||||
Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if player has the correct top ten perm to have their level saved
|
||||
* @param world
|
||||
* @param targetPlayer
|
||||
* @return
|
||||
*/
|
||||
boolean hasTopTenPerm(@NonNull World world, @NonNull UUID targetPlayer) {
|
||||
String permPrefix = addon.getPlugin().getIWM().getPermissionPrefix(world);
|
||||
boolean hasPerm = Bukkit.getPlayer(targetPlayer) != null && Bukkit.getPlayer(targetPlayer).hasPermission(permPrefix + INTOPTEN);
|
||||
return hasPerm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all the top tens from the database
|
||||
*/
|
||||
void loadTopTens() {
|
||||
topTenLists = new HashMap<>();
|
||||
topTenHandler.loadObjects().forEach(tt -> {
|
||||
World world = Bukkit.getWorld(tt.getUniqueId());
|
||||
if (world != null) {
|
||||
topTenLists.put(world, tt);
|
||||
addon.log("Loaded TopTen for " + world.getName());
|
||||
// Update based on user data
|
||||
for (UUID uuid : tt.getTopTen().keySet()) {
|
||||
tt.getTopTen().compute(uuid, (k,v) -> v = updateLevel(k, world));
|
||||
}
|
||||
} else {
|
||||
addon.logError("TopTen world '" + tt.getUniqueId() + "' is not known on server. You might want to delete this table. Skipping...");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a player from a world's top ten and removes world from player's level data
|
||||
* @param world - world
|
||||
* @param uuid - the player's uuid
|
||||
*/
|
||||
public void removeEntry(World world, UUID uuid) {
|
||||
if (levelsCache.containsKey(uuid)) {
|
||||
levelsCache.get(uuid).remove(world);
|
||||
// Save
|
||||
handler.saveObjectAsync(levelsCache.get(uuid));
|
||||
}
|
||||
if (topTenLists.containsKey(world)) {
|
||||
topTenLists.get(world).getTopTen().remove(uuid);
|
||||
topTenHandler.saveObjectAsync(topTenLists.get(world));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all player data and the top ten
|
||||
*/
|
||||
public void save() {
|
||||
levelsCache.values().forEach(handler::saveObjectAsync);
|
||||
topTenLists.values().forEach(topTenHandler::saveObjectAsync);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an initial island level for player
|
||||
* @param island - the island to set. Must have a non-null world and owner
|
||||
* @param lv - initial island level
|
||||
*/
|
||||
public void setInitialIslandLevel(@NonNull Island island, long lv) {
|
||||
if (island.getOwner() == null || island.getWorld() == null) return;
|
||||
levelsCache.computeIfAbsent(island.getOwner(), k -> new LevelsData(k)).setInitialLevel(island.getWorld(), lv);
|
||||
handler.saveObjectAsync(levelsCache.get(island.getOwner()));
|
||||
}
|
||||
|
||||
public void setIslandLevel(@NonNull World world, @NonNull UUID targetPlayer, long lv) {
|
||||
levelsCache.computeIfAbsent(targetPlayer, k -> new LevelsData(targetPlayer)).setLevel(world, lv);
|
||||
handler.saveObjectAsync(levelsCache.get(targetPlayer));
|
||||
// Add to Top Ten
|
||||
addToTopTen(world, targetPlayer, lv);
|
||||
}
|
||||
|
||||
private Long updateLevel(UUID uuid, World world) {
|
||||
if (handler.objectExists(uuid.toString())) {
|
||||
@Nullable
|
||||
LevelsData ld = handler.loadObject(uuid.toString());
|
||||
if (ld != null) {
|
||||
return ld.getLevel(world);
|
||||
}
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -1,199 +0,0 @@
|
||||
package world.bentobox.level;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.api.panels.PanelItem;
|
||||
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
|
||||
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.Database;
|
||||
import world.bentobox.level.objects.TopTenData;
|
||||
|
||||
/**
|
||||
* Handles all Top Ten List functions
|
||||
*
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
public class TopTen implements Listener {
|
||||
private final Level addon;
|
||||
// Top ten list of players
|
||||
private Map<World,TopTenData> topTenList;
|
||||
private final int[] SLOTS = new int[] {4, 12, 14, 19, 20, 21, 22, 23, 24, 25};
|
||||
private final Database<TopTenData> handler;
|
||||
|
||||
public TopTen(Level addon) {
|
||||
this.addon = addon;
|
||||
// Set up the database handler to store and retrieve the TopTenList class
|
||||
// Note that these are saved in the BSkyBlock database
|
||||
handler = new Database<>(addon, TopTenData.class);
|
||||
loadTopTen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all the top tens from the database
|
||||
*/
|
||||
private void loadTopTen() {
|
||||
topTenList = new HashMap<>();
|
||||
handler.loadObjects().forEach(tt -> {
|
||||
World world = Bukkit.getWorld(tt.getUniqueId());
|
||||
if (world != null) {
|
||||
topTenList.put(world, tt);
|
||||
addon.log("Loaded TopTen for " + world.getName());
|
||||
} else {
|
||||
addon.logError("TopTen world " + tt.getUniqueId() + " is not known on server. Skipping...");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a player to the top ten, if the level is good enough
|
||||
*
|
||||
* @param ownerUUID - owner UUID
|
||||
* @param l - level
|
||||
*/
|
||||
public void addEntry(World world, UUID ownerUUID, long l) {
|
||||
// Check if player is an island owner or not
|
||||
if (!addon.getIslands().isOwner(world, ownerUUID)) {
|
||||
return;
|
||||
}
|
||||
// Set up world data
|
||||
topTenList.putIfAbsent(world, new TopTenData());
|
||||
topTenList.get(world).setUniqueId(world.getName());
|
||||
|
||||
// Try and see if the player is online
|
||||
Player player = Bukkit.getServer().getPlayer(ownerUUID);
|
||||
if (player != null && !player.hasPermission(addon.getPlugin().getIWM().getPermissionPrefix(world) + "intopten")) {
|
||||
topTenList.get(world).remove(ownerUUID);
|
||||
return;
|
||||
}
|
||||
topTenList.get(world).addLevel(ownerUUID, l);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the Top Ten list
|
||||
* @param world - world
|
||||
* @param user - the requesting player
|
||||
*/
|
||||
public void getGUI(World world, final User user, String permPrefix) {
|
||||
// Check world
|
||||
topTenList.putIfAbsent(world, new TopTenData());
|
||||
topTenList.get(world).setUniqueId(world.getName());
|
||||
|
||||
PanelBuilder panel = new PanelBuilder()
|
||||
.name(user.getTranslation("island.top.gui-title"))
|
||||
.user(user);
|
||||
|
||||
int i = 1;
|
||||
Iterator<Entry<UUID, Long>> it = topTenList.get(world).getTopTen().entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<UUID, Long> m = it.next();
|
||||
UUID topTenUUID = m.getKey();
|
||||
// Remove from TopTen if the player is online and has the permission
|
||||
Player entry = Bukkit.getServer().getPlayer(topTenUUID);
|
||||
boolean show = true;
|
||||
if (entry != null) {
|
||||
if (!entry.hasPermission(permPrefix + "intopten")) {
|
||||
it.remove();
|
||||
show = false;
|
||||
// Remove from Top Ten completely
|
||||
topTenList.get(world).remove(topTenUUID);
|
||||
}
|
||||
}
|
||||
if (show) {
|
||||
panel.item(SLOTS[i-1], getHead(i, m.getValue(), topTenUUID, user, world));
|
||||
if (i++ == 10) break;
|
||||
}
|
||||
}
|
||||
panel.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the head panel item
|
||||
* @param rank - the top ten rank of this player/team. Can be used in the name of the island for vanity.
|
||||
* @param level - the level of the island
|
||||
* @param playerUUID - the UUID of the top ten player
|
||||
* @param asker - the asker of the top ten
|
||||
* @return PanelItem
|
||||
*/
|
||||
private PanelItem getHead(int rank, long level, UUID playerUUID, User asker, World world) {
|
||||
final String name = addon.getPlayers().getName(playerUUID);
|
||||
List<String> description = new ArrayList<>();
|
||||
description.add(asker.getTranslation("island.top.gui-heading", "[name]", name, "[rank]", String.valueOf(rank)));
|
||||
description.add(asker.getTranslation("island.top.island-level","[level]", addon.getLevelPresenter().getLevelString(level)));
|
||||
if (addon.getIslands().inTeam(world, playerUUID)) {
|
||||
List<String> memberList = new ArrayList<>();
|
||||
for (UUID members : addon.getIslands().getMembers(world, playerUUID)) {
|
||||
memberList.add(ChatColor.AQUA + addon.getPlayers().getName(members));
|
||||
}
|
||||
description.addAll(memberList);
|
||||
}
|
||||
|
||||
PanelItemBuilder builder = new PanelItemBuilder()
|
||||
.icon(name)
|
||||
.name(name)
|
||||
.description(description);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the top ten list for this world
|
||||
* @param world - world
|
||||
* @return top ten data object
|
||||
*/
|
||||
@NonNull
|
||||
public TopTenData getTopTenList(World world) {
|
||||
return topTenList.computeIfAbsent(world, k -> new TopTenData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the UUID for this rank in this world
|
||||
* @param world - world
|
||||
* @param rank - rank between 1 and 10
|
||||
* @return UUID or null
|
||||
*/
|
||||
@Nullable
|
||||
public UUID getTopTenUUID(World world, int rank) {
|
||||
return getTopTenList(world).getTopTenUUID(rank);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the island level for this rank in this world
|
||||
* @param world - world
|
||||
* @param rank - rank between 1 and 10
|
||||
* @return level or 0
|
||||
*/
|
||||
public long getTopTenLevel(World world, int rank) {
|
||||
return getTopTenList(world).getTopTenLevel(rank);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes ownerUUID from the top ten list
|
||||
*
|
||||
* @param ownerUUID - uuid to remove
|
||||
*/
|
||||
public void removeEntry(World world, UUID ownerUUID) {
|
||||
topTenList.putIfAbsent(world, new TopTenData());
|
||||
topTenList.get(world).setUniqueId(world.getName());
|
||||
topTenList.get(world).remove(ownerUUID);
|
||||
}
|
||||
|
||||
public void saveTopTen() {
|
||||
topTenList.values().forEach(handler::saveObjectAsync);
|
||||
}
|
||||
|
||||
}
|
@ -1,534 +0,0 @@
|
||||
package world.bentobox.level.calculators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.bgsoftware.wildstacker.api.WildStackerAPI;
|
||||
import com.bgsoftware.wildstacker.api.objects.StackedBarrel;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.ChunkSnapshot;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Tag;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.Slab;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.common.collect.Multiset.Entry;
|
||||
import com.google.common.collect.Multisets;
|
||||
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.util.Pair;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
import world.bentobox.level.Level;
|
||||
|
||||
|
||||
public class CalcIslandLevel {
|
||||
|
||||
private static final String LINE_BREAK = "==================================";
|
||||
|
||||
public static final long MAX_AMOUNT = 10000;
|
||||
public static Boolean stackersEnabled;
|
||||
|
||||
private final Level addon;
|
||||
|
||||
private final Set<Pair<Integer, Integer>> chunksToScan;
|
||||
private final Island island;
|
||||
private final Results result;
|
||||
private final Runnable onExit;
|
||||
|
||||
// Copy the limits hash map
|
||||
private final HashMap<Material, Integer> limitCount;
|
||||
private final List<World> worlds;
|
||||
private final World world;
|
||||
|
||||
private AtomicInteger count;
|
||||
|
||||
private int total;
|
||||
private Queue<Chunk> q;
|
||||
private int queueid;
|
||||
|
||||
/**
|
||||
* Calculate the island's level
|
||||
* Results are available in {@link CalcIslandLevel.Results}
|
||||
* @param addon - Level addon
|
||||
* @param island - island to be calculated
|
||||
* @param onExit - what to run when done
|
||||
*/
|
||||
public CalcIslandLevel(final Level addon, final Island island, final Runnable onExit) {
|
||||
this.addon = addon;
|
||||
this.island = island;
|
||||
this.world = island.getWorld();
|
||||
this.limitCount = new HashMap<>(addon.getBlockConfig().getBlockLimits());
|
||||
this.onExit = onExit;
|
||||
this.worlds = new ArrayList<>();
|
||||
this.worlds.add(world);
|
||||
q = new LinkedList<>();
|
||||
|
||||
// Results go here
|
||||
result = new Results();
|
||||
|
||||
// Set the initial island handicap
|
||||
result.setInitialLevel(addon.getInitialIslandLevel(island));
|
||||
|
||||
// Get chunks to scan
|
||||
chunksToScan = getChunksToScan(island);
|
||||
count = new AtomicInteger();
|
||||
// Total number of chunks to scan
|
||||
total = chunksToScan.size();
|
||||
// Add nether world scanning
|
||||
if (addon.getSettings().isNether()) {
|
||||
World netherWorld = addon.getPlugin().getIWM().getNetherWorld(world);
|
||||
if (netherWorld != null) {
|
||||
this.worlds.add(netherWorld);
|
||||
total += chunksToScan.size();
|
||||
}
|
||||
}
|
||||
// Add End world scanning
|
||||
if (addon.getSettings().isEnd()) {
|
||||
World endWorld = addon.getPlugin().getIWM().getEndWorld(world);
|
||||
if (endWorld != null) {
|
||||
this.worlds.add(endWorld);
|
||||
total += chunksToScan.size();
|
||||
}
|
||||
}
|
||||
queueid = Bukkit.getScheduler().scheduleSyncRepeatingTask(addon.getPlugin(), () -> {
|
||||
for (int i = 0; i < addon.getSettings().getChunks(); i++) {
|
||||
if (q.size() == 0) {
|
||||
return;
|
||||
}
|
||||
Chunk c = q.remove();
|
||||
getChunk(c);
|
||||
}
|
||||
}, addon.getSettings().getTaskDelay(), addon.getSettings().getTaskDelay());
|
||||
chunksToScan.forEach(c -> worlds.forEach(w -> Util.getChunkAtAsync(w, c.x, c.z).thenAccept(this::addChunkQueue)));
|
||||
|
||||
}
|
||||
|
||||
private void addChunkQueue(Chunk ch) {
|
||||
q.add(ch);
|
||||
}
|
||||
|
||||
private void getChunk(Chunk ch) {
|
||||
ChunkSnapshot snapShot = ch.getChunkSnapshot();
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> {
|
||||
this.scanChunk(snapShot, ch);
|
||||
count.getAndIncrement();
|
||||
if (count.get() == total) {
|
||||
Bukkit.getScheduler().cancelTask(queueid);
|
||||
this.tidyUp();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void scanChunk(ChunkSnapshot chunk, Chunk physicalChunk) {
|
||||
World chunkWorld = Bukkit.getWorld(chunk.getWorldName());
|
||||
if (chunkWorld == null) return;
|
||||
int maxHeight = chunkWorld.getMaxHeight();
|
||||
|
||||
for (int x = 0; x< 16; x++) {
|
||||
// Check if the block coordinate is inside the protection zone and if not, don't count it
|
||||
if (chunk.getX() * 16 + x < island.getMinProtectedX() || chunk.getX() * 16 + x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
|
||||
continue;
|
||||
}
|
||||
for (int z = 0; z < 16; z++) {
|
||||
// Check if the block coordinate is inside the protection zone and if not, don't count it
|
||||
if (chunk.getZ() * 16 + z < island.getMinProtectedZ() || chunk.getZ() * 16 + z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int y = 0; y < maxHeight; y++) {
|
||||
BlockData blockData = chunk.getBlockData(x, y, z);
|
||||
int seaHeight = addon.getPlugin().getIWM().getSeaHeight(chunkWorld);
|
||||
boolean belowSeaLevel = seaHeight > 0 && y <= seaHeight;
|
||||
// Slabs can be doubled, so check them twice
|
||||
if (Tag.SLABS.isTagged(blockData.getMaterial())) {
|
||||
Slab slab = (Slab)blockData;
|
||||
if (slab.getType().equals(Slab.Type.DOUBLE)) {
|
||||
checkBlock(blockData.getMaterial(), belowSeaLevel);
|
||||
}
|
||||
}
|
||||
|
||||
// Hook for Wild Stackers (Blocks Only)
|
||||
if (stackersEnabled && blockData.getMaterial() == Material.CAULDRON) {
|
||||
Block cauldronBlock = physicalChunk.getBlock(x, y, z);
|
||||
if (WildStackerAPI.getWildStacker().getSystemManager().isStackedBarrel(cauldronBlock)) {
|
||||
StackedBarrel barrel = WildStackerAPI.getStackedBarrel(cauldronBlock);
|
||||
int barrelAmt = WildStackerAPI.getBarrelAmount(cauldronBlock);
|
||||
for (int _x = 0; _x < barrelAmt; _x++) {
|
||||
checkBlock(barrel.getType(), belowSeaLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkBlock(blockData.getMaterial(), belowSeaLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Didnt see a reason to pass BlockData when all that's used was the material
|
||||
private void checkBlock(Material mat, boolean belowSeaLevel) {
|
||||
int count = limitCount(mat);
|
||||
if (belowSeaLevel) {
|
||||
result.underWaterBlockCount.addAndGet(count);
|
||||
result.uwCount.add(mat);
|
||||
} else {
|
||||
result.rawBlockCount.addAndGet(count);
|
||||
result.mdCount.add(mat);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a block has been limited or not and whether a block has any value or not
|
||||
* @param md Material
|
||||
* @return value of the block if can be counted
|
||||
*/
|
||||
private int limitCount(Material md) {
|
||||
if (limitCount.containsKey(md)) {
|
||||
int count = limitCount.get(md);
|
||||
if (count > 0) {
|
||||
limitCount.put(md, --count);
|
||||
return getValue(md);
|
||||
} else {
|
||||
result.ofCount.add(md);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return getValue(md);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value of a material
|
||||
* World blocks trump regular block values
|
||||
* @param md - Material to check
|
||||
* @return value of a material
|
||||
*/
|
||||
private int getValue(Material md) {
|
||||
Integer value = addon.getBlockConfig().getValue(world, md);
|
||||
if (value == null) {
|
||||
// Not in config
|
||||
result.ncCount.add(md);
|
||||
return 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a set of all the chunks in island
|
||||
* @param island - island
|
||||
* @return - set of pairs of x,z coordinates to check
|
||||
*/
|
||||
private Set<Pair<Integer, Integer>> getChunksToScan(Island island) {
|
||||
Set<Pair<Integer, Integer>> chunkSnapshot = new HashSet<>();
|
||||
for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionRange() * 2 + 16); x += 16) {
|
||||
for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + island.getProtectionRange() * 2 + 16); z += 16) {
|
||||
chunkSnapshot.add(new Pair<>(x >> 4, z >> 4));
|
||||
}
|
||||
}
|
||||
return chunkSnapshot;
|
||||
}
|
||||
|
||||
private void tidyUp() {
|
||||
// Finalize calculations
|
||||
result.rawBlockCount.addAndGet((long)(result.underWaterBlockCount.get() * addon.getSettings().getUnderWaterMultiplier()));
|
||||
|
||||
// Set the death penalty
|
||||
if (this.addon.getSettings().isSumTeamDeaths())
|
||||
{
|
||||
for (UUID uuid : this.island.getMemberSet())
|
||||
{
|
||||
this.result.deathHandicap.addAndGet(this.addon.getPlayers().getDeaths(this.world, uuid));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// At this point, it may be that the island has become unowned.
|
||||
this.result.deathHandicap.set(this.island.getOwner() == null ? 0 :
|
||||
this.addon.getPlayers().getDeaths(this.world, this.island.getOwner()));
|
||||
}
|
||||
|
||||
long blockAndDeathPoints = this.result.rawBlockCount.get();
|
||||
|
||||
if (this.addon.getSettings().getDeathPenalty() > 0)
|
||||
{
|
||||
// Proper death penalty calculation.
|
||||
blockAndDeathPoints -= this.result.deathHandicap.get() * this.addon.getSettings().getDeathPenalty();
|
||||
}
|
||||
this.result.level.set(calculateLevel(blockAndDeathPoints));
|
||||
|
||||
// Calculate how many points are required to get to the next level
|
||||
long nextLevel = this.result.level.get();
|
||||
long blocks = blockAndDeathPoints;
|
||||
while (nextLevel < this.result.level.get() + 1 && blocks - blockAndDeathPoints < MAX_AMOUNT) {
|
||||
nextLevel = calculateLevel(++blocks);
|
||||
}
|
||||
this.result.pointsToNextLevel.set(blocks - blockAndDeathPoints);
|
||||
|
||||
// Report
|
||||
result.report = getReport();
|
||||
// All done.
|
||||
if (onExit != null) {
|
||||
Bukkit.getScheduler().runTask(addon.getPlugin(), onExit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private long calculateLevel(long blockAndDeathPoints) {
|
||||
String calcString = addon.getSettings().getLevelCalc();
|
||||
String withValues = calcString.replace("blocks", String.valueOf(blockAndDeathPoints)).replace("level_cost", String.valueOf(this.addon.getSettings().getLevelCost()));
|
||||
return (long)eval(withValues) - this.island.getLevelHandicap() - result.initialLevel.get();
|
||||
}
|
||||
|
||||
private List<String> getReport() {
|
||||
List<String> reportLines = new ArrayList<>();
|
||||
// provide counts
|
||||
reportLines.add("Level Log for island in " + addon.getPlugin().getIWM().getFriendlyName(island.getWorld()) + " at " + Util.xyz(island.getCenter().toVector()));
|
||||
reportLines.add("Island owner UUID = " + island.getOwner());
|
||||
reportLines.add("Total block value count = " + String.format("%,d",result.rawBlockCount.get()));
|
||||
reportLines.add("Formula to calculate island level: " + addon.getSettings().getLevelCalc());
|
||||
reportLines.add("Level cost = " + addon.getSettings().getLevelCost());
|
||||
reportLines.add("Deaths handicap = " + result.deathHandicap.get());
|
||||
reportLines.add("Initial island level = " + (0L - result.initialLevel.get()));
|
||||
reportLines.add("Level calculated = " + result.level.get());
|
||||
reportLines.add(LINE_BREAK);
|
||||
int total = 0;
|
||||
if (!result.uwCount.isEmpty()) {
|
||||
reportLines.add("Underwater block count (Multiplier = x" + addon.getSettings().getUnderWaterMultiplier() + ") value");
|
||||
reportLines.add("Total number of underwater blocks = " + String.format("%,d",result.uwCount.size()));
|
||||
reportLines.addAll(sortedReport(total, result.uwCount));
|
||||
}
|
||||
reportLines.add("Regular block count");
|
||||
reportLines.add("Total number of blocks = " + String.format("%,d",result.mdCount.size()));
|
||||
reportLines.addAll(sortedReport(total, result.mdCount));
|
||||
|
||||
reportLines.add("Blocks not counted because they exceeded limits: " + String.format("%,d",result.ofCount.size()));
|
||||
Iterable<Multiset.Entry<Material>> entriesSortedByCount = result.ofCount.entrySet();
|
||||
Iterator<Entry<Material>> it = entriesSortedByCount.iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry<Material> type = it.next();
|
||||
Integer limit = addon.getBlockConfig().getBlockLimits().get(type.getElement());
|
||||
String explain = ")";
|
||||
if (limit == null) {
|
||||
Material generic = type.getElement();
|
||||
limit = addon.getBlockConfig().getBlockLimits().get(generic);
|
||||
explain = " - All types)";
|
||||
}
|
||||
reportLines.add(type.getElement().toString() + ": " + String.format("%,d",type.getCount()) + " blocks (max " + limit + explain);
|
||||
}
|
||||
reportLines.add(LINE_BREAK);
|
||||
reportLines.add("Blocks on island that are not in config.yml");
|
||||
reportLines.add("Total number = " + String.format("%,d",result.ncCount.size()));
|
||||
entriesSortedByCount = result.ncCount.entrySet();
|
||||
it = entriesSortedByCount.iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry<Material> type = it.next();
|
||||
reportLines.add(type.getElement().toString() + ": " + String.format("%,d",type.getCount()) + " blocks");
|
||||
}
|
||||
reportLines.add(LINE_BREAK);
|
||||
|
||||
return reportLines;
|
||||
}
|
||||
|
||||
private Collection<String> sortedReport(int total, Multiset<Material> MaterialCount) {
|
||||
Collection<String> r = new ArrayList<>();
|
||||
Iterable<Multiset.Entry<Material>> entriesSortedByCount = Multisets.copyHighestCountFirst(MaterialCount).entrySet();
|
||||
for (Entry<Material> en : entriesSortedByCount) {
|
||||
Material type = en.getElement();
|
||||
|
||||
int value = getValue(type);
|
||||
|
||||
r.add(type.toString() + ":"
|
||||
+ String.format("%,d", en.getCount()) + " blocks x " + value + " = " + (value * en.getCount()));
|
||||
total += (value * en.getCount());
|
||||
|
||||
}
|
||||
r.add("Subtotal = " + total);
|
||||
r.add(LINE_BREAK);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the result
|
||||
*/
|
||||
public Results getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Results class
|
||||
*
|
||||
*/
|
||||
public class Results {
|
||||
private List<String> report;
|
||||
private final Multiset<Material> mdCount = HashMultiset.create();
|
||||
private final Multiset<Material> uwCount = HashMultiset.create();
|
||||
private final Multiset<Material> ncCount = HashMultiset.create();
|
||||
private final Multiset<Material> ofCount = HashMultiset.create();
|
||||
// AtomicLong and AtomicInteger must be used because they are changed by multiple concurrent threads
|
||||
private AtomicLong rawBlockCount = new AtomicLong(0);
|
||||
private AtomicLong underWaterBlockCount = new AtomicLong(0);
|
||||
private AtomicLong level = new AtomicLong(0);
|
||||
private AtomicInteger deathHandicap = new AtomicInteger(0);
|
||||
private AtomicLong pointsToNextLevel = new AtomicLong(0);
|
||||
private AtomicLong initialLevel = new AtomicLong(0);
|
||||
|
||||
/**
|
||||
* @return the deathHandicap
|
||||
*/
|
||||
public int getDeathHandicap() {
|
||||
return deathHandicap.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the report
|
||||
*/
|
||||
public List<String> getReport() {
|
||||
return report;
|
||||
}
|
||||
/**
|
||||
* Set level
|
||||
* @param level - level
|
||||
*/
|
||||
public void setLevel(long level) {
|
||||
this.level.set(level);
|
||||
}
|
||||
/**
|
||||
* @return the level
|
||||
*/
|
||||
public long getLevel() {
|
||||
return level.get();
|
||||
}
|
||||
/**
|
||||
* @return the pointsToNextLevel
|
||||
*/
|
||||
public long getPointsToNextLevel() {
|
||||
return pointsToNextLevel.get();
|
||||
}
|
||||
|
||||
public long getInitialLevel() {
|
||||
return initialLevel.get();
|
||||
}
|
||||
|
||||
public void setInitialLevel(long initialLevel) {
|
||||
this.initialLevel.set(initialLevel);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Results [report=" + report + ", mdCount=" + mdCount + ", uwCount=" + uwCount + ", ncCount="
|
||||
+ ncCount + ", ofCount=" + ofCount + ", rawBlockCount=" + rawBlockCount + ", underWaterBlockCount="
|
||||
+ underWaterBlockCount + ", level=" + level + ", deathHandicap=" + deathHandicap
|
||||
+ ", pointsToNextLevel=" + pointsToNextLevel + ", initialLevel=" + initialLevel + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static double eval(final String str) {
|
||||
return new Object() {
|
||||
int pos = -1, ch;
|
||||
|
||||
void nextChar() {
|
||||
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
|
||||
}
|
||||
|
||||
boolean eat(int charToEat) {
|
||||
while (ch == ' ') nextChar();
|
||||
if (ch == charToEat) {
|
||||
nextChar();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
double parse() {
|
||||
nextChar();
|
||||
double x = parseExpression();
|
||||
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
|
||||
return x;
|
||||
}
|
||||
|
||||
// Grammar:
|
||||
// expression = term | expression `+` term | expression `-` term
|
||||
// term = factor | term `*` factor | term `/` factor
|
||||
// factor = `+` factor | `-` factor | `(` expression `)`
|
||||
// | number | functionName factor | factor `^` factor
|
||||
|
||||
double parseExpression() {
|
||||
double x = parseTerm();
|
||||
for (;;) {
|
||||
if (eat('+')) x += parseTerm(); // addition
|
||||
else if (eat('-')) x -= parseTerm(); // subtraction
|
||||
else return x;
|
||||
}
|
||||
}
|
||||
|
||||
double parseTerm() {
|
||||
double x = parseFactor();
|
||||
for (;;) {
|
||||
if (eat('*')) x *= parseFactor(); // multiplication
|
||||
else if (eat('/')) x /= parseFactor(); // division
|
||||
else return x;
|
||||
}
|
||||
}
|
||||
|
||||
double parseFactor() {
|
||||
if (eat('+')) return parseFactor(); // unary plus
|
||||
if (eat('-')) return -parseFactor(); // unary minus
|
||||
|
||||
double x;
|
||||
int startPos = this.pos;
|
||||
if (eat('(')) { // parentheses
|
||||
x = parseExpression();
|
||||
eat(')');
|
||||
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
|
||||
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
|
||||
x = Double.parseDouble(str.substring(startPos, this.pos));
|
||||
} else if (ch >= 'a' && ch <= 'z') { // functions
|
||||
while (ch >= 'a' && ch <= 'z') nextChar();
|
||||
String func = str.substring(startPos, this.pos);
|
||||
x = parseFactor();
|
||||
switch (func) {
|
||||
case "sqrt":
|
||||
x = Math.sqrt(x);
|
||||
break;
|
||||
case "sin":
|
||||
x = Math.sin(Math.toRadians(x));
|
||||
break;
|
||||
case "cos":
|
||||
x = Math.cos(Math.toRadians(x));
|
||||
break;
|
||||
case "tan":
|
||||
x = Math.tan(Math.toRadians(x));
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unknown function: " + func);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected: " + (char)ch);
|
||||
}
|
||||
|
||||
if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation
|
||||
|
||||
return x;
|
||||
}
|
||||
}.parse();
|
||||
}
|
||||
}
|
@ -1,10 +1,535 @@
|
||||
package world.bentobox.level.calculators;
|
||||
|
||||
public interface IslandLevelCalculator {
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.ChunkSnapshot;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Tag;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.World.Environment;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.Container;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.Slab;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import com.bgsoftware.wildstacker.api.WildStackerAPI;
|
||||
import com.bgsoftware.wildstacker.api.objects.StackedBarrel;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.common.collect.Multiset.Entry;
|
||||
import com.google.common.collect.Multisets;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.util.Pair;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
import world.bentobox.level.Level;
|
||||
|
||||
public class IslandLevelCalculator {
|
||||
private static final String LINE_BREAK = "==================================";
|
||||
public static final long MAX_AMOUNT = 10000;
|
||||
public static Boolean stackersEnabled;
|
||||
|
||||
/**
|
||||
* @return the results of the island calculation
|
||||
* Method to evaluate a mathematical equation
|
||||
* @param str - equation to evaluate
|
||||
* @return value of equation
|
||||
*/
|
||||
Results getResult();
|
||||
private static double eval(final String str) {
|
||||
return new Object() {
|
||||
int pos = -1, ch;
|
||||
|
||||
boolean eat(int charToEat) {
|
||||
while (ch == ' ') nextChar();
|
||||
if (ch == charToEat) {
|
||||
nextChar();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void nextChar() {
|
||||
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
|
||||
}
|
||||
|
||||
double parse() {
|
||||
nextChar();
|
||||
double x = parseExpression();
|
||||
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
|
||||
return x;
|
||||
}
|
||||
|
||||
// Grammar:
|
||||
// expression = term | expression `+` term | expression `-` term
|
||||
// term = factor | term `*` factor | term `/` factor
|
||||
// factor = `+` factor | `-` factor | `(` expression `)`
|
||||
// | number | functionName factor | factor `^` factor
|
||||
|
||||
double parseExpression() {
|
||||
double x = parseTerm();
|
||||
for (;;) {
|
||||
if (eat('+')) x += parseTerm(); // addition
|
||||
else if (eat('-')) x -= parseTerm(); // subtraction
|
||||
else return x;
|
||||
}
|
||||
}
|
||||
|
||||
double parseFactor() {
|
||||
if (eat('+')) return parseFactor(); // unary plus
|
||||
if (eat('-')) return -parseFactor(); // unary minus
|
||||
|
||||
double x;
|
||||
int startPos = this.pos;
|
||||
if (eat('(')) { // parentheses
|
||||
x = parseExpression();
|
||||
eat(')');
|
||||
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
|
||||
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
|
||||
x = Double.parseDouble(str.substring(startPos, this.pos));
|
||||
} else if (ch >= 'a' && ch <= 'z') { // functions
|
||||
while (ch >= 'a' && ch <= 'z') nextChar();
|
||||
String func = str.substring(startPos, this.pos);
|
||||
x = parseFactor();
|
||||
switch (func) {
|
||||
case "sqrt":
|
||||
x = Math.sqrt(x);
|
||||
break;
|
||||
case "sin":
|
||||
x = Math.sin(Math.toRadians(x));
|
||||
break;
|
||||
case "cos":
|
||||
x = Math.cos(Math.toRadians(x));
|
||||
break;
|
||||
case "tan":
|
||||
x = Math.tan(Math.toRadians(x));
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unknown function: " + func);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected: " + (char)ch);
|
||||
}
|
||||
|
||||
if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
double parseTerm() {
|
||||
double x = parseFactor();
|
||||
for (;;) {
|
||||
if (eat('*')) x *= parseFactor(); // multiplication
|
||||
else if (eat('/')) x /= parseFactor(); // division
|
||||
else return x;
|
||||
}
|
||||
}
|
||||
}.parse();
|
||||
}
|
||||
private final Level addon;
|
||||
private final Queue<Pair<Integer, Integer>> chunksToCheck;
|
||||
private final Island island;
|
||||
private final HashMap<Material, Integer> limitCount;
|
||||
private final CompletableFuture<Results> r;
|
||||
|
||||
|
||||
private final Results results;
|
||||
|
||||
/**
|
||||
* Constructor to get the level for an island
|
||||
* @param addon - Level addon
|
||||
* @param island - the island to scan
|
||||
* @param r - completeable result that will be completed when the calculation is complete
|
||||
*/
|
||||
public IslandLevelCalculator(Level addon, Island island, CompletableFuture<Results> r) {
|
||||
this.addon = addon;
|
||||
this.island = island;
|
||||
this.r = r;
|
||||
results = new Results();
|
||||
chunksToCheck = getChunksToScan(island);
|
||||
this.limitCount = new HashMap<>(addon.getBlockConfig().getBlockLimits());
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the level based on the raw points
|
||||
* @param blockAndDeathPoints - raw points counted on island
|
||||
* @return level of island
|
||||
*/
|
||||
private long calculateLevel(long blockAndDeathPoints) {
|
||||
String calcString = addon.getSettings().getLevelCalc();
|
||||
String withValues = calcString.replace("blocks", String.valueOf(blockAndDeathPoints)).replace("level_cost", String.valueOf(this.addon.getSettings().getLevelCost()));
|
||||
return (long)eval(withValues) - this.island.getLevelHandicap() - results.initialLevel.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds value to the results based on the material and whether the block is below sea level or not
|
||||
* @param mat - material of the block
|
||||
* @param belowSeaLevel - true if below sea level
|
||||
*/
|
||||
private void checkBlock(Material mat, boolean belowSeaLevel) {
|
||||
int count = limitCount(mat);
|
||||
if (belowSeaLevel) {
|
||||
results.underWaterBlockCount.addAndGet(count);
|
||||
results.uwCount.add(mat);
|
||||
} else {
|
||||
results.rawBlockCount.addAndGet(count);
|
||||
results.mdCount.add(mat);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a set of all the chunks in island
|
||||
* @param island - island
|
||||
* @return - set of pairs of x,z coordinates to check
|
||||
*/
|
||||
private Queue<Pair<Integer, Integer>> getChunksToScan(Island island) {
|
||||
Queue<Pair<Integer, Integer>> chunkQueue = new ConcurrentLinkedQueue<>();
|
||||
for (int x = island.getMinProtectedX(); x < (island.getMinProtectedX() + island.getProtectionRange() * 2 + 16); x += 16) {
|
||||
for (int z = island.getMinProtectedZ(); z < (island.getMinProtectedZ() + island.getProtectionRange() * 2 + 16); z += 16) {
|
||||
chunkQueue.add(new Pair<>(x >> 4, z >> 4));
|
||||
}
|
||||
}
|
||||
return chunkQueue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the island
|
||||
*/
|
||||
public Island getIsland() {
|
||||
return island;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the completable result for this calculation
|
||||
* @return the r
|
||||
*/
|
||||
public CompletableFuture<Results> getR() {
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full analysis report
|
||||
* @return a list of lines
|
||||
*/
|
||||
private List<String> getReport() {
|
||||
List<String> reportLines = new ArrayList<>();
|
||||
// provide counts
|
||||
reportLines.add("Level Log for island in " + addon.getPlugin().getIWM().getFriendlyName(island.getWorld()) + " at " + Util.xyz(island.getCenter().toVector()));
|
||||
reportLines.add("Island owner UUID = " + island.getOwner());
|
||||
reportLines.add("Total block value count = " + String.format("%,d",results.rawBlockCount.get()));
|
||||
reportLines.add("Formula to calculate island level: " + addon.getSettings().getLevelCalc());
|
||||
reportLines.add("Level cost = " + addon.getSettings().getLevelCost());
|
||||
reportLines.add("Deaths handicap = " + results.deathHandicap.get());
|
||||
reportLines.add("Initial island level = " + (0L - addon.getManager().getInitialLevel(island)));
|
||||
reportLines.add("Level calculated = " + addon.getManager().getIslandLevel(island.getWorld(), island.getOwner()));
|
||||
reportLines.add(LINE_BREAK);
|
||||
int total = 0;
|
||||
if (!results.uwCount.isEmpty()) {
|
||||
reportLines.add("Underwater block count (Multiplier = x" + addon.getSettings().getUnderWaterMultiplier() + ") value");
|
||||
reportLines.add("Total number of underwater blocks = " + String.format("%,d",results.uwCount.size()));
|
||||
reportLines.addAll(sortedReport(total, results.uwCount));
|
||||
}
|
||||
reportLines.add("Regular block count");
|
||||
reportLines.add("Total number of blocks = " + String.format("%,d",results.mdCount.size()));
|
||||
reportLines.addAll(sortedReport(total, results.mdCount));
|
||||
|
||||
reportLines.add("Blocks not counted because they exceeded limits: " + String.format("%,d",results.ofCount.size()));
|
||||
Iterable<Multiset.Entry<Material>> entriesSortedByCount = results.ofCount.entrySet();
|
||||
Iterator<Entry<Material>> it = entriesSortedByCount.iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry<Material> type = it.next();
|
||||
Integer limit = addon.getBlockConfig().getBlockLimits().get(type.getElement());
|
||||
String explain = ")";
|
||||
if (limit == null) {
|
||||
Material generic = type.getElement();
|
||||
limit = addon.getBlockConfig().getBlockLimits().get(generic);
|
||||
explain = " - All types)";
|
||||
}
|
||||
reportLines.add(type.getElement().toString() + ": " + String.format("%,d",type.getCount()) + " blocks (max " + limit + explain);
|
||||
}
|
||||
reportLines.add(LINE_BREAK);
|
||||
reportLines.add("Blocks on island that are not in config.yml");
|
||||
reportLines.add("Total number = " + String.format("%,d",results.ncCount.size()));
|
||||
entriesSortedByCount = results.ncCount.entrySet();
|
||||
it = entriesSortedByCount.iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry<Material> type = it.next();
|
||||
reportLines.add(type.getElement().toString() + ": " + String.format("%,d",type.getCount()) + " blocks");
|
||||
}
|
||||
reportLines.add(LINE_BREAK);
|
||||
|
||||
return reportLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the results
|
||||
*/
|
||||
public Results getResults() {
|
||||
return results;
|
||||
}
|
||||
/**
|
||||
* Get value of a material
|
||||
* World blocks trump regular block values
|
||||
* @param md - Material to check
|
||||
* @return value of a material
|
||||
*/
|
||||
private int getValue(Material md) {
|
||||
Integer value = addon.getBlockConfig().getValue(island.getWorld(), md);
|
||||
if (value == null) {
|
||||
// Not in config
|
||||
results.ncCount.add(md);
|
||||
return 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a chunk async
|
||||
* @param world - the world where the chunk is
|
||||
* @param env - the environment
|
||||
* @param x - chunk x coordinate
|
||||
* @param z - chunk z coordinate
|
||||
* @return a future chunk or future null if there is no chunk to load, e.g., there is no island nether
|
||||
*/
|
||||
private CompletableFuture<Chunk> getWorldChunk(@NonNull World world, Environment env, int x, int z) {
|
||||
switch (env) {
|
||||
case NETHER:
|
||||
if (addon.getSettings().isNether()) {
|
||||
World nether = addon.getPlugin().getIWM().getNetherWorld(island.getWorld());
|
||||
if (nether != null) {
|
||||
return Util.getChunkAtAsync(nether, x, z, false);
|
||||
}
|
||||
}
|
||||
// There is no chunk to scan, so return a null chunk
|
||||
return CompletableFuture.completedFuture(null);
|
||||
case THE_END:
|
||||
if (addon.getSettings().isEnd()) {
|
||||
World end = addon.getPlugin().getIWM().getEndWorld(island.getWorld());
|
||||
if (end != null) {
|
||||
return Util.getChunkAtAsync(end, x, z, false);
|
||||
}
|
||||
}
|
||||
// There is no chunk to scan, so return a null chunk
|
||||
return CompletableFuture.completedFuture(null);
|
||||
default:
|
||||
return Util.getChunkAtAsync(world, x, z, false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a block has been limited or not and whether a block has any value or not
|
||||
* @param md Material
|
||||
* @return value of the block if can be counted
|
||||
*/
|
||||
private int limitCount(Material md) {
|
||||
if (limitCount.containsKey(md)) {
|
||||
int count = limitCount.get(md);
|
||||
if (count > 0) {
|
||||
limitCount.put(md, --count);
|
||||
return getValue(md);
|
||||
} else {
|
||||
results.ofCount.add(md);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return getValue(md);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Count the blocks on the island
|
||||
* @param result - the CompletableFuture that should be completed when this scan is done
|
||||
* @param chunk - the chunk to scan
|
||||
*/
|
||||
private void scanAsync(CompletableFuture<Boolean> result, Chunk chunk) {
|
||||
// Get a thread-safe snapshot of the chunk
|
||||
ChunkSnapshot chunkSnapshot = chunk.getChunkSnapshot();
|
||||
for (int x = 0; x< 16; x++) {
|
||||
// Check if the block coordinate is inside the protection zone and if not, don't count it
|
||||
if (chunkSnapshot.getX() * 16 + x < island.getMinProtectedX() || chunkSnapshot.getX() * 16 + x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
|
||||
continue;
|
||||
}
|
||||
for (int z = 0; z < 16; z++) {
|
||||
// Check if the block coordinate is inside the protection zone and if not, don't count it
|
||||
if (chunkSnapshot.getZ() * 16 + z < island.getMinProtectedZ() || chunkSnapshot.getZ() * 16 + z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
|
||||
continue;
|
||||
}
|
||||
// Only count to the highest block in the world for some optimization
|
||||
for (int y = 0; y < chunkSnapshot.getHighestBlockYAt(x, z); y++) {
|
||||
BlockData blockData = chunkSnapshot.getBlockData(x, y, z);
|
||||
int seaHeight = addon.getPlugin().getIWM().getSeaHeight(island.getWorld());
|
||||
boolean belowSeaLevel = seaHeight > 0 && y <= seaHeight;
|
||||
// Slabs can be doubled, so check them twice
|
||||
if (Tag.SLABS.isTagged(blockData.getMaterial())) {
|
||||
Slab slab = (Slab)blockData;
|
||||
if (slab.getType().equals(Slab.Type.DOUBLE)) {
|
||||
checkBlock(blockData.getMaterial(), belowSeaLevel);
|
||||
}
|
||||
}
|
||||
// Hook for Wild Stackers (Blocks Only) - this has to use the real chunk
|
||||
if (stackersEnabled && blockData.getMaterial() == Material.CAULDRON) {
|
||||
Block cauldronBlock = chunk.getBlock(x, y, z);
|
||||
if (WildStackerAPI.getWildStacker().getSystemManager().isStackedBarrel(cauldronBlock)) {
|
||||
StackedBarrel barrel = WildStackerAPI.getStackedBarrel(cauldronBlock);
|
||||
int barrelAmt = WildStackerAPI.getBarrelAmount(cauldronBlock);
|
||||
for (int _x = 0; _x < barrelAmt; _x++) {
|
||||
checkBlock(barrel.getType(), belowSeaLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add the value of the block's material
|
||||
checkBlock(blockData.getMaterial(), belowSeaLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Chunk finished
|
||||
if (chunk.getWorld().getEnvironment().equals(Environment.NORMAL) && chunksToCheck.isEmpty()) {
|
||||
// This was the last chunk
|
||||
tidyUp();
|
||||
}
|
||||
// Complete the future - this must go back onto the primary thread to exit async otherwise subsequent actions will be async
|
||||
Bukkit.getScheduler().runTask(addon.getPlugin(),() -> result.complete(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan all containers in a chunk and count their blocks
|
||||
* @param chunk - the chunk to scan
|
||||
*/
|
||||
private void scanChests(Chunk chunk) {
|
||||
// Count blocks in chests
|
||||
for (BlockState bs : chunk.getTileEntities()) {
|
||||
if (bs instanceof Container) {
|
||||
Inventory inv = ((Container)bs).getSnapshotInventory();
|
||||
for (ItemStack i : inv) {
|
||||
if (i != null && i.getType().isBlock()) {
|
||||
for (int c = 0; c < i.getAmount(); c++) {
|
||||
checkBlock(i.getType(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the chunk chests and count the blocks
|
||||
* @param chunk - the chunk to scan
|
||||
* @return future that completes when the scan is done and supplies a boolean that will be true if the scan was successful, false if not
|
||||
*/
|
||||
private CompletableFuture<Boolean> scanChunk(@NonNull Chunk chunk) {
|
||||
if (chunk == null) {
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
// Scan chests
|
||||
if (addon.getSettings().isIncludeChests()) {
|
||||
scanChests(chunk);
|
||||
}
|
||||
|
||||
// Count blocks in chunk
|
||||
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||
Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(), () -> scanAsync(result, chunk));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the next chunk on the island
|
||||
* @return completable boolean future that will be true if more chunks are left to be scanned, and false if not
|
||||
*/
|
||||
public CompletableFuture<Boolean> scanNextChunk() {
|
||||
if (chunksToCheck.isEmpty()) {
|
||||
// This should not be needed, but just in case
|
||||
return CompletableFuture.completedFuture(false);
|
||||
}
|
||||
// Retrieve and remove from the queue
|
||||
Pair<Integer, Integer> p = chunksToCheck.poll();
|
||||
// Set up the result
|
||||
CompletableFuture<Boolean> result = new CompletableFuture<>();
|
||||
// Get chunks and scan
|
||||
getWorldChunk(island.getWorld(), Environment.THE_END, p.x, p.z).thenAccept(endChunk ->
|
||||
scanChunk(endChunk).thenAccept(b ->
|
||||
getWorldChunk(island.getWorld(), Environment.NETHER, p.x, p.z).thenAccept(netherChunk ->
|
||||
scanChunk(netherChunk).thenAccept(b2 ->
|
||||
getWorldChunk(island.getWorld(), Environment.NORMAL, p.x, p.z).thenAccept(normalChunk ->
|
||||
scanChunk(normalChunk).thenAccept(b3 ->
|
||||
// Complete the result now that all chunks have been scanned
|
||||
result.complete(!chunksToCheck.isEmpty()))))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Collection<String> sortedReport(int total, Multiset<Material> MaterialCount) {
|
||||
Collection<String> r = new ArrayList<>();
|
||||
Iterable<Multiset.Entry<Material>> entriesSortedByCount = Multisets.copyHighestCountFirst(MaterialCount).entrySet();
|
||||
for (Entry<Material> en : entriesSortedByCount) {
|
||||
Material type = en.getElement();
|
||||
|
||||
int value = getValue(type);
|
||||
|
||||
r.add(type.toString() + ":"
|
||||
+ String.format("%,d", en.getCount()) + " blocks x " + value + " = " + (value * en.getCount()));
|
||||
total += (value * en.getCount());
|
||||
|
||||
}
|
||||
r.add("Subtotal = " + total);
|
||||
r.add(LINE_BREAK);
|
||||
return r;
|
||||
}
|
||||
|
||||
private void tidyUp() {
|
||||
// Finalize calculations
|
||||
results.rawBlockCount.addAndGet((long)(results.underWaterBlockCount.get() * addon.getSettings().getUnderWaterMultiplier()));
|
||||
|
||||
// Set the death penalty
|
||||
if (this.addon.getSettings().isSumTeamDeaths())
|
||||
{
|
||||
for (UUID uuid : this.island.getMemberSet())
|
||||
{
|
||||
this.results.deathHandicap.addAndGet(this.addon.getPlayers().getDeaths(island.getWorld(), uuid));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// At this point, it may be that the island has become unowned.
|
||||
this.results.deathHandicap.set(this.island.getOwner() == null ? 0 :
|
||||
this.addon.getPlayers().getDeaths(island.getWorld(), this.island.getOwner()));
|
||||
}
|
||||
|
||||
long blockAndDeathPoints = this.results.rawBlockCount.get();
|
||||
|
||||
if (this.addon.getSettings().getDeathPenalty() > 0)
|
||||
{
|
||||
// Proper death penalty calculation.
|
||||
blockAndDeathPoints -= this.results.deathHandicap.get() * this.addon.getSettings().getDeathPenalty();
|
||||
}
|
||||
this.results.level.set(calculateLevel(blockAndDeathPoints));
|
||||
|
||||
// Calculate how many points are required to get to the next level
|
||||
long nextLevel = this.results.level.get();
|
||||
long blocks = blockAndDeathPoints;
|
||||
while (nextLevel < this.results.level.get() + 1 && blocks - blockAndDeathPoints < MAX_AMOUNT) {
|
||||
nextLevel = calculateLevel(++blocks);
|
||||
}
|
||||
this.results.pointsToNextLevel.set(blocks - blockAndDeathPoints);
|
||||
|
||||
// Report
|
||||
results.report = getReport();
|
||||
// All done.
|
||||
}
|
||||
}
|
||||
|
100
src/main/java/world/bentobox/level/calculators/Pipeliner.java
Normal file
100
src/main/java/world/bentobox/level/calculators/Pipeliner.java
Normal file
@ -0,0 +1,100 @@
|
||||
package world.bentobox.level.calculators;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.level.Level;
|
||||
|
||||
/**
|
||||
* A pipeliner that will process one island at a time
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
public class Pipeliner {
|
||||
|
||||
private final Queue<IslandLevelCalculator> processQueue;
|
||||
private final BukkitTask task;
|
||||
private boolean inProcess;
|
||||
private final Level addon;
|
||||
|
||||
/**
|
||||
* Construct the pipeliner
|
||||
*/
|
||||
public Pipeliner(Level addon) {
|
||||
this.addon = addon;
|
||||
processQueue = new ConcurrentLinkedQueue<>();
|
||||
// Loop continuously - check every tick if there is an island to scan
|
||||
task = Bukkit.getScheduler().runTaskTimer(BentoBox.getInstance(), () -> {
|
||||
if (!BentoBox.getInstance().isEnabled()) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
// One island at a time
|
||||
if (inProcess || processQueue.isEmpty()) return;
|
||||
IslandLevelCalculator iD = processQueue.poll();
|
||||
// Ignore deleted or unonwed islands
|
||||
if (iD.getIsland().isDeleted() || iD.getIsland().isUnowned()) return;
|
||||
// Start the process
|
||||
inProcess = true;
|
||||
// Start the scanning of a island with the first chunk
|
||||
scanChunk(iD);
|
||||
|
||||
}, 1L, 1L);
|
||||
}
|
||||
|
||||
private void cancel() {
|
||||
task.cancel();
|
||||
}
|
||||
|
||||
public int getIslandsInQueue() {
|
||||
return processQueue.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans one chunk of an island and adds the results to a results object
|
||||
* @param iD
|
||||
*/
|
||||
private void scanChunk(IslandLevelCalculator iD) {
|
||||
if (iD.getIsland().isDeleted() || iD.getIsland().isUnowned()) {
|
||||
// Island is deleted, so finish early with nothing
|
||||
inProcess = false;
|
||||
iD.getR().complete(null);
|
||||
return;
|
||||
}
|
||||
// Scan the next chunk
|
||||
iD.scanNextChunk().thenAccept(r -> {
|
||||
if (!Bukkit.isPrimaryThread()) {
|
||||
addon.getPlugin().logError("scanChunk not on Primary Thread!");
|
||||
}
|
||||
if (r) {
|
||||
// scanNextChunk returns true if there are more chunks to scan
|
||||
scanChunk(iD);
|
||||
} else {
|
||||
// Done
|
||||
inProcess = false;
|
||||
iD.getR().complete(iD.getResults());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds an island to the scanning queue
|
||||
* @param island - the island to scan
|
||||
*
|
||||
*/
|
||||
public CompletableFuture<Results> addIsland(Island island) {
|
||||
CompletableFuture<Results> r = new CompletableFuture<>();
|
||||
processQueue.add(new IslandLevelCalculator(addon, island, r));
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
package world.bentobox.level.calculators;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.api.events.addon.AddonEvent;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.level.Level;
|
||||
import world.bentobox.level.calculators.CalcIslandLevel.Results;
|
||||
import world.bentobox.level.event.IslandLevelCalculatedEvent;
|
||||
import world.bentobox.level.event.IslandPreLevelEvent;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the player's island level. For admin or players
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
public class PlayerLevel {
|
||||
|
||||
private final Level addon;
|
||||
|
||||
private final Island island;
|
||||
private final World world;
|
||||
private final User asker;
|
||||
private final UUID targetPlayer;
|
||||
|
||||
private final long oldLevel;
|
||||
|
||||
private CalcIslandLevel calc;
|
||||
|
||||
|
||||
public PlayerLevel(final Level addon, final Island island, final UUID targetPlayer, @Nullable final User asker) {
|
||||
this.addon = addon;
|
||||
this.island = island;
|
||||
this.world = island.getCenter().getWorld();
|
||||
this.asker = asker;
|
||||
this.targetPlayer = targetPlayer;
|
||||
this.oldLevel = addon.getIslandLevel(world, targetPlayer);
|
||||
|
||||
// Fire pre-level calc event
|
||||
IslandPreLevelEvent e = new IslandPreLevelEvent(targetPlayer, island);
|
||||
addon.getServer().getPluginManager().callEvent(e);
|
||||
if (!e.isCancelled()) {
|
||||
// Calculate if not cancelled
|
||||
calc = new CalcIslandLevel(addon, island, this::fireIslandLevelCalcEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void fireIslandLevelCalcEvent() {
|
||||
// Fire post calculation event
|
||||
IslandLevelCalculatedEvent ilce = new IslandLevelCalculatedEvent(targetPlayer, island, calc.getResult());
|
||||
addon.getServer().getPluginManager().callEvent(ilce);
|
||||
// This exposes these values to plugins via the event
|
||||
Map<String, Object> keyValues = new HashMap<>();
|
||||
keyValues.put("eventName", "IslandLevelCalculatedEvent");
|
||||
keyValues.put("targetPlayer", targetPlayer);
|
||||
keyValues.put("islandUUID", island.getUniqueId());
|
||||
keyValues.put("level", calc.getResult().getLevel());
|
||||
keyValues.put("pointsToNextLevel", calc.getResult().getPointsToNextLevel());
|
||||
keyValues.put("deathHandicap", calc.getResult().getDeathHandicap());
|
||||
keyValues.put("initialLevel", calc.getResult().getInitialLevel());
|
||||
new AddonEvent().builder().addon(addon).keyValues(keyValues).build();
|
||||
Results results = ilce.getResults();
|
||||
// Save the results
|
||||
island.getMemberSet().forEach(m -> addon.setIslandLevel(world, m, results.getLevel()));
|
||||
// Display result if event is not cancelled
|
||||
if (!ilce.isCancelled() && asker != null) {
|
||||
informPlayers(results);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void informPlayers(Results results) {
|
||||
// Tell the asker
|
||||
asker.sendMessage("island.level.island-level-is", "[level]", String.valueOf(addon.getIslandLevel(world, targetPlayer)));
|
||||
// Console
|
||||
if (!asker.isPlayer()) {
|
||||
results.getReport().forEach(asker::sendRawMessage);
|
||||
return;
|
||||
}
|
||||
// Player
|
||||
if (addon.getSettings().getDeathPenalty() != 0) {
|
||||
asker.sendMessage("island.level.deaths", "[number]", String.valueOf(results.getDeathHandicap()));
|
||||
}
|
||||
// Send player how many points are required to reach next island level
|
||||
if (results.getPointsToNextLevel() >= 0 && results.getPointsToNextLevel() < CalcIslandLevel.MAX_AMOUNT) {
|
||||
asker.sendMessage("island.level.required-points-to-next-level", "[points]", String.valueOf(results.getPointsToNextLevel()));
|
||||
}
|
||||
// Tell other team members
|
||||
if (addon.getIslandLevel(world, targetPlayer) != oldLevel) {
|
||||
island.getMemberSet().stream()
|
||||
.filter(u -> !u.equals(asker.getUniqueId()))
|
||||
.forEach(m -> User.getInstance(m).sendMessage("island.level.island-level-is", "[level]", String.valueOf(addon.getIslandLevel(world, targetPlayer))));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,78 +1,33 @@
|
||||
package world.bentobox.level.calculators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.bukkit.Material;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Multiset;
|
||||
|
||||
/**
|
||||
* Results class
|
||||
*
|
||||
*/
|
||||
public class Results {
|
||||
private int deathHandicap = 0;
|
||||
private long initialLevel = 0;
|
||||
private long level = 0;
|
||||
private final Multiset<Material> mdCount = HashMultiset.create();
|
||||
private final Multiset<Material> ncCount = HashMultiset.create();
|
||||
private final Multiset<Material> ofCount = HashMultiset.create();
|
||||
private long pointsToNextLevel = 0;
|
||||
private long rawBlockCount = 0;
|
||||
private List<String> report = new ArrayList<>();
|
||||
private long underWaterBlockCount = 0;
|
||||
private final Multiset<Material> uwCount = HashMultiset.create();
|
||||
List<String> report;
|
||||
final Multiset<Material> mdCount = HashMultiset.create();
|
||||
final Multiset<Material> uwCount = HashMultiset.create();
|
||||
final Multiset<Material> ncCount = HashMultiset.create();
|
||||
final Multiset<Material> ofCount = HashMultiset.create();
|
||||
// AtomicLong and AtomicInteger must be used because they are changed by multiple concurrent threads
|
||||
AtomicLong rawBlockCount = new AtomicLong(0);
|
||||
AtomicLong underWaterBlockCount = new AtomicLong(0);
|
||||
AtomicLong level = new AtomicLong(0);
|
||||
AtomicInteger deathHandicap = new AtomicInteger(0);
|
||||
AtomicLong pointsToNextLevel = new AtomicLong(0);
|
||||
AtomicLong initialLevel = new AtomicLong(0);
|
||||
|
||||
/**
|
||||
* @return the deathHandicap
|
||||
*/
|
||||
public int getDeathHandicap() {
|
||||
return deathHandicap;
|
||||
}
|
||||
|
||||
public long getInitialLevel() {
|
||||
return initialLevel;
|
||||
}
|
||||
/**
|
||||
* @return the level
|
||||
*/
|
||||
public long getLevel() {
|
||||
return level;
|
||||
}
|
||||
/**
|
||||
* @return the mdCount
|
||||
*/
|
||||
public Multiset<Material> getMdCount() {
|
||||
return mdCount;
|
||||
}
|
||||
/**
|
||||
* @return the ncCount
|
||||
*/
|
||||
public Multiset<Material> getNcCount() {
|
||||
return ncCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the ofCount
|
||||
*/
|
||||
public Multiset<Material> getOfCount() {
|
||||
return ofCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the pointsToNextLevel
|
||||
*/
|
||||
public long getPointsToNextLevel() {
|
||||
return pointsToNextLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rawBlockCount
|
||||
*/
|
||||
public long getRawBlockCount() {
|
||||
return rawBlockCount;
|
||||
return deathHandicap.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,82 +36,32 @@ public class Results {
|
||||
public List<String> getReport() {
|
||||
return report;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the underWaterBlockCount
|
||||
*/
|
||||
public long getUnderWaterBlockCount() {
|
||||
return underWaterBlockCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the uwCount
|
||||
*/
|
||||
public Multiset<Material> getUwCount() {
|
||||
return uwCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param deathHandicap the deathHandicap to set
|
||||
*/
|
||||
public void setDeathHandicap(int deathHandicap) {
|
||||
this.deathHandicap = deathHandicap;
|
||||
}
|
||||
|
||||
public void setInitialLevel(long initialLevel) {
|
||||
this.initialLevel = initialLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set level
|
||||
* @param level - level
|
||||
*/
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param level the level to set
|
||||
*/
|
||||
public void setLevel(long level) {
|
||||
this.level = level;
|
||||
this.level.set(level);
|
||||
}
|
||||
/**
|
||||
* @return the level
|
||||
*/
|
||||
public long getLevel() {
|
||||
return level.get();
|
||||
}
|
||||
/**
|
||||
* @return the pointsToNextLevel
|
||||
*/
|
||||
public long getPointsToNextLevel() {
|
||||
return pointsToNextLevel.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pointsToNextLevel the pointsToNextLevel to set
|
||||
*/
|
||||
public void setPointsToNextLevel(long pointsToNextLevel) {
|
||||
this.pointsToNextLevel = pointsToNextLevel;
|
||||
public long getInitialLevel() {
|
||||
return initialLevel.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rawBlockCount the rawBlockCount to set
|
||||
*/
|
||||
public void setRawBlockCount(long rawBlockCount) {
|
||||
this.rawBlockCount = rawBlockCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param report the report to set
|
||||
*/
|
||||
public void setReport(List<String> report) {
|
||||
this.report = report;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param underWaterBlockCount the underWaterBlockCount to set
|
||||
*/
|
||||
public void setUnderWaterBlockCount(long underWaterBlockCount) {
|
||||
this.underWaterBlockCount = underWaterBlockCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to death handicap
|
||||
* @param deaths - number to add
|
||||
*/
|
||||
public void addToDeathHandicap(int deaths) {
|
||||
this.deathHandicap += deaths;
|
||||
|
||||
public void setInitialLevel(long initialLevel) {
|
||||
this.initialLevel.set(initialLevel);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@ -164,9 +69,9 @@ public class Results {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Results [report=" + report + ", mdCount=" + mdCount + ", uwCount=" + getUwCount() + ", ncCount="
|
||||
return "Results [report=" + report + ", mdCount=" + mdCount + ", uwCount=" + uwCount + ", ncCount="
|
||||
+ ncCount + ", ofCount=" + ofCount + ", rawBlockCount=" + rawBlockCount + ", underWaterBlockCount="
|
||||
+ getUnderWaterBlockCount() + ", level=" + level + ", deathHandicap=" + deathHandicap
|
||||
+ underWaterBlockCount + ", level=" + level + ", deathHandicap=" + deathHandicap
|
||||
+ ", pointsToNextLevel=" + pointsToNextLevel + ", initialLevel=" + initialLevel + "]";
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,18 @@
|
||||
package world.bentobox.level.commands.admin;
|
||||
package world.bentobox.level.commands;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
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.util.Util;
|
||||
import world.bentobox.level.Level;
|
||||
|
||||
public class AdminLevelCommand extends CompositeCommand {
|
||||
|
||||
private final Level addon;
|
||||
public class AdminLevelCommand extends IslandLevelCommand {
|
||||
|
||||
public AdminLevelCommand(Level addon, CompositeCommand parent) {
|
||||
super(parent, "level");
|
||||
this.addon = addon;
|
||||
super(addon, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -28,25 +23,6 @@ public class AdminLevelCommand extends CompositeCommand {
|
||||
this.setDescription("admin.level.description");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
if (args.size() == 1) {
|
||||
// Asking for another player's level?
|
||||
// Convert name to a UUID
|
||||
final UUID playerUUID = getPlugin().getPlayers().getUUID(args.get(0));
|
||||
if (playerUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return true;
|
||||
} else {
|
||||
addon.calculateIslandLevel(getWorld(), user, playerUUID);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
showHelp(this, user);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
|
@ -0,0 +1,30 @@
|
||||
package world.bentobox.level.commands;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.level.Level;
|
||||
|
||||
public class AdminLevelStatusCommand extends CompositeCommand {
|
||||
|
||||
private final Level addon;
|
||||
|
||||
public AdminLevelStatusCommand(Level addon, CompositeCommand parent) {
|
||||
super(parent, "levelstatus");
|
||||
this.addon = addon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
this.setPermission("admin.levelstatus");
|
||||
this.setOnlyPlayer(false);
|
||||
this.setDescription("admin.levelstatus.description");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
user.sendRawMessage("Islands in queue: " + addon.getPipeliner().getIslandsInQueue());
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package world.bentobox.level.commands.admin;
|
||||
package world.bentobox.level.commands;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -13,10 +13,10 @@ public class AdminTopCommand extends CompositeCommand {
|
||||
|
||||
private final Level levelPlugin;
|
||||
|
||||
public AdminTopCommand(Level levelPlugin, CompositeCommand parent) {
|
||||
public AdminTopCommand(Level addon, CompositeCommand parent) {
|
||||
super(parent, "top", "topten");
|
||||
this.levelPlugin = levelPlugin;
|
||||
new AdminTopRemoveCommand(levelPlugin, this);
|
||||
this.levelPlugin = addon;
|
||||
new AdminTopRemoveCommand(addon, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -28,8 +28,9 @@ public class AdminTopCommand extends CompositeCommand {
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
user.sendMessage("island.top.gui-title");
|
||||
int rank = 0;
|
||||
for (Map.Entry<UUID, Long> topTen : levelPlugin.getTopTen().getTopTenList(getWorld()).getTopTen().entrySet()) {
|
||||
for (Map.Entry<UUID, Long> topTen : levelPlugin.getManager().getTopTen(getWorld(), 10).entrySet()) {
|
||||
Island island = getPlugin().getIslands().getIsland(getWorld(), topTen.getKey());
|
||||
if (island != null) {
|
||||
rank++;
|
@ -1,6 +1,8 @@
|
||||
package world.bentobox.level.commands.admin;
|
||||
package world.bentobox.level.commands;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
@ -44,12 +46,20 @@ public class AdminTopRemoveCommand extends CompositeCommand {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
addon.getTopTen().getTopTenList(getWorld()).remove(target.getUniqueId());
|
||||
addon.getManager().removeEntry(getWorld(), target.getUniqueId());
|
||||
user.sendMessage("general.success");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
|
||||
return Optional.of(addon.getManager().getTopTen(getWorld(), 10).keySet().stream().map(addon.getPlayers()::getName)
|
||||
.filter(n -> !n.isEmpty()).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
package world.bentobox.level.commands;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
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.database.objects.Island;
|
||||
import world.bentobox.level.Level;
|
||||
|
||||
public class IslandLevelCommand extends CompositeCommand {
|
||||
|
||||
private final Level addon;
|
||||
|
||||
public IslandLevelCommand(Level addon, CompositeCommand parent) {
|
||||
super(parent, "level");
|
||||
this.addon = addon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
this.setPermission("island.level");
|
||||
this.setParametersHelp("island.level.parameters");
|
||||
this.setDescription("island.level.description");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
if (!args.isEmpty()) {
|
||||
// Asking for another player's level?
|
||||
// Convert name to a UUID
|
||||
final UUID playerUUID = getPlugin().getPlayers().getUUID(args.get(0));
|
||||
if (playerUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return true;
|
||||
}
|
||||
// Ops, console and admin perms can request and calculate other player levels
|
||||
if (!user.isPlayer() || user.isOp() || user.hasPermission(this.getPermissionPrefix() + "admin.level")) {
|
||||
return scanIsland(user, playerUUID);
|
||||
}
|
||||
// Request for another player's island level
|
||||
if (!user.getUniqueId().equals(playerUUID) ) {
|
||||
user.sendMessage("island.level.island-level-is", "[level]", addon.getManager().getIslandLevelString(getWorld(), playerUUID));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Self request
|
||||
// Check player cooldown
|
||||
int coolDown = this.addon.getSettings().getLevelWait();
|
||||
|
||||
if (coolDown > 0) {
|
||||
// Check cool down
|
||||
if (checkCooldown(user)) return false;
|
||||
// Set cool down
|
||||
setCooldown(user.getUniqueId(), coolDown);
|
||||
}
|
||||
|
||||
// Self level request
|
||||
return scanIsland(user, user.getUniqueId());
|
||||
|
||||
}
|
||||
|
||||
|
||||
private boolean scanIsland(User user, UUID playerUUID) {
|
||||
Island island = getIslands().getIsland(getWorld(), playerUUID);
|
||||
if (island != null) {
|
||||
user.sendMessage("island.level.calculating");
|
||||
int inQueue = addon.getPipeliner().getIslandsInQueue();
|
||||
if (inQueue > 1) {
|
||||
user.sendMessage("island.level.in-queue", TextVariables.NUMBER, String.valueOf(inQueue + 1));
|
||||
}
|
||||
// Get the old level
|
||||
long oldLevel = addon.getManager().getIslandLevel(getWorld(), playerUUID);
|
||||
addon.getManager().calculateLevel(playerUUID, island).thenAccept(results -> {
|
||||
if (results == null) return; // island was deleted or become unowned
|
||||
if (user.isPlayer()) {
|
||||
user.sendMessage("island.level.island-level-is", "[level]", addon.getManager().getIslandLevelString(getWorld(), playerUUID));
|
||||
// Player
|
||||
if (addon.getSettings().getDeathPenalty() != 0) {
|
||||
user.sendMessage("island.level.deaths", "[number]", String.valueOf(results.getDeathHandicap()));
|
||||
}
|
||||
// Send player how many points are required to reach next island level
|
||||
if (results.getPointsToNextLevel() >= 0 && results.getPointsToNextLevel() < 10000) {
|
||||
user.sendMessage("island.level.required-points-to-next-level", "[points]", String.valueOf(results.getPointsToNextLevel()));
|
||||
}
|
||||
// Tell other team members
|
||||
if (results.getLevel() != oldLevel) {
|
||||
island.getMemberSet().stream()
|
||||
.filter(u -> !u.equals(user.getUniqueId()))
|
||||
.forEach(m -> User.getInstance(m).sendMessage("island.level.island-level-is", "[level]", addon.getManager().getIslandLevelString(getWorld(), playerUUID)));
|
||||
}
|
||||
} else {
|
||||
results.getReport().forEach(BentoBox.getInstance()::log);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
user.sendMessage("general.errors.player-has-no-island");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package world.bentobox.level.commands.island;
|
||||
package world.bentobox.level.commands;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -8,11 +8,11 @@ import world.bentobox.level.Level;
|
||||
|
||||
public class IslandTopCommand extends CompositeCommand {
|
||||
|
||||
private final Level plugin;
|
||||
private final Level addon;
|
||||
|
||||
public IslandTopCommand(Level plugin, CompositeCommand parent) {
|
||||
public IslandTopCommand(Level addon, CompositeCommand parent) {
|
||||
super(parent, "top", "topten");
|
||||
this.plugin = plugin;
|
||||
this.addon = addon;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -24,7 +24,7 @@ public class IslandTopCommand extends CompositeCommand {
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> list) {
|
||||
plugin.getTopTen().getGUI(getWorld(), user, getPermissionPrefix());
|
||||
addon.getManager().getGUI(getWorld(), user);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,14 +1,15 @@
|
||||
package world.bentobox.level.commands.island;
|
||||
package world.bentobox.level.commands;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.level.Level;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class IslandValueCommand extends CompositeCommand {
|
||||
private final Level addon;
|
||||
|
@ -1,73 +0,0 @@
|
||||
package world.bentobox.level.commands.island;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import world.bentobox.bentobox.api.commands.CompositeCommand;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.level.Level;
|
||||
|
||||
public class IslandLevelCommand extends CompositeCommand {
|
||||
|
||||
private final Level levelPlugin;
|
||||
|
||||
public IslandLevelCommand(Level levelPlugin, CompositeCommand parent) {
|
||||
super(parent, "level");
|
||||
this.levelPlugin = levelPlugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
this.setPermission("island.level");
|
||||
this.setParametersHelp("island.level.parameters");
|
||||
this.setDescription("island.level.description");
|
||||
this.setOnlyPlayer(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
if (!args.isEmpty()) {
|
||||
// Asking for another player's level?
|
||||
// Convert name to a UUID
|
||||
final UUID playerUUID = getPlugin().getPlayers().getUUID(args.get(0));
|
||||
if (playerUUID == null) {
|
||||
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
|
||||
return true;
|
||||
} else if (user.getUniqueId().equals(playerUUID) ) {
|
||||
return this.calculateLevel(user);
|
||||
} else {
|
||||
user.sendMessage("island.level.island-level-is", "[level]", String.valueOf(levelPlugin.getIslandLevel(getWorld(), playerUUID)));
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return this.calculateLevel(user);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method calls island level calculation if it is allowed by cooldown.
|
||||
* @param user User which island level must be calculated.
|
||||
* @return True if le
|
||||
*/
|
||||
private boolean calculateLevel(User user)
|
||||
{
|
||||
int coolDown = this.levelPlugin.getSettings().getLevelWait();
|
||||
|
||||
if (coolDown > 0 && this.checkCooldown(user, null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Self level request
|
||||
this.levelPlugin.calculateIslandLevel(getWorld(), user, user.getUniqueId());
|
||||
|
||||
if (coolDown > 0)
|
||||
{
|
||||
this.setCooldown(user.getUniqueId(), null, coolDown);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,37 +1,23 @@
|
||||
package world.bentobox.level.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.configuration.ConfigComment;
|
||||
import world.bentobox.bentobox.api.configuration.ConfigEntry;
|
||||
import world.bentobox.bentobox.api.configuration.ConfigObject;
|
||||
import world.bentobox.bentobox.api.configuration.StoreAt;
|
||||
import world.bentobox.level.Level;
|
||||
|
||||
@StoreAt(filename="config.yml", path="addons/Level")
|
||||
@ConfigComment("Level Configuration [version]")
|
||||
@ConfigComment("")
|
||||
public class ConfigSettings implements ConfigObject {
|
||||
@ConfigComment("")
|
||||
@ConfigComment("Game Mode Addons")
|
||||
@ConfigComment("Level will hook into these game mode addons. Don't forget to set any world-specific")
|
||||
@ConfigComment("block values below!")
|
||||
@ConfigEntry(path = "game-modes")
|
||||
private List<String> gameModes = Arrays.asList("BSkyBlock","AcidIsland","CaveBlock");
|
||||
|
||||
@ConfigComment("")
|
||||
@ConfigComment("Performance settings")
|
||||
@ConfigComment("Level is very processor-intensive, so these settings may need to be tweaked to optimize for your server")
|
||||
@ConfigComment("Delay between each task that loads chunks for calculating levels")
|
||||
@ConfigComment("Increasing this will slow down level calculations but reduce average load")
|
||||
@ConfigEntry(path = "task-delay")
|
||||
private long taskDelay = 1;
|
||||
|
||||
@ConfigComment("")
|
||||
@ConfigComment("Number of chunks that will be processed per task")
|
||||
@ConfigEntry(path = "chunks")
|
||||
private int chunks = 10;
|
||||
@ConfigComment("Disabled Game Mode Addons")
|
||||
@ConfigComment("Level will NOT hook into these game mode addons.")
|
||||
@ConfigEntry(path = "disabled-game-modes")
|
||||
private List<String> gameModes = Collections.emptyList();
|
||||
|
||||
@ConfigComment("")
|
||||
@ConfigComment("Calculate island level on login")
|
||||
@ -54,6 +40,12 @@ public class ConfigSettings implements ConfigObject {
|
||||
@ConfigEntry(path = "end")
|
||||
private boolean end = false;
|
||||
|
||||
@ConfigComment("")
|
||||
@ConfigComment("Include chest contents in level calculations.")
|
||||
@ConfigComment("Will count blocks in chests or containers.")
|
||||
@ConfigEntry(path = "include-chests")
|
||||
private boolean includeChests = false;
|
||||
|
||||
@ConfigComment("")
|
||||
@ConfigComment("Underwater block multiplier")
|
||||
@ConfigComment("If blocks are below sea-level, they can have a higher value. e.g. 2x")
|
||||
@ -116,48 +108,6 @@ public class ConfigSettings implements ConfigObject {
|
||||
this.gameModes = gameModes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the taskDelay
|
||||
*/
|
||||
public long getTaskDelay() {
|
||||
if (taskDelay < 1L) {
|
||||
Level.getInstance().logError("task-delay must be at least 1");
|
||||
taskDelay = 1;
|
||||
}
|
||||
return taskDelay;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param taskDelay the taskDelay to set
|
||||
*/
|
||||
public void setTaskDelay(long taskDelay) {
|
||||
this.taskDelay = taskDelay;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the chunks
|
||||
*/
|
||||
public int getChunks() {
|
||||
if (chunks < 1) {
|
||||
Level.getInstance().logError("chunks must be at least 1");
|
||||
chunks = 1;
|
||||
}
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param chunks the chunks to set
|
||||
*/
|
||||
public void setChunks(int chunks) {
|
||||
this.chunks = chunks;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the calcOnLogin
|
||||
*/
|
||||
@ -228,7 +178,7 @@ public class ConfigSettings implements ConfigObject {
|
||||
public long getLevelCost() {
|
||||
if (levelCost < 1) {
|
||||
levelCost = 1;
|
||||
Level.getInstance().logError("levelcost in config.yml cannot be less than 1. Setting to 1.");
|
||||
BentoBox.getInstance().logError("levelcost in config.yml cannot be less than 1. Setting to 1.");
|
||||
}
|
||||
return levelCost;
|
||||
}
|
||||
@ -263,7 +213,7 @@ public class ConfigSettings implements ConfigObject {
|
||||
*/
|
||||
public int getLevelWait() {
|
||||
if (levelWait < 0) {
|
||||
Level.getInstance().logError("levelwait must be at least 0");
|
||||
BentoBox.getInstance().logError("levelwait must be at least 0");
|
||||
levelWait = 0;
|
||||
}
|
||||
return levelWait;
|
||||
@ -326,5 +276,24 @@ public class ConfigSettings implements ConfigObject {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the includeChests
|
||||
*/
|
||||
public boolean isIncludeChests() {
|
||||
return includeChests;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param includeChests the includeChests to set
|
||||
*/
|
||||
public void setIncludeChests(boolean includeChests) {
|
||||
this.includeChests = includeChests;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
package world.bentobox.level.event;
|
||||
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* This event is fired when a player clicks on a top ten head.
|
||||
*
|
||||
* @author tastybento
|
||||
*/
|
||||
class TopTenClick extends Event implements Cancellable {
|
||||
|
||||
private boolean cancelled;
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private final String owner;
|
||||
|
||||
|
||||
public TopTenClick(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return name of head owner that was clicked
|
||||
*/
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return getHandlerList();
|
||||
}
|
||||
|
||||
private static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
package world.bentobox.level.event;
|
||||
package world.bentobox.level.events;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import world.bentobox.bentobox.api.events.IslandBaseEvent;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.level.calculators.CalcIslandLevel.Results;
|
||||
import world.bentobox.level.calculators.Results;
|
||||
|
||||
/**
|
||||
* This event is fired after the island level is calculated and before the results are saved.
|
||||
@ -36,30 +36,30 @@ public class IslandLevelCalculatedEvent extends IslandBaseEvent {
|
||||
public Results getResults() {
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return death handicap value
|
||||
*/
|
||||
public int getDeathHandicap() {
|
||||
return results.getDeathHandicap();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the island's initial level. It may be zero if it was never calculated
|
||||
* Get the island's initial level. It may be zero if it was never calculated
|
||||
* or if a player was registered to the island after it was made.
|
||||
* @return initial level of island as calculated when the island was created.
|
||||
*/
|
||||
public long getInitialLevel() {
|
||||
return results.getInitialLevel();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the level calculated
|
||||
*/
|
||||
public long getLevel() {
|
||||
return results.getLevel();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Overwrite the level. This level will be used instead of the calculated level.
|
||||
@ -68,14 +68,14 @@ public class IslandLevelCalculatedEvent extends IslandBaseEvent {
|
||||
public void setLevel(long level) {
|
||||
results.setLevel(level);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return number of points required to next level
|
||||
*/
|
||||
public long getPointsToNextLevel() {
|
||||
return results.getPointsToNextLevel();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return a human readable report explaining how the calculation was made
|
||||
*/
|
@ -1,4 +1,4 @@
|
||||
package world.bentobox.level.event;
|
||||
package world.bentobox.level.events;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -1,7 +1,5 @@
|
||||
package world.bentobox.level.listeners;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.World;
|
||||
@ -20,107 +18,91 @@ import world.bentobox.bentobox.api.events.team.TeamEvent.TeamLeaveEvent;
|
||||
import world.bentobox.bentobox.api.events.team.TeamEvent.TeamSetownerEvent;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.level.Level;
|
||||
import world.bentobox.level.calculators.CalcIslandLevel;
|
||||
|
||||
/**
|
||||
* Listens for new islands or ownership changes and sets the level to zero automatically
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
public class IslandTeamListeners implements Listener {
|
||||
public class IslandActivitiesListeners implements Listener {
|
||||
|
||||
private final Level addon;
|
||||
private final Map<Island, CalcIslandLevel> cil;
|
||||
|
||||
/**
|
||||
* @param addon - addon
|
||||
*/
|
||||
public IslandTeamListeners(Level addon) {
|
||||
public IslandActivitiesListeners(Level addon) {
|
||||
this.addon = addon;
|
||||
cil = new HashMap<>();
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onNewIsland(IslandCreatedEvent e) {
|
||||
// Clear the island setting
|
||||
addon.setInitialIslandLevel(e.getIsland(), 0L);
|
||||
if (e.getIsland().getOwner() != null && e.getIsland().getWorld() != null) {
|
||||
cil.putIfAbsent(e.getIsland(), new CalcIslandLevel(addon, e.getIsland(), () -> zeroLevel(e.getIsland())));
|
||||
}
|
||||
zeroIsland(e.getIsland());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onNewIsland(IslandResettedEvent e) {
|
||||
zeroIsland(e.getIsland());
|
||||
}
|
||||
|
||||
private void zeroIsland(final Island island) {
|
||||
// Clear the island setting
|
||||
addon.setInitialIslandLevel(e.getIsland(), 0L);
|
||||
if (e.getIsland().getOwner() != null && e.getIsland().getWorld() != null) {
|
||||
cil.putIfAbsent(e.getIsland(), new CalcIslandLevel(addon, e.getIsland(), () -> zeroLevel(e.getIsland())));
|
||||
if (island.getOwner() != null && island.getWorld() != null) {
|
||||
addon.getPipeliner().addIsland(island).thenAccept(results -> {
|
||||
addon.getManager().setInitialIslandLevel(island, results.getLevel());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onIslandDelete(IslandPreclearEvent e) {
|
||||
// Remove player from the top ten and level
|
||||
final UUID owner = e.getIsland().getOwner();
|
||||
final World world = e.getIsland().getWorld();
|
||||
addon.setIslandLevel(world, owner, 0);
|
||||
addon.getTopTen().removeEntry(world, owner);
|
||||
UUID uuid = e.getIsland().getOwner();
|
||||
World world = e.getIsland().getWorld();
|
||||
remove(world, uuid);
|
||||
}
|
||||
|
||||
private void remove(World world, UUID uuid) {
|
||||
if (uuid != null && world != null) {
|
||||
addon.getManager().removeEntry(world, uuid);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onNewIslandOwner(TeamSetownerEvent e) {
|
||||
// Remove player from the top ten and level
|
||||
addon.setIslandLevel(e.getIsland().getWorld(), e.getIsland().getOwner(), 0);
|
||||
addon.getTopTen().removeEntry(e.getIsland().getWorld(), e.getIsland().getOwner());
|
||||
remove(e.getIsland().getWorld(), e.getIsland().getOwner());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onIsland(TeamJoinedEvent e) {
|
||||
// Remove player from the top ten and level
|
||||
addon.setIslandLevel(e.getIsland().getWorld(), e.getPlayerUUID(), 0);
|
||||
addon.getTopTen().removeEntry(e.getIsland().getWorld(), e.getPlayerUUID());
|
||||
remove(e.getIsland().getWorld(), e.getPlayerUUID());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onIsland(IslandUnregisteredEvent e) {
|
||||
// Remove player from the top ten and level
|
||||
addon.setIslandLevel(e.getIsland().getWorld(), e.getPlayerUUID(), 0);
|
||||
addon.getTopTen().removeEntry(e.getIsland().getWorld(), e.getPlayerUUID());
|
||||
remove(e.getIsland().getWorld(), e.getPlayerUUID());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onIsland(IslandRegisteredEvent e) {
|
||||
// Remove player from the top ten and level
|
||||
addon.setIslandLevel(e.getIsland().getWorld(), e.getPlayerUUID(), 0);
|
||||
addon.getTopTen().removeEntry(e.getIsland().getWorld(), e.getPlayerUUID());
|
||||
remove(e.getIsland().getWorld(), e.getPlayerUUID());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onIsland(TeamLeaveEvent e) {
|
||||
// Remove player from the top ten and level
|
||||
addon.setIslandLevel(e.getIsland().getWorld(), e.getPlayerUUID(), 0);
|
||||
addon.getTopTen().removeEntry(e.getIsland().getWorld(), e.getPlayerUUID());
|
||||
remove(e.getIsland().getWorld(), e.getPlayerUUID());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onIsland(TeamKickEvent e) {
|
||||
// Remove player from the top ten and level
|
||||
addon.setIslandLevel(e.getIsland().getWorld(), e.getPlayerUUID(), 0);
|
||||
addon.getTopTen().removeEntry(e.getIsland().getWorld(), e.getPlayerUUID());
|
||||
remove(e.getIsland().getWorld(), e.getPlayerUUID());
|
||||
}
|
||||
|
||||
private void zeroLevel(Island island) {
|
||||
if (cil.containsKey(island)) {
|
||||
long level = cil.get(island).getResult().getLevel();
|
||||
// Get deaths
|
||||
int deaths = addon.getPlayers().getDeaths(island.getWorld(), island.getOwner());
|
||||
// Death penalty calculation.
|
||||
if (addon.getSettings().getLevelCost() != 0) {
|
||||
// Add the deaths because this makes the original island that much "bigger"
|
||||
level += deaths * addon.getSettings().getDeathPenalty() / addon.getSettings().getLevelCost();
|
||||
}
|
||||
addon.setInitialIslandLevel(island, level);
|
||||
cil.remove(island);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package world.bentobox.level.listeners;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
@ -26,13 +28,16 @@ public class JoinLeaveListener implements Listener {
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onPlayerJoin(PlayerJoinEvent e) {
|
||||
// Load player into cache
|
||||
addon.getLevelsData(e.getPlayer().getUniqueId());
|
||||
addon.getManager().getLevelsData(e.getPlayer().getUniqueId());
|
||||
// If level calc on login is enabled, run through all the worlds and calculate the level
|
||||
if (addon.getSettings().isCalcOnLogin()) {
|
||||
addon.getPlugin().getAddonsManager().getGameModeAddons().stream()
|
||||
.filter(gm -> addon.getSettings().getGameModes().contains(gm.getDescription().getName()))
|
||||
.forEach(gm -> addon.calculateIslandLevel(gm.getOverWorld(), null, e.getPlayer().getUniqueId()));
|
||||
.filter(gm -> !addon.getSettings().getGameModes().contains(gm.getDescription().getName()))
|
||||
.map(gm -> gm.getIslands().getIsland(gm.getOverWorld(), e.getPlayer().getUniqueId()))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(island -> addon.getManager().calculateLevel(e.getPlayer().getUniqueId(), island));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package world.bentobox.level.objects;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
@ -28,8 +29,11 @@ public class LevelsData implements DataObject {
|
||||
*/
|
||||
@Expose
|
||||
private Map<String, Long> initialLevel = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
public LevelsData() {} // For Bean loading
|
||||
/**
|
||||
* Map of world name to points to next level
|
||||
*/
|
||||
@Expose
|
||||
private Map<String, Long> pointsToNextLevel = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
/**
|
||||
* Create a level entry for target player
|
||||
@ -37,9 +41,8 @@ public class LevelsData implements DataObject {
|
||||
* @param level - level
|
||||
* @param world - world
|
||||
*/
|
||||
public LevelsData(UUID targetPlayer, long level, World world) {
|
||||
public LevelsData(UUID targetPlayer) {
|
||||
uniqueId = targetPlayer.toString();
|
||||
levels.put(world.getName(), level);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
@ -81,8 +84,14 @@ public class LevelsData implements DataObject {
|
||||
this.levels = levels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the island level to level - the initial level
|
||||
* @param world - world where island is
|
||||
* @param lv - level
|
||||
*/
|
||||
public void setLevel(World world, Long lv) {
|
||||
levels.put(world.getName(),lv);
|
||||
String name = world.getName().toLowerCase(Locale.ENGLISH);
|
||||
levels.put(name, lv - this.initialLevel.getOrDefault(name, 0L));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,7 +100,7 @@ public class LevelsData implements DataObject {
|
||||
* @param level - level
|
||||
*/
|
||||
public void setInitialLevel(World world, long level) {
|
||||
this.initialLevel.put(world.getName(), level);
|
||||
this.initialLevel.put(world.getName().toLowerCase(Locale.ENGLISH), level);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,6 +123,51 @@ public class LevelsData implements DataObject {
|
||||
* @return initial island level or 0 by default
|
||||
*/
|
||||
public long getInitialLevel(World world) {
|
||||
return initialLevel.getOrDefault(world.getName(), 0L);
|
||||
return initialLevel.getOrDefault(world.getName().toLowerCase(Locale.ENGLISH), 0L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a world from a player's data
|
||||
* @param world - world to remove
|
||||
*/
|
||||
public void remove(World world) {
|
||||
levels.remove(world.getName().toLowerCase(Locale.ENGLISH));
|
||||
initialLevel.remove(world.getName().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the pointsToNextLevel
|
||||
*/
|
||||
public Map<String, Long> getPointsToNextLevel() {
|
||||
return pointsToNextLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pointsToNextLevel the pointsToNextLevel to set
|
||||
*/
|
||||
public void setPointsToNextLevel(Map<String, Long> pointsToNextLevel) {
|
||||
this.pointsToNextLevel = pointsToNextLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the island points to next level.
|
||||
* This is calculated the last time the level was calculated and will not change dynamically.
|
||||
* @param world - world where island is
|
||||
* @param points - points to next level
|
||||
*/
|
||||
public void setPointsToNextLevel(World world, Long points) {
|
||||
pointsToNextLevel.put(world.getName().toLowerCase(Locale.ENGLISH), points);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the points required to get to the next island level for this world.
|
||||
* This is calculated when the island level is calculated and will not change dynamically.
|
||||
* @param world - world
|
||||
* @return points to next level or zero if unknown
|
||||
*/
|
||||
public long getPointsToNextLevel(World world) {
|
||||
return pointsToNextLevel.getOrDefault(world.getName().toLowerCase(Locale.ENGLISH), 0L);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
package world.bentobox.level.objects;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.bukkit.World;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
@ -14,7 +13,7 @@ import world.bentobox.bentobox.database.objects.DataObject;
|
||||
import world.bentobox.bentobox.database.objects.Table;
|
||||
|
||||
/**
|
||||
* This class stores and sorts the top ten.
|
||||
* This class stores the top ten.
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
@ -27,41 +26,8 @@ public class TopTenData implements DataObject {
|
||||
@Expose
|
||||
private Map<UUID, Long> topTen = new LinkedHashMap<>();
|
||||
|
||||
public Map<UUID, Long> getTopTen() {
|
||||
// Remove any entries that have level values less than 1
|
||||
//topTen.values().removeIf(l -> l < 1);
|
||||
// make copy
|
||||
Map<UUID, Long> snapTopTen = Collections.unmodifiableMap(topTen);
|
||||
return snapTopTen.entrySet().stream()
|
||||
.filter(l -> l.getValue() > 0)
|
||||
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue())).limit(10)
|
||||
.collect(Collectors.toMap(
|
||||
Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the level for the rank
|
||||
* @param rank - rank
|
||||
* @return level value or 0 if none.
|
||||
*/
|
||||
public long getTopTenLevel(int rank) {
|
||||
Map<UUID, Long> tt = getTopTen();
|
||||
return rank <= tt.size() ? (long)tt.values().toArray()[(rank-1)] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the UUID of the rank
|
||||
* @param rank - rank
|
||||
* @return UUID or null
|
||||
*/
|
||||
@Nullable
|
||||
public UUID getTopTenUUID(int rank) {
|
||||
Map<UUID, Long> tt = getTopTen();
|
||||
return rank <= tt.size() ? (UUID)tt.keySet().toArray()[(rank-1)] : null;
|
||||
}
|
||||
|
||||
public void setTopTen(Map<UUID, Long> topTen) {
|
||||
this.topTen = topTen;
|
||||
public TopTenData(World k) {
|
||||
uniqueId = k.getName().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -73,35 +39,20 @@ public class TopTenData implements DataObject {
|
||||
@Override
|
||||
public void setUniqueId(String uniqueId) {
|
||||
// This is the world name - make it always lowercase
|
||||
this.uniqueId = uniqueId.toLowerCase();
|
||||
this.uniqueId = uniqueId.toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
/**
|
||||
* @return the topTen
|
||||
*/
|
||||
public Map<UUID, Long> getTopTen() {
|
||||
return topTen;
|
||||
}
|
||||
/**
|
||||
* @param topTen the topTen to set
|
||||
*/
|
||||
public void setTopTen(Map<UUID, Long> topTen) {
|
||||
this.topTen = topTen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add level for this island owner or team leader, sort the top ten and limit to ten entries
|
||||
* @param uuid - UUID of owner or team leader
|
||||
* @param level - island level
|
||||
*/
|
||||
public void addLevel(UUID uuid, Long level) {
|
||||
this.topTen.put(uuid, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the level for this UUID, or zero if the UUID is not found
|
||||
* @param uuid - UUID to check
|
||||
* @return island level
|
||||
*/
|
||||
public long getLevel(UUID uuid) {
|
||||
if (topTen.containsKey(uuid))
|
||||
return topTen.get(uuid);
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes ownerUUID from the top ten
|
||||
* @param ownerUUID - UUID to remove
|
||||
*/
|
||||
public void remove(UUID ownerUUID) {
|
||||
this.topTen.remove(ownerUUID);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -38,6 +38,6 @@ public class LevelRequestHandler extends AddonRequestHandler {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
return addon.getIslandLevel(Bukkit.getWorld((String) map.get("world-name")), (UUID) map.get("player"));
|
||||
return addon.getManager().getIslandLevel(Bukkit.getWorld((String) map.get("world-name")), (UUID) map.get("player"));
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,6 @@ public class TopTenRequestHandler extends AddonRequestHandler {
|
||||
}
|
||||
|
||||
// No null check required
|
||||
return addon.getTopTen().getTopTenList(Bukkit.getWorld((String) map.get(WORLD_NAME))).getTopTen();
|
||||
return addon.getManager().getTopTen(Bukkit.getWorld((String) map.get(WORLD_NAME)), 10);
|
||||
}
|
||||
}
|
||||
|
@ -4,83 +4,24 @@ version: ${version}${build.number}
|
||||
icon: DIAMOND
|
||||
api-version: 1.14
|
||||
|
||||
softdepend: AcidIsland, BSkyBlock, CaveBlock, AOneBlock, SkyGrid
|
||||
|
||||
authors: tastybento
|
||||
|
||||
permissions:
|
||||
bskyblock.intopten:
|
||||
'[gamemode].intopten':
|
||||
description: Player is in the top ten.
|
||||
default: true
|
||||
bskyblock.island.level:
|
||||
'[gamemode].island.level':
|
||||
description: Player can use level command
|
||||
default: true
|
||||
bskyblock.island.top:
|
||||
'[gamemode].island.top':
|
||||
description: Player can use top ten command
|
||||
default: true
|
||||
bskyblock.island.value:
|
||||
'[gamemode].island.value':
|
||||
description: Player can use value command
|
||||
default: true
|
||||
bskyblock.admin.level:
|
||||
'[gamemode].admin.level':
|
||||
description: Player can use admin level command
|
||||
default: true
|
||||
bskyblock.admin.topten:
|
||||
description: Player can use admin top ten command
|
||||
default: true
|
||||
|
||||
acidisland.intopten:
|
||||
description: Player is in the top ten.
|
||||
default: true
|
||||
acidisland.island.level:
|
||||
description: Player can use level command
|
||||
default: true
|
||||
acidisland.island.top:
|
||||
description: Player can use top ten command
|
||||
default: true
|
||||
acidisland.island.value:
|
||||
description: Player can use value command
|
||||
default: true
|
||||
acidisland.admin.level:
|
||||
description: Player can use admin level command
|
||||
default: true
|
||||
acidisland.admin.topten:
|
||||
description: Player can use admin top ten command
|
||||
default: true
|
||||
|
||||
caveblock.intopten:
|
||||
description: Player is in the top ten.
|
||||
default: true
|
||||
caveblock.island.level:
|
||||
description: Player can use level command
|
||||
default: true
|
||||
caveblock.island.top:
|
||||
description: Player can use top ten command
|
||||
default: true
|
||||
caveblock.island.value:
|
||||
description: Player can use value command
|
||||
default: true
|
||||
caveblock.admin.level:
|
||||
description: Player can use admin level command
|
||||
default: true
|
||||
caveblock.admin.topten:
|
||||
description: Player can use admin top ten command
|
||||
default: true
|
||||
|
||||
aoneblock.intopten:
|
||||
description: Player is in the top ten.
|
||||
default: true
|
||||
aoneblock.island.level:
|
||||
description: Player can use level command
|
||||
default: true
|
||||
aoneblock.island.top:
|
||||
description: Player can use top ten command
|
||||
default: true
|
||||
aoneblock.island.value:
|
||||
description: Player can use value command
|
||||
default: true
|
||||
aoneblock.admin.level:
|
||||
description: Player can use admin level command
|
||||
default: true
|
||||
aoneblock.admin.topten:
|
||||
'[gamemode].admin.topten':
|
||||
description: Player can use admin top ten command
|
||||
default: true
|
||||
|
@ -1,58 +1,48 @@
|
||||
# Config file for Level add-on Version ${version}
|
||||
|
||||
# Game Mode Addons
|
||||
# Level will hook into these game mode addons. Don't forget to set any world-specific
|
||||
# block values below!
|
||||
game-modes:
|
||||
- AcidIsland
|
||||
- BSkyBlock
|
||||
- CaveBlock
|
||||
#- SkyGrid
|
||||
#- AOneBlock
|
||||
|
||||
# Performance settings
|
||||
# Level is very processor-intensive, so these settings may need to be tweaked to optimize for your server
|
||||
# Delay between each task that loads chunks for calculating levels
|
||||
# Increasing this will slow down level calculations but reduce average load
|
||||
task-delay: 1
|
||||
|
||||
# Number of chunks that will be processed per task
|
||||
chunks: 10
|
||||
|
||||
# Level Configuration ${version}
|
||||
#
|
||||
#
|
||||
# Disabled Game Mode Addons
|
||||
# Level will NOT hook into these game mode addons.
|
||||
disabled-game-modes:
|
||||
- AOneBlock
|
||||
#
|
||||
# Calculate island level on login
|
||||
# This silently calculates the player's island level when they login
|
||||
# This applies to all islands the player has on the server, e.g., BSkyBlock, AcidIsland
|
||||
login: false
|
||||
|
||||
#
|
||||
# Include nether island in level calculations.
|
||||
# Warning: Enabling this mid-game will give players with an island a jump in
|
||||
# island level. New islands will be correctly zeroed.
|
||||
nether: false
|
||||
|
||||
# Include end island in level calculations
|
||||
#
|
||||
# Include end island in level calculations.
|
||||
# Warning: Enabling this mid-game will give players with an island a jump in
|
||||
# island level. New islands will be correctly zeroed.
|
||||
end: false
|
||||
|
||||
#
|
||||
# Include chest contents in level calculations.
|
||||
# Will count blocks in chests or containers.
|
||||
include-chests: false
|
||||
#
|
||||
# Underwater block multiplier
|
||||
# If blocks are below sea-level, they can have a higher value. e.g. 2x
|
||||
# Promotes under-water development if there is a sea. Value can be fractional.
|
||||
underwater: 1.0
|
||||
|
||||
#
|
||||
# Value of one island level. Default 100. Minimum value is 1.
|
||||
levelcost: 100
|
||||
|
||||
#
|
||||
# Island level calculation formula
|
||||
# blocks - the sum total of all block values, less any death penalty
|
||||
# level_cost - in a linear equation, the value of one level
|
||||
# This formula can include +,=,*,/,sqrt,^,sin,cos,tan. Result will always be rounded to a long integer
|
||||
# for example, an alternative non-linear option could be: 3 * sqrt(blocks / level_cost)
|
||||
level-calc: blocks / level_cost
|
||||
|
||||
|
||||
#
|
||||
# Cooldown between level requests in seconds
|
||||
levelwait: 60
|
||||
|
||||
#
|
||||
# Death penalty
|
||||
# How many block values a player will lose per death.
|
||||
# Default value of 100 means that for every death, the player will lose 1 level (if levelcost is 100)
|
||||
@ -60,9 +50,8 @@ levelwait: 60
|
||||
deathpenalty: 100
|
||||
# Sum team deaths - if true, all the teams deaths are summed
|
||||
# If false, only the leader's deaths counts
|
||||
sumteamdeaths: false
|
||||
# For other death related settings, see the GameModeAddon's config.yml settings.
|
||||
|
||||
sumteamdeaths: false
|
||||
# Shorthand island level
|
||||
# Shows large level values rounded down, e.g., 10,345 -> 10k
|
||||
shorthand: false
|
||||
|
@ -9,7 +9,7 @@ admin:
|
||||
description: "calculate the island level for player"
|
||||
top:
|
||||
description: "show the top ten list"
|
||||
unknown-world: "&cUnknown world!"
|
||||
unknown-world: "&c Unknown world!"
|
||||
display: "&f[rank]. &a[name] &7- &b[level]"
|
||||
remove:
|
||||
description: "remove player from Top Ten"
|
||||
@ -19,22 +19,23 @@ island:
|
||||
level:
|
||||
parameters: "[player]"
|
||||
description: "calculate your island level or show the level of [player]"
|
||||
calculating: "&aCalculating level..."
|
||||
island-level-is: "&aIsland level is &b[level]"
|
||||
required-points-to-next-level: "&a[points] points required until the next level"
|
||||
calculating: "&a Calculating level..."
|
||||
in-queue: "&a You are number [number] in the queue"
|
||||
island-level-is: "&a Island level is &b[level]"
|
||||
required-points-to-next-level: "&a [points] points required until the next level"
|
||||
deaths: "&c([number] deaths)"
|
||||
cooldown: "&cYou must wait &b[time] &cseconds until you can do that again"
|
||||
cooldown: "&c You must wait &b[time] &c seconds until you can do that again"
|
||||
|
||||
top:
|
||||
description: "show the Top Ten"
|
||||
gui-title: "&aTop Ten"
|
||||
gui-title: "&a Top Ten"
|
||||
gui-heading: "&6[name]: &B[rank]"
|
||||
island-level: "&BLevel [level]"
|
||||
warp-to: "&AWarping to [name]'s island"
|
||||
island-level: "&b Level [level]"
|
||||
warp-to: "&A Warping to [name]'s island"
|
||||
|
||||
value:
|
||||
description: "shows the value of any block"
|
||||
success: "&7The value of this block is: &e[value]"
|
||||
success-underwater: "&7The value of this block below sea-level: &e[value]"
|
||||
empty-hand: "&cThere are no blocks in your hand"
|
||||
no-value: "&cThat item has no value."
|
||||
success: "&7 The value of this block is: &e[value]"
|
||||
success-underwater: "&7 The value of this block below sea-level: &e[value]"
|
||||
empty-hand: "&c There are no blocks in your hand"
|
||||
no-value: "&c That item has no value."
|
@ -1,115 +0,0 @@
|
||||
package world.bentobox.level;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.managers.IslandWorldManager;
|
||||
import world.bentobox.bentobox.managers.IslandsManager;
|
||||
import world.bentobox.level.calculators.PlayerLevel;
|
||||
import world.bentobox.level.config.BlockConfig;
|
||||
import world.bentobox.level.config.ConfigSettings;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({Bukkit.class, LevelPresenter.class})
|
||||
public class LevelPresenterTest {
|
||||
|
||||
@Mock
|
||||
private BentoBox plugin;
|
||||
@Mock
|
||||
private Level addon;
|
||||
@Mock
|
||||
private PlayerLevel pl;
|
||||
@Mock
|
||||
private ConfigSettings settings;
|
||||
@Mock
|
||||
private BlockConfig blockConfig;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
IslandWorldManager iwm = mock(IslandWorldManager.class);
|
||||
when(plugin.getIWM()).thenReturn(iwm);
|
||||
when(iwm.getPermissionPrefix(Mockito.any())).thenReturn("world");
|
||||
IslandsManager im = mock(IslandsManager.class);
|
||||
when(plugin.getIslands()).thenReturn(im);
|
||||
// Has island
|
||||
when(im.hasIsland(Mockito.any(), Mockito.any(User.class))).thenReturn(true);
|
||||
// In team
|
||||
when(im.inTeam(Mockito.any(), Mockito.any())).thenReturn(true);
|
||||
// team leader
|
||||
when(im.getOwner(Mockito.any(), Mockito.any())).thenReturn(UUID.randomUUID());
|
||||
|
||||
// Player level
|
||||
PowerMockito.whenNew(PlayerLevel.class).withAnyArguments().thenReturn(pl);
|
||||
|
||||
// Settings
|
||||
when(addon.getSettings()).thenReturn(settings);
|
||||
when(addon.getBlockConfig()).thenReturn(blockConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link LevelPresenter#LevelPresenter(Level, world.bentobox.bentobox.BentoBox)}.
|
||||
*/
|
||||
@Test
|
||||
public void testLevelPresenter() {
|
||||
new LevelPresenter(addon, plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link LevelPresenter#calculateIslandLevel(org.bukkit.World, world.bentobox.bentobox.api.user.User, java.util.UUID)}.
|
||||
*/
|
||||
@Test
|
||||
public void testCalculateIslandLevel() {
|
||||
LevelPresenter lp = new LevelPresenter(addon, plugin);
|
||||
World world = mock(World.class);
|
||||
User sender = mock(User.class);
|
||||
UUID targetPlayer = UUID.randomUUID();
|
||||
lp.calculateIslandLevel(world, sender, targetPlayer);
|
||||
|
||||
Mockito.verify(sender).sendMessage("island.level.calculating");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link LevelPresenter#getLevelString(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLevelStringLong() {
|
||||
LevelPresenter lp = new LevelPresenter(addon, plugin);
|
||||
assertEquals("123456789", lp.getLevelString(123456789L));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link LevelPresenter#getLevelString(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLevelStringLongShorthand() {
|
||||
when(settings.isShorthand()).thenReturn(true);
|
||||
LevelPresenter lp = new LevelPresenter(addon, plugin);
|
||||
assertEquals("123.5M", lp.getLevelString(123456789L));
|
||||
assertEquals("1.2k", lp.getLevelString(1234L));
|
||||
assertEquals("123.5G", lp.getLevelString(123456789352L));
|
||||
assertEquals("1.2T", lp.getLevelString(1234567893524L));
|
||||
assertEquals("12345.7T", lp.getLevelString(12345678345345349L));
|
||||
|
||||
}
|
||||
}
|
@ -1,13 +1,9 @@
|
||||
package world.bentobox.level;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@ -67,7 +63,7 @@ import world.bentobox.bentobox.managers.IslandsManager;
|
||||
import world.bentobox.bentobox.managers.PlaceholdersManager;
|
||||
import world.bentobox.level.config.BlockConfig;
|
||||
import world.bentobox.level.config.ConfigSettings;
|
||||
import world.bentobox.level.listeners.IslandTeamListeners;
|
||||
import world.bentobox.level.listeners.IslandActivitiesListeners;
|
||||
import world.bentobox.level.listeners.JoinLeaveListener;
|
||||
|
||||
/**
|
||||
@ -97,6 +93,9 @@ public class LevelTest {
|
||||
@Mock
|
||||
private BukkitScheduler scheduler;
|
||||
|
||||
@Mock
|
||||
private Settings pluginSettings;
|
||||
|
||||
private Level addon;
|
||||
|
||||
@Mock
|
||||
@ -115,8 +114,6 @@ public class LevelTest {
|
||||
private PluginManager pim;
|
||||
@Mock
|
||||
private BlockConfig blockConfig;
|
||||
@Mock
|
||||
private Settings pluginSettings;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws IOException {
|
||||
@ -152,6 +149,12 @@ public class LevelTest {
|
||||
// Set up plugin
|
||||
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
|
||||
when(plugin.getLogger()).thenReturn(Logger.getAnonymousLogger());
|
||||
|
||||
// The database type has to be created one line before the thenReturn() to work!
|
||||
DatabaseType value = DatabaseType.JSON;
|
||||
when(plugin.getSettings()).thenReturn(pluginSettings);
|
||||
when(pluginSettings.getDatabaseType()).thenReturn(value);
|
||||
|
||||
//when(plugin.isEnabled()).thenReturn(true);
|
||||
// Command manager
|
||||
CommandsManager cm = mock(CommandsManager.class);
|
||||
@ -172,6 +175,7 @@ public class LevelTest {
|
||||
when(plugin.getIWM()).thenReturn(iwm);
|
||||
|
||||
|
||||
|
||||
// Player has island to begin with
|
||||
when(im.getIsland(Mockito.any(), Mockito.any(UUID.class))).thenReturn(island);
|
||||
when(plugin.getIslands()).thenReturn(im);
|
||||
@ -201,6 +205,7 @@ public class LevelTest {
|
||||
when(am.getGameModeAddons()).thenReturn(Collections.singletonList(gameMode));
|
||||
AddonDescription desc2 = new AddonDescription.Builder("bentobox", "BSkyBlock", "1.3").description("test").authors("tasty").build();
|
||||
when(gameMode.getDescription()).thenReturn(desc2);
|
||||
when(gameMode.getOverWorld()).thenReturn(world);
|
||||
|
||||
// Player command
|
||||
@NonNull
|
||||
@ -214,10 +219,6 @@ public class LevelTest {
|
||||
when(plugin.getFlagsManager()).thenReturn(fm);
|
||||
when(fm.getFlags()).thenReturn(Collections.emptyList());
|
||||
|
||||
// The database type has to be created one line before the thenReturn() to work!
|
||||
DatabaseType value = DatabaseType.JSON;
|
||||
when(plugin.getSettings()).thenReturn(pluginSettings);
|
||||
when(pluginSettings.getDatabaseType()).thenReturn(value);
|
||||
|
||||
// Bukkit
|
||||
PowerMockito.mockStatic(Bukkit.class);
|
||||
@ -275,47 +276,20 @@ public class LevelTest {
|
||||
verify(plugin).logWarning("[Level] Level Addon: No such world in blockconfig.yml : acidisland_world");
|
||||
verify(plugin).log("[Level] Level hooking into BSkyBlock");
|
||||
verify(cmd, times(3)).getAddon(); // Three commands
|
||||
verify(adminCmd, times(2)).getAddon(); // Two commands
|
||||
verify(adminCmd, times(3)).getAddon(); // Three commands
|
||||
// Placeholders
|
||||
verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_island_level"), any());
|
||||
verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_visited_island_level"), any());
|
||||
verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_points_to_next_level"), any());
|
||||
for (int i = 1; i < 11; i++) {
|
||||
verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_top_name_" + i), any());
|
||||
verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_top_value_" + i), any());
|
||||
}
|
||||
// Commands
|
||||
verify(am).registerListener(eq(addon), any(IslandTeamListeners.class));
|
||||
verify(am).registerListener(eq(addon), any(IslandActivitiesListeners.class));
|
||||
verify(am).registerListener(eq(addon), any(JoinLeaveListener.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.Level#getIslandLevel(org.bukkit.World, java.util.UUID)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetIslandLevelUnknown() {
|
||||
addon.onEnable();
|
||||
assertEquals(0L, addon.getIslandLevel(world, UUID.randomUUID()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.Level#getIslandLevel(org.bukkit.World, java.util.UUID)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetIslandLevelNullTarget() {
|
||||
addon.onEnable();
|
||||
assertEquals(0L, addon.getIslandLevel(world, UUID.randomUUID()));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.Level#getLevelsData(java.util.UUID)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLevelsDataUnknown() {
|
||||
addon.onEnable();
|
||||
assertNull(addon.getLevelsData(UUID.randomUUID()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.Level#getSettings()}.
|
||||
*/
|
||||
@ -326,55 +300,5 @@ public class LevelTest {
|
||||
assertEquals(100, s.getLevelCost());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.Level#getTopTen()}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTopTen() {
|
||||
addon.onEnable();
|
||||
assertNotNull(addon.getTopTen());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.Level#getLevelPresenter()}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLevelPresenter() {
|
||||
addon.onEnable();
|
||||
assertNotNull(addon.getLevelPresenter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.Level#setIslandLevel(org.bukkit.World, java.util.UUID, long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testSetIslandLevel() {
|
||||
addon.onEnable();
|
||||
addon.setIslandLevel(world, uuid, 345L);
|
||||
assertEquals(345L, addon.getIslandLevel(world, uuid));
|
||||
verify(plugin, never()).logError(anyString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.Level#getInitialIslandLevel(world.bentobox.bentobox.database.objects.Island)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetInitialIslandLevel() {
|
||||
addon.onEnable();
|
||||
addon.setInitialIslandLevel(island, 40);
|
||||
verify(plugin, never()).logError(anyString());
|
||||
assertEquals(40, addon.getInitialIslandLevel(island));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.Level#getHandler()}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetHandler() {
|
||||
addon.onEnable();
|
||||
assertNotNull(addon.getHandler());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
420
src/test/java/world/bentobox/level/LevelsManagerTest.java
Normal file
420
src/test/java/world/bentobox/level/LevelsManagerTest.java
Normal file
@ -0,0 +1,420 @@
|
||||
package world.bentobox.level;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemFactory;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.Settings;
|
||||
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.managers.IslandWorldManager;
|
||||
import world.bentobox.bentobox.managers.PlayersManager;
|
||||
import world.bentobox.level.calculators.Pipeliner;
|
||||
import world.bentobox.level.calculators.Results;
|
||||
import world.bentobox.level.config.ConfigSettings;
|
||||
import world.bentobox.level.objects.LevelsData;
|
||||
import world.bentobox.level.objects.TopTenData;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({Bukkit.class, BentoBox.class, DatabaseSetup.class, PanelBuilder.class})
|
||||
public class LevelsManagerTest {
|
||||
|
||||
@Mock
|
||||
private static AbstractDatabaseHandler<Object> handler;
|
||||
@Mock
|
||||
Level addon;
|
||||
@Mock
|
||||
private BentoBox plugin;
|
||||
@Mock
|
||||
private Settings pluginSettings;
|
||||
|
||||
|
||||
// Class under test
|
||||
private LevelsManager lm;
|
||||
@Mock
|
||||
private Island island;
|
||||
@Mock
|
||||
private Pipeliner pipeliner;
|
||||
private CompletableFuture<Results> cf;
|
||||
private UUID uuid;
|
||||
@Mock
|
||||
private World world;
|
||||
@Mock
|
||||
private Player player;
|
||||
@Mock
|
||||
private ConfigSettings settings;
|
||||
@Mock
|
||||
private User user;
|
||||
@Mock
|
||||
private PlayersManager pm;
|
||||
@Mock
|
||||
private Inventory inv;
|
||||
@Mock
|
||||
private IslandWorldManager iwm;
|
||||
@Mock
|
||||
private PluginManager pim;
|
||||
@Mock
|
||||
private LevelsData levelsData;
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
// This has to be done beforeClass otherwise the tests will interfere with each other
|
||||
handler = mock(AbstractDatabaseHandler.class);
|
||||
// Database
|
||||
PowerMockito.mockStatic(DatabaseSetup.class);
|
||||
DatabaseSetup dbSetup = mock(DatabaseSetup.class);
|
||||
when(DatabaseSetup.getDatabase()).thenReturn(dbSetup);
|
||||
when(dbSetup.getHandler(any())).thenReturn(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
when(addon.getPlugin()).thenReturn(plugin);
|
||||
// Set up plugin
|
||||
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
|
||||
|
||||
// Bukkit
|
||||
PowerMockito.mockStatic(Bukkit.class);
|
||||
when(Bukkit.getWorld(anyString())).thenReturn(world);
|
||||
when(Bukkit.getPluginManager()).thenReturn(pim);
|
||||
when(Bukkit.getPlayer(any(UUID.class))).thenReturn(player);
|
||||
|
||||
// The database type has to be created one line before the thenReturn() to work!
|
||||
DatabaseType value = DatabaseType.JSON;
|
||||
when(plugin.getSettings()).thenReturn(pluginSettings);
|
||||
when(pluginSettings.getDatabaseType()).thenReturn(value);
|
||||
|
||||
// Pipeliner
|
||||
when(addon.getPipeliner()).thenReturn(pipeliner);
|
||||
cf = new CompletableFuture<>();
|
||||
when(pipeliner.addIsland(any())).thenReturn(cf);
|
||||
|
||||
// Island
|
||||
uuid = UUID.randomUUID();
|
||||
ImmutableSet<UUID> iset = ImmutableSet.of(uuid);
|
||||
when(island.getMemberSet()).thenReturn(iset);
|
||||
when(island.getOwner()).thenReturn(uuid);
|
||||
when(island.getWorld()).thenReturn(world);
|
||||
|
||||
|
||||
// Player
|
||||
when(player.getUniqueId()).thenReturn(uuid);
|
||||
when(player.hasPermission(anyString())).thenReturn(true);
|
||||
|
||||
// World
|
||||
when(world.getName()).thenReturn("bskyblock-world");
|
||||
|
||||
// Settings
|
||||
when(addon.getSettings()).thenReturn(settings);
|
||||
|
||||
// User
|
||||
when(user.getTranslation(anyString())).thenAnswer((Answer<String>) invocation -> invocation.getArgument(0, String.class));
|
||||
when(user.getTranslation(eq("island.top.gui-heading"), eq("[name]"), anyString(), eq("[rank]"), anyString())).thenReturn("gui-heading");
|
||||
when(user.getTranslation(eq("island.top.island-level"),eq("[level]"), anyString())).thenReturn("island-level");
|
||||
when(user.getPlayer()).thenReturn(player);
|
||||
|
||||
// Player Manager
|
||||
when(addon.getPlayers()).thenReturn(pm);
|
||||
when(pm.getName(any())).thenReturn("player1",
|
||||
"player2",
|
||||
"player3",
|
||||
"player4",
|
||||
"player5",
|
||||
"player6",
|
||||
"player7",
|
||||
"player8",
|
||||
"player9",
|
||||
"player10"
|
||||
);
|
||||
// Mock item factory (for itemstacks)
|
||||
ItemFactory itemFactory = mock(ItemFactory.class);
|
||||
when(Bukkit.getItemFactory()).thenReturn(itemFactory);
|
||||
ItemMeta itemMeta = mock(ItemMeta.class);
|
||||
when(itemFactory.getItemMeta(any())).thenReturn(itemMeta);
|
||||
|
||||
// Has perms
|
||||
when(player.hasPermission(anyString())).thenReturn(true);
|
||||
// Fill the top ten
|
||||
TopTenData ttd = new TopTenData(world);
|
||||
ttd.setUniqueId("world");
|
||||
List<Object> topTen = new ArrayList<>();
|
||||
for (long i = -5; i < 5; i ++) {
|
||||
ttd.getTopTen().put(UUID.randomUUID(), i);
|
||||
}
|
||||
// Include a known UUID
|
||||
ttd.getTopTen().put(uuid, 456789L);
|
||||
topTen.add(ttd);
|
||||
when(handler.loadObjects()).thenReturn(topTen);
|
||||
when(handler.objectExists(anyString())).thenReturn(true);
|
||||
when(levelsData.getLevel(any())).thenReturn(-5L, -4L, -3L, -2L, -1L, 0L, 1L, 2L, 3L, 4L, 5L, 45678L);
|
||||
when(levelsData.getUniqueId()).thenReturn(uuid.toString());
|
||||
when(handler.loadObject(anyString())).thenReturn(levelsData );
|
||||
|
||||
|
||||
// Inventory GUI
|
||||
when(Bukkit.createInventory(any(), anyInt(), anyString())).thenReturn(inv);
|
||||
|
||||
// IWM
|
||||
when(plugin.getIWM()).thenReturn(iwm);
|
||||
when(iwm.getPermissionPrefix(any())).thenReturn("bskyblock.");
|
||||
|
||||
lm = new LevelsManager(addon);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
deleteAll(new File("database"));
|
||||
User.clearUsers();
|
||||
Mockito.framework().clearInlineMocks();
|
||||
}
|
||||
|
||||
private static void deleteAll(File file) throws IOException {
|
||||
if (file.exists()) {
|
||||
Files.walk(file.toPath())
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.map(Path::toFile)
|
||||
.forEach(File::delete);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#calculateLevel(UUID, world.bentobox.bentobox.database.objects.Island)}.
|
||||
*/
|
||||
@Test
|
||||
public void testCalculateLevel() {
|
||||
Results results = new Results();
|
||||
results.setLevel(10000);
|
||||
results.setInitialLevel(3);
|
||||
lm.calculateLevel(uuid, island);
|
||||
cf.complete(results);
|
||||
|
||||
assertTrue(lm.getLevelsData(uuid).getLevel(world) == 10000);
|
||||
//Map<UUID, Long> tt = lm.getTopTen(world, 10);
|
||||
//assertEquals(1, tt.size());
|
||||
//assertTrue(tt.get(uuid) == 10000);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#getInitialLevel(world.bentobox.bentobox.database.objects.Island)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetInitialLevel() {
|
||||
assertEquals(0,lm.getInitialLevel(island));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#getIslandLevel(org.bukkit.World, java.util.UUID)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetIslandLevel() {
|
||||
assertEquals(-5, lm.getIslandLevel(world, uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#getPointsToNextString(org.bukkit.World, java.util.UUID)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetPointsToNextString() {
|
||||
assertEquals("0", lm.getPointsToNextString(world, UUID.randomUUID()));
|
||||
assertEquals("0", lm.getPointsToNextString(world, uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#getIslandLevelString(org.bukkit.World, java.util.UUID)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetIslandLevelString() {
|
||||
assertEquals("-5", lm.getIslandLevelString(world, uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#getLevelsData(java.util.UUID)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLevelsData() {
|
||||
assertEquals(levelsData, lm.getLevelsData(uuid));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#formatLevel(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testFormatLevel() {
|
||||
assertEquals("123456789", lm.formatLevel(123456789L));
|
||||
when(settings.isShorthand()).thenReturn(true);
|
||||
assertEquals("123.5M", lm.formatLevel(123456789L));
|
||||
assertEquals("1.2k", lm.formatLevel(1234L));
|
||||
assertEquals("123.5G", lm.formatLevel(123456789352L));
|
||||
assertEquals("1.2T", lm.formatLevel(1234567893524L));
|
||||
assertEquals("12345.7T", lm.formatLevel(12345678345345349L));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#getTopTen(org.bukkit.World, int)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTopTenEmpty() {
|
||||
Map<UUID, Long> tt = lm.getTopTen(world, 10);
|
||||
assertTrue(tt.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#getTopTen(org.bukkit.World, int)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTopTen() {
|
||||
testLoadTopTens();
|
||||
Map<UUID, Long> tt = lm.getTopTen(world, 10);
|
||||
assertFalse(tt.isEmpty());
|
||||
assertEquals(5, tt.size());
|
||||
assertEquals(1, lm.getTopTen(world, 1).size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#hasTopTenPerm(org.bukkit.World, java.util.UUID)}.
|
||||
*/
|
||||
@Test
|
||||
public void testHasTopTenPerm() {
|
||||
assertTrue(lm.hasTopTenPerm(world, uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#loadTopTens()}.
|
||||
*/
|
||||
@Test
|
||||
public void testLoadTopTens() {
|
||||
lm.loadTopTens();
|
||||
PowerMockito.verifyStatic(Bukkit.class); // 1
|
||||
Bukkit.getWorld(eq("world"));
|
||||
verify(addon).log(eq("Loaded TopTen for bskyblock-world"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#loadTopTens()}.
|
||||
*/
|
||||
@Test
|
||||
public void testLoadTopTensNullWorlds() {
|
||||
when(Bukkit.getWorld(anyString())).thenReturn(null);
|
||||
lm.loadTopTens();
|
||||
PowerMockito.verifyStatic(Bukkit.class); // 1
|
||||
Bukkit.getWorld(eq("world"));
|
||||
verify(addon).logError(eq("TopTen world 'world' is not known on server. You might want to delete this table. Skipping..."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#removeEntry(org.bukkit.World, java.util.UUID)}.
|
||||
*/
|
||||
@Test
|
||||
public void testRemoveEntry() {
|
||||
testLoadTopTens();
|
||||
Map<UUID, Long> tt = lm.getTopTen(world, 10);
|
||||
assertTrue(tt.containsKey(uuid));
|
||||
lm.removeEntry(world, uuid);
|
||||
tt = lm.getTopTen(world, 10);
|
||||
assertFalse(tt.containsKey(uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#save()}.
|
||||
*/
|
||||
@Test
|
||||
public void testSave() {
|
||||
lm.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#setInitialIslandLevel(world.bentobox.bentobox.database.objects.Island, long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testSetInitialIslandLevel() {
|
||||
lm.setInitialIslandLevel(island, 10);
|
||||
assertEquals(10, lm.getInitialLevel(island));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#setIslandLevel(org.bukkit.World, java.util.UUID, long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testSetIslandLevel() {
|
||||
lm.setIslandLevel(world, uuid, 1234);
|
||||
assertEquals(1234, lm.getIslandLevel(world, uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.LevelsManager#getGUI(org.bukkit.World, world.bentobox.bentobox.api.user.User)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetGUI() {
|
||||
lm.getGUI(world, user);
|
||||
verify(user).getTranslation(eq("island.top.gui-title"));
|
||||
verify(player).openInventory(inv);
|
||||
/*
|
||||
int[] SLOTS = new int[] {4, 12, 14, 19, 20, 21, 22, 23, 24, 25};
|
||||
for (int i : SLOTS) {
|
||||
verify(inv).setItem(eq(i), any());
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
@ -1,278 +0,0 @@
|
||||
package world.bentobox.level;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemFactory;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.panels.builders.PanelBuilder;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.managers.IslandWorldManager;
|
||||
import world.bentobox.bentobox.managers.IslandsManager;
|
||||
import world.bentobox.bentobox.managers.PlayersManager;
|
||||
import world.bentobox.level.config.BlockConfig;
|
||||
import world.bentobox.level.objects.TopTenData;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({Bukkit.class, BentoBox.class, DatabaseSetup.class, PanelBuilder.class})
|
||||
public class TopTenTest {
|
||||
|
||||
@Mock
|
||||
private Level addon;
|
||||
@Mock
|
||||
private World world;
|
||||
@Mock
|
||||
private BentoBox plugin;
|
||||
@Mock
|
||||
private static AbstractDatabaseHandler<Object> handler;
|
||||
@Mock
|
||||
private IslandsManager im;
|
||||
@Mock
|
||||
private Player player;
|
||||
@Mock
|
||||
private IslandWorldManager iwm;
|
||||
@Mock
|
||||
private User user;
|
||||
@Mock
|
||||
private PlayersManager pm;
|
||||
@Mock
|
||||
private Inventory inv;
|
||||
@Mock
|
||||
private LevelPresenter lp;
|
||||
@Mock
|
||||
private BlockConfig settings;
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
// This has to be done beforeClass otherwise the tests will interfere with each other
|
||||
handler = mock(AbstractDatabaseHandler.class);
|
||||
// Database
|
||||
PowerMockito.mockStatic(DatabaseSetup.class);
|
||||
DatabaseSetup dbSetup = mock(DatabaseSetup.class);
|
||||
when(DatabaseSetup.getDatabase()).thenReturn(dbSetup);
|
||||
when(dbSetup.getHandler(any())).thenReturn(handler);
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
|
||||
when(addon.getPlugin()).thenReturn(plugin);
|
||||
|
||||
PowerMockito.mockStatic(Bukkit.class);
|
||||
when(Bukkit.getWorld(anyString())).thenReturn(world);
|
||||
Server server = mock(Server.class);
|
||||
when(server.getPlayer(any(UUID.class))).thenReturn(player);
|
||||
when(Bukkit.getServer()).thenReturn(server);
|
||||
// Has perms
|
||||
when(player.hasPermission(anyString())).thenReturn(true);
|
||||
// Fill the top ten
|
||||
TopTenData ttd = new TopTenData();
|
||||
ttd.setUniqueId("world");
|
||||
List<Object> topTen = new ArrayList<>();
|
||||
for (long i = -100; i < 100; i ++) {
|
||||
ttd.addLevel(UUID.randomUUID(), i);
|
||||
topTen.add(ttd);
|
||||
}
|
||||
when(handler.loadObjects()).thenReturn(topTen);
|
||||
|
||||
// Islands
|
||||
when(addon.getIslands()).thenReturn(im);
|
||||
// World
|
||||
when(world.getName()).thenReturn("world");
|
||||
// IWM
|
||||
when(plugin.getIWM()).thenReturn(iwm);
|
||||
|
||||
// User
|
||||
when(user.getTranslation(anyString())).thenAnswer((Answer<String>) invocation -> invocation.getArgument(0, String.class));
|
||||
when(user.getTranslation(eq("island.top.gui-heading"), eq("[name]"), anyString(), eq("[rank]"), anyString())).thenReturn("gui-heading");
|
||||
when(user.getTranslation(eq("island.top.island-level"),eq("[level]"), anyString())).thenReturn("island-level");
|
||||
when(user.getPlayer()).thenReturn(player);
|
||||
|
||||
// Player Manager
|
||||
when(addon.getPlayers()).thenReturn(pm);
|
||||
when(pm.getName(any())).thenReturn("player1",
|
||||
"player2",
|
||||
"player3",
|
||||
"player4",
|
||||
"player5",
|
||||
"player6",
|
||||
"player7",
|
||||
"player8",
|
||||
"player9",
|
||||
"player10"
|
||||
);
|
||||
// Mock item factory (for itemstacks)
|
||||
ItemFactory itemFactory = mock(ItemFactory.class);
|
||||
when(Bukkit.getItemFactory()).thenReturn(itemFactory);
|
||||
ItemMeta itemMeta = mock(ItemMeta.class);
|
||||
when(itemFactory.getItemMeta(any())).thenReturn(itemMeta);
|
||||
|
||||
// Inventory GUI
|
||||
when(Bukkit.createInventory(any(), anyInt(), anyString())).thenReturn(inv);
|
||||
|
||||
// Level presenter
|
||||
when(addon.getLevelPresenter()).thenReturn(lp);
|
||||
when(lp.getLevelString(anyLong())).thenAnswer((Answer<String>) invocation -> String.valueOf(invocation.getArgument(0, Long.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTopTen() {
|
||||
new TopTen(addon);
|
||||
PowerMockito.verifyStatic(Bukkit.class, times(200)); // 1
|
||||
Bukkit.getWorld(eq("world"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTopTenNullWorld() {
|
||||
when(Bukkit.getWorld(anyString())).thenReturn(null);
|
||||
new TopTen(addon);
|
||||
verify(addon, times(200)).logError("TopTen world world is not known on server. Skipping...");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddEntryNotOwner() throws Exception {
|
||||
// Database
|
||||
when(handler.loadObjects()).thenReturn(new ArrayList<>());
|
||||
TopTen tt = new TopTen(addon);
|
||||
UUID ownerUUID = UUID.randomUUID();
|
||||
tt.addEntry(world, ownerUUID, 200L);
|
||||
assertEquals(0, tt.getTopTenList(world).getTopTen().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddEntryIsOwner() throws Exception {
|
||||
when(im.isOwner(any(), any())).thenReturn(true);
|
||||
when(handler.loadObjects()).thenReturn(new ArrayList<>());
|
||||
TopTen tt = new TopTen(addon);
|
||||
UUID ownerUUID = UUID.randomUUID();
|
||||
tt.addEntry(world, ownerUUID, 200L);
|
||||
assertEquals(200L, (long) tt.getTopTenList(world).getTopTen().get(ownerUUID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddEntryIsOwnerNoPermission() throws Exception {
|
||||
when(player.hasPermission(anyString())).thenReturn(false);
|
||||
when(im.isOwner(any(), any())).thenReturn(true);
|
||||
when(handler.loadObjects()).thenReturn(new ArrayList<>());
|
||||
TopTen tt = new TopTen(addon);
|
||||
UUID ownerUUID = UUID.randomUUID();
|
||||
tt.addEntry(world, ownerUUID, 200L);
|
||||
assertNull(tt
|
||||
.getTopTenList(world)
|
||||
.getTopTen()
|
||||
.get(ownerUUID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGUIFullTopTen() {
|
||||
TopTen tt = new TopTen(addon);
|
||||
tt.getGUI(world, user, "bskyblock");
|
||||
verify(user).getTranslation(eq("island.top.gui-title"));
|
||||
verify(player).openInventory(inv);
|
||||
int[] SLOTS = new int[] {4, 12, 14, 19, 20, 21, 22, 23, 24, 25};
|
||||
for (int i : SLOTS) {
|
||||
verify(inv).setItem(eq(i), any());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGUINoPerms() {
|
||||
when(player.hasPermission(anyString())).thenReturn(false);
|
||||
TopTen tt = new TopTen(addon);
|
||||
tt.getGUI(world, user, "bskyblock");
|
||||
verify(user).getTranslation(eq("island.top.gui-title"));
|
||||
verify(player).openInventory(inv);
|
||||
int[] SLOTS = new int[] {4, 12, 14, 19, 20, 21, 22, 23, 24, 25};
|
||||
for (int i : SLOTS) {
|
||||
verify(inv, Mockito.never()).setItem(eq(i), any());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGUINoTopTen() throws Exception {
|
||||
when(handler.loadObjects()).thenReturn(new ArrayList<>());
|
||||
TopTen tt = new TopTen(addon);
|
||||
tt.getGUI(world, user, "bskyblock");
|
||||
verify(user).getTranslation(eq("island.top.gui-title"));
|
||||
verify(player).openInventory(inv);
|
||||
int[] SLOTS = new int[] {4, 12, 14, 19, 20, 21, 22, 23, 24, 25};
|
||||
for (int i : SLOTS) {
|
||||
verify(inv, Mockito.never()).setItem(eq(i), any());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTopTenList() {
|
||||
TopTen tt = new TopTen(addon);
|
||||
TopTenData ttdList = tt.getTopTenList(world);
|
||||
assertEquals(plugin, ttdList.getPlugin());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTopTenListNewWorld() {
|
||||
TopTen tt = new TopTen(addon);
|
||||
TopTenData ttdList = tt.getTopTenList(mock(World.class));
|
||||
assertEquals(plugin, ttdList.getPlugin());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveEntry() throws Exception {
|
||||
// Add entry
|
||||
when(im.isOwner(any(), any())).thenReturn(true);
|
||||
when(handler.loadObjects()).thenReturn(new ArrayList<>());
|
||||
TopTen tt = new TopTen(addon);
|
||||
UUID ownerUUID = UUID.randomUUID();
|
||||
tt.addEntry(world, ownerUUID, 200L);
|
||||
assertEquals(200L, (long) tt.getTopTenList(world).getTopTen().get(ownerUUID));
|
||||
// Remove it
|
||||
tt.removeEntry(world, ownerUUID);
|
||||
assertNull(tt.getTopTenList(world).getTopTen().get(ownerUUID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSaveTopTen() throws Exception {
|
||||
TopTen tt = new TopTen(addon);
|
||||
tt.saveTopTen();
|
||||
verify(handler).saveObject(any());
|
||||
}
|
||||
|
||||
}
|
@ -36,7 +36,8 @@ import world.bentobox.bentobox.managers.IslandsManager;
|
||||
import world.bentobox.bentobox.managers.LocalesManager;
|
||||
import world.bentobox.bentobox.managers.PlayersManager;
|
||||
import world.bentobox.level.Level;
|
||||
import world.bentobox.level.TopTen;
|
||||
import world.bentobox.level.LevelsManager;
|
||||
import world.bentobox.level.commands.AdminTopRemoveCommand;
|
||||
import world.bentobox.level.objects.TopTenData;
|
||||
|
||||
/**
|
||||
@ -73,9 +74,9 @@ public class AdminTopRemoveCommandTest {
|
||||
|
||||
private AdminTopRemoveCommand atrc;
|
||||
@Mock
|
||||
private TopTen tt;
|
||||
@Mock
|
||||
private TopTenData ttd;
|
||||
@Mock
|
||||
private LevelsManager manager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@ -105,8 +106,7 @@ public class AdminTopRemoveCommandTest {
|
||||
when(plugin.getPlayers()).thenReturn(pm);
|
||||
when(pm.getUser(anyString())).thenReturn(user);
|
||||
// topTen
|
||||
when(addon.getTopTen()).thenReturn(tt);
|
||||
when(tt.getTopTenList(any())).thenReturn(ttd);
|
||||
when(addon.getManager()).thenReturn(manager);
|
||||
// User
|
||||
uuid = UUID.randomUUID();
|
||||
when(user.getUniqueId()).thenReturn(uuid);
|
||||
@ -175,7 +175,7 @@ public class AdminTopRemoveCommandTest {
|
||||
public void testExecuteUserStringListOfString() {
|
||||
testCanExecuteKnown();
|
||||
assertTrue(atrc.execute(user, "delete", Collections.singletonList("tastybento")));
|
||||
verify(ttd).remove(eq(uuid));
|
||||
verify(manager).removeEntry(any(World.class), eq(uuid));
|
||||
verify(user).sendMessage(eq("general.success"));
|
||||
}
|
||||
|
||||
|
@ -1,124 +0,0 @@
|
||||
package world.bentobox.level.objects;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
public class TopTenDataTest {
|
||||
|
||||
private final Map<UUID, Long> topTen = new LinkedHashMap<>();
|
||||
private TopTenData ttd;
|
||||
private final UUID uuid = UUID.randomUUID();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// Create a top ten map
|
||||
for (long i = 0; i < 100; i++) {
|
||||
topTen.put(UUID.randomUUID(), i);
|
||||
}
|
||||
// Add the top player
|
||||
topTen.put(uuid, 100L);
|
||||
// Add negative values
|
||||
for (long i = 0; i < 100; i++) {
|
||||
topTen.put(UUID.randomUUID(), - i);
|
||||
}
|
||||
ttd = new TopTenData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.objects.TopTenData#getTopTen()}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTopTen() {
|
||||
assertTrue(ttd.getTopTen().isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.objects.TopTenData#setTopTen(java.util.Map)}.
|
||||
*/
|
||||
@Test
|
||||
public void testSetAndGetTopTen() {
|
||||
ttd.setTopTen(topTen);
|
||||
// Ten only
|
||||
assertEquals(10, ttd.getTopTen().size());
|
||||
// Check order
|
||||
long i = 100;
|
||||
for (long l : ttd.getTopTen().values()) {
|
||||
|
||||
assertEquals(i--, l);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.objects.TopTenData#setTopTen(java.util.Map)}.
|
||||
*/
|
||||
@Test
|
||||
public void testSetAndGetTopTenShort() {
|
||||
Map<UUID, Long> topTen2 = new LinkedHashMap<>();
|
||||
// Create a top ten map
|
||||
for (long i = 0; i < 3; i++) {
|
||||
topTen2.put(UUID.randomUUID(), i);
|
||||
}
|
||||
// Add the top player
|
||||
topTen2.put(uuid, 3L);
|
||||
ttd.setTopTen(topTen2);
|
||||
// Three only
|
||||
assertEquals(3, ttd.getTopTen().size());
|
||||
// Check order
|
||||
long i = 3;
|
||||
for (long l : ttd.getTopTen().values()) {
|
||||
System.out.println(l);
|
||||
assertEquals(i--, l);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.objects.TopTenData#getUniqueId()}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetUniqueId() {
|
||||
assertTrue(ttd.getUniqueId().isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.objects.TopTenData#setUniqueId(java.lang.String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testSetUniqueId() {
|
||||
ttd.setUniqueId("unique");
|
||||
assertEquals("unique", ttd.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.objects.TopTenData#addLevel(java.util.UUID, java.lang.Long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testAddAndGetLevel() {
|
||||
topTen.forEach(ttd::addLevel);
|
||||
topTen.keySet().forEach(k -> assertEquals((long) topTen.get(k), ttd.getLevel(k)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.level.objects.TopTenData#remove(java.util.UUID)}.
|
||||
*/
|
||||
@Test
|
||||
public void testRemove() {
|
||||
ttd.remove(uuid);
|
||||
// Check order
|
||||
long i = 99;
|
||||
for (long l : ttd.getTopTen().values()) {
|
||||
assertEquals(i--, l);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user