Island levels (#178)

* Stores level data on a per island basis

* Migrate after BentoBox worlds have loaded.

* Added new Admin set initial level handicap command

* Bug fixing

* Fix test

* Removed code smell
This commit is contained in:
tastybento 2020-07-26 14:40:17 -07:00 committed by GitHub
parent 507cefd128
commit dab0e84bc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 383 additions and 98 deletions

View File

@ -65,7 +65,7 @@
<!-- Do not change unless you want different name for local builds. --> <!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number> <build.number>-LOCAL</build.number>
<!-- This allows to change between versions. --> <!-- This allows to change between versions. -->
<build.version>2.3.4</build.version> <build.version>2.4.0</build.version>
</properties> </properties>
<!-- Profiles will allow to automatically change build version. --> <!-- Profiles will allow to automatically change build version. -->

View File

@ -22,6 +22,7 @@ import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.level.calculators.Pipeliner; import world.bentobox.level.calculators.Pipeliner;
import world.bentobox.level.commands.AdminLevelCommand; import world.bentobox.level.commands.AdminLevelCommand;
import world.bentobox.level.commands.AdminLevelStatusCommand; import world.bentobox.level.commands.AdminLevelStatusCommand;
import world.bentobox.level.commands.AdminSetInitialLevelCommand;
import world.bentobox.level.commands.AdminTopCommand; import world.bentobox.level.commands.AdminTopCommand;
import world.bentobox.level.commands.IslandLevelCommand; import world.bentobox.level.commands.IslandLevelCommand;
import world.bentobox.level.commands.IslandTopCommand; import world.bentobox.level.commands.IslandTopCommand;
@ -30,7 +31,6 @@ import world.bentobox.level.config.BlockConfig;
import world.bentobox.level.config.ConfigSettings; import world.bentobox.level.config.ConfigSettings;
import world.bentobox.level.listeners.IslandActivitiesListeners; import world.bentobox.level.listeners.IslandActivitiesListeners;
import world.bentobox.level.listeners.JoinLeaveListener; import world.bentobox.level.listeners.JoinLeaveListener;
import world.bentobox.level.objects.LevelsData;
import world.bentobox.level.requests.LevelRequestHandler; import world.bentobox.level.requests.LevelRequestHandler;
import world.bentobox.level.requests.TopTenRequestHandler; import world.bentobox.level.requests.TopTenRequestHandler;
@ -99,6 +99,9 @@ public class Level extends Addon implements Listener {
@EventHandler @EventHandler
public void onBentoBoxReady(BentoBoxReadyEvent e) { public void onBentoBoxReady(BentoBoxReadyEvent e) {
// Perform upgrade check
manager.migrate();
// Load TopTens
manager.loadTopTens(); manager.loadTopTens();
/* /*
* DEBUG code to generate fake islands and then try to level them all. * DEBUG code to generate fake islands and then try to level them all.
@ -187,6 +190,7 @@ public class Level extends Addon implements Listener {
new AdminLevelCommand(this, adminCommand); new AdminLevelCommand(this, adminCommand);
new AdminTopCommand(this, adminCommand); new AdminTopCommand(this, adminCommand);
new AdminLevelStatusCommand(this, adminCommand); new AdminLevelStatusCommand(this, adminCommand);
new AdminSetInitialLevelCommand(this, adminCommand);
}); });
gm.getPlayerCommand().ifPresent(playerCmd -> { gm.getPlayerCommand().ifPresent(playerCmd -> {
new IslandLevelCommand(this, playerCmd); new IslandLevelCommand(this, playerCmd);
@ -319,12 +323,4 @@ public class Level extends Addon implements Listener {
if (island != null) getManager().calculateLevel(playerUUID, island); if (island != null) getManager().calculateLevel(playerUUID, island);
} }
/**
* Load a player from the cache or database
* @param targetPlayer - UUID of target player
* @return LevelsData object or null if not found
*/
public LevelsData getLevelsData(UUID targetPlayer) {
return getManager().getLevelsData(targetPlayer);
}
} }

View File

@ -9,6 +9,7 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Objects;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -21,6 +22,8 @@ import org.bukkit.World;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import com.google.common.collect.Maps;
import world.bentobox.bentobox.api.events.addon.AddonBaseEvent; import world.bentobox.bentobox.api.events.addon.AddonBaseEvent;
import world.bentobox.bentobox.api.events.addon.AddonEvent; import world.bentobox.bentobox.api.events.addon.AddonEvent;
import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.PanelItem;
@ -33,6 +36,7 @@ import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.level.calculators.Results; import world.bentobox.level.calculators.Results;
import world.bentobox.level.events.IslandLevelCalculatedEvent; import world.bentobox.level.events.IslandLevelCalculatedEvent;
import world.bentobox.level.events.IslandPreLevelEvent; import world.bentobox.level.events.IslandPreLevelEvent;
import world.bentobox.level.objects.IslandLevels;
import world.bentobox.level.objects.LevelsData; import world.bentobox.level.objects.LevelsData;
import world.bentobox.level.objects.TopTenData; import world.bentobox.level.objects.TopTenData;
import world.bentobox.level.panels.DetailsGUITab; import world.bentobox.level.panels.DetailsGUITab;
@ -55,9 +59,9 @@ public class LevelsManager {
// Database handler for level data // Database handler for level data
private final Database<LevelsData> handler; private final Database<IslandLevels> handler;
// A cache of island levels. // A cache of island levels.
private final Map<UUID, LevelsData> levelsCache; private final Map<String, IslandLevels> levelsCache;
private final Database<TopTenData> topTenHandler; private final Database<TopTenData> topTenHandler;
// Top ten lists // Top ten lists
@ -72,7 +76,7 @@ public class LevelsManager {
// Get the BentoBox database // Get the BentoBox database
// Set up the database handler to store and retrieve data // Set up the database handler to store and retrieve data
// Note that these are saved by the BentoBox database // Note that these are saved by the BentoBox database
handler = new Database<>(addon, LevelsData.class); handler = new Database<>(addon, IslandLevels.class);
// Top Ten handler // Top Ten handler
topTenHandler = new Database<>(addon, TopTenData.class); topTenHandler = new Database<>(addon, TopTenData.class);
// Initialize the cache // Initialize the cache
@ -83,6 +87,39 @@ public class LevelsManager {
background = new PanelItemBuilder().icon(Material.BLACK_STAINED_GLASS_PANE).name(" ").build(); background = new PanelItemBuilder().icon(Material.BLACK_STAINED_GLASS_PANE).name(" ").build();
} }
public void migrate() {
Database<LevelsData> oldDb = new Database<>(addon, LevelsData.class);
oldDb.loadObjects().forEach(ld -> {
try {
UUID owner = UUID.fromString(ld.getUniqueId());
// Step through each world
ld.getLevels().keySet().stream()
// World
.map(Bukkit::getWorld).filter(Objects::nonNull)
// Island
.map(w -> addon.getIslands().getIsland(w, owner)).filter(Objects::nonNull)
.forEach(i -> {
// Make new database entry
World w = i.getWorld();
IslandLevels il = new IslandLevels(i.getUniqueId());
il.setInitialLevel(ld.getInitialLevel(w));
il.setLevel(ld.getLevel(w));
il.setMdCount(ld.getMdCount(w));
il.setPointsToNextLevel(ld.getPointsToNextLevel(w));
il.setUwCount(ld.getUwCount(w));
// Save it
handler.saveObjectAsync(il);
});
// Now delete the old database entry
oldDb.deleteID(ld.getUniqueId());
} catch (Exception e) {
addon.logError("Could not migrate level data database! " + e.getMessage());
e.printStackTrace();
return;
}
});
}
/** /**
* Add a score to the top players list * Add a score to the top players list
* @param world - world * @param world - world
@ -286,9 +323,7 @@ public class LevelsManager {
* @return initial level of island * @return initial level of island
*/ */
public long getInitialLevel(Island island) { public long getInitialLevel(Island island) {
@Nullable return getLevelsData(island).getInitialLevel();
LevelsData ld = getLevelsData(island.getOwner());
return ld == null ? 0 : ld.getInitialLevel(island.getWorld());
} }
/** /**
@ -299,11 +334,9 @@ public class LevelsManager {
*/ */
public long getIslandLevel(@NonNull World world, @Nullable UUID targetPlayer) { public long getIslandLevel(@NonNull World world, @Nullable UUID targetPlayer) {
if (targetPlayer == null) return 0L; if (targetPlayer == null) return 0L;
// Get the island owner // Get the island
UUID owner = addon.getIslands().getOwner(world, targetPlayer); Island island = addon.getIslands().getIsland(world, targetPlayer);
if (owner == null) return 0L; return island == null ? 0L : getLevelsData(island).getLevel();
LevelsData ld = getLevelsData(owner);
return ld == null ? 0L : ld.getLevel(world);
} }
/** /**
@ -317,23 +350,30 @@ public class LevelsManager {
} }
/** /**
* Load a level data for the island owner from the cache or database. Only island owners are stored. * Load a level data for the island from the cache or database.
* @param islandOwner - UUID of island owner * @param island - UUID of island
* @return LevelsData object or null if not found * @return IslandLevels object
*/ */
@Nullable @NonNull
public LevelsData getLevelsData(@NonNull UUID islandOwner) { public IslandLevels getLevelsData(@NonNull Island island) {
String id = island.getUniqueId();
if (levelsCache.containsKey(id)) {
return levelsCache.get(id);
}
// Get from database if not in cache // Get from database if not in cache
if (!levelsCache.containsKey(islandOwner) && handler.objectExists(islandOwner.toString())) { if (handler.objectExists(id)) {
LevelsData ld = handler.loadObject(islandOwner.toString()); IslandLevels ld = handler.loadObject(id);
if (ld != null) { if (ld != null) {
levelsCache.put(islandOwner, ld); levelsCache.put(id, ld);
} else { } else {
handler.deleteID(islandOwner.toString()); handler.deleteID(id);
levelsCache.put(id, new IslandLevels(id));
} }
} else {
levelsCache.put(id, new IslandLevels(id));
} }
// Return cached value or null // Return cached value
return levelsCache.get(islandOwner); return levelsCache.get(id);
} }
/** /**
@ -344,10 +384,8 @@ public class LevelsManager {
*/ */
public String getPointsToNextString(@NonNull World world, @Nullable UUID targetPlayer) { public String getPointsToNextString(@NonNull World world, @Nullable UUID targetPlayer) {
if (targetPlayer == null) return ""; if (targetPlayer == null) return "";
UUID owner = addon.getIslands().getOwner(world, targetPlayer); Island island = addon.getIslands().getIsland(world, targetPlayer);
if (owner == null) return ""; return island == null ? "" : String.valueOf(getLevelsData(island).getPointsToNextLevel());
LevelsData ld = getLevelsData(owner);
return ld == null ? "" : String.valueOf(ld.getPointsToNextLevel(world));
} }
/** /**
@ -394,9 +432,6 @@ public class LevelsManager {
// Update based on user data // Update based on user data
// Remove any non island owners // Remove any non island owners
tt.getTopTen().keySet().removeIf(u -> !addon.getIslands().isOwner(world, u)); tt.getTopTen().keySet().removeIf(u -> !addon.getIslands().isOwner(world, u));
for (UUID uuid : tt.getTopTen().keySet()) {
tt.getTopTen().compute(uuid, (k,v) -> v = updateLevel(k, world));
}
} else { } else {
addon.logError("TopTen world '" + tt.getUniqueId() + "' is not known on server. You might want to delete this table. Skipping..."); addon.logError("TopTen world '" + tt.getUniqueId() + "' is not known on server. You might want to delete this table. Skipping...");
} }
@ -409,14 +444,6 @@ public class LevelsManager {
* @param uuid - the player's uuid * @param uuid - the player's uuid
*/ */
public void removeEntry(World world, UUID uuid) { public void removeEntry(World world, UUID uuid) {
// Load the user if they haven't yet done anything to put them in the cache
this.getLevelsData(uuid);
// Remove them
if (levelsCache.containsKey(uuid)) {
levelsCache.get(uuid).remove(world);
// Save
handler.saveObjectAsync(levelsCache.get(uuid));
}
if (topTenLists.containsKey(world)) { if (topTenLists.containsKey(world)) {
topTenLists.get(world).getTopTen().remove(uuid); topTenLists.get(world).getTopTen().remove(uuid);
topTenHandler.saveObjectAsync(topTenLists.get(world)); topTenHandler.saveObjectAsync(topTenLists.get(world));
@ -441,14 +468,14 @@ public class LevelsManager {
} }
/** /**
* Set an initial island level for player * Set an initial island level
* @param island - the island to set. Must have a non-null world and owner * @param island - the island to set. Must have a non-null world
* @param lv - initial island level * @param lv - initial island level
*/ */
public void setInitialIslandLevel(@NonNull Island island, long lv) { public void setInitialIslandLevel(@NonNull Island island, long lv) {
if (island.getOwner() == null || island.getWorld() == null) return; if (island.getWorld() == null) return;
levelsCache.computeIfAbsent(island.getOwner(), LevelsData::new).setInitialLevel(island.getWorld(), lv); levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new).setInitialLevel(lv);
handler.saveObjectAsync(levelsCache.get(island.getOwner())); handler.saveObjectAsync(levelsCache.get(island.getUniqueId()));
} }
/** /**
@ -458,10 +485,18 @@ public class LevelsManager {
* @param lv - level * @param lv - level
*/ */
public void setIslandLevel(@NonNull World world, @NonNull UUID targetPlayer, long lv) { public void setIslandLevel(@NonNull World world, @NonNull UUID targetPlayer, long lv) {
levelsCache.computeIfAbsent(targetPlayer, LevelsData::new).setLevel(world, lv); // Get the island
handler.saveObjectAsync(levelsCache.get(targetPlayer)); Island island = addon.getIslands().getIsland(world, targetPlayer);
if (island != null) {
String id = island.getUniqueId();
IslandLevels il = levelsCache.computeIfAbsent(id, IslandLevels::new);
// Remove the initial level
il.setLevel(lv - il.getInitialLevel());
handler.saveObjectAsync(levelsCache.get(id));
// Update TopTen // Update TopTen
addToTopTen(world, targetPlayer, levelsCache.get(targetPlayer).getLevel(world)); addToTopTen(world, targetPlayer, levelsCache.get(id).getLevel());
}
} }
/** /**
@ -471,26 +506,27 @@ public class LevelsManager {
* @param r - results of the calculation * @param r - results of the calculation
*/ */
private void setIslandResults(World world, @NonNull UUID owner, Results r) { private void setIslandResults(World world, @NonNull UUID owner, Results r) {
LevelsData ld = levelsCache.computeIfAbsent(owner, LevelsData::new); // Get the island
ld.setLevel(world, r.getLevel()); Island island = addon.getIslands().getIsland(world, owner);
ld.setUwCount(world, r.getUwCount()); if (island == null) return;
ld.setMdCount(world, r.getMdCount()); IslandLevels ld = levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new);
ld.setPointsToNextLevel(world, r.getPointsToNextLevel()); ld.setLevel(r.getLevel() - ld.getInitialLevel());
levelsCache.put(owner, ld); ld.setUwCount(Maps.asMap(r.getUwCount().elementSet(), elem -> r.getUwCount().count(elem)));
ld.setMdCount(Maps.asMap(r.getMdCount().elementSet(), elem -> r.getMdCount().count(elem)));
ld.setPointsToNextLevel(r.getPointsToNextLevel());
levelsCache.put(island.getUniqueId(), ld);
handler.saveObjectAsync(ld); handler.saveObjectAsync(ld);
// Update TopTen // Update TopTen
addToTopTen(world, owner, ld.getLevel(world)); addToTopTen(world, owner, ld.getLevel());
} }
private Long updateLevel(UUID uuid, World world) { /**
if (handler.objectExists(uuid.toString())) { * Removes island from cache when it is deleted
@Nullable * @param uniqueId - id of island
LevelsData ld = handler.loadObject(uuid.toString()); */
if (ld != null) { public void deleteIsland(String uniqueId) {
return ld.getLevel(world); levelsCache.remove(uniqueId);
} handler.deleteID(uniqueId);
}
return 0L;
} }
} }

View File

@ -0,0 +1,80 @@
package world.bentobox.level.commands;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.eclipse.jdt.annotation.Nullable;
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.bentobox.util.Util;
import world.bentobox.level.Level;
public class AdminSetInitialLevelCommand extends CompositeCommand {
private @Nullable UUID targetUUID;
private @Nullable Island island;
private Level addon;
public AdminSetInitialLevelCommand(Level addon, CompositeCommand parent) {
super(parent, "sethandicap");
this.addon = addon;
}
@Override
public void setup() {
this.setPermission("admin.level.sethandicap");
this.setOnlyPlayer(false);
this.setParametersHelp("admin.level.sethandicap.parameters");
this.setDescription("admin.level.sethandicap.description");
}
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
if (args.isEmpty()) {
// Don't show every player on the server. Require at least the first letter
return Optional.empty();
}
List<String> options = new ArrayList<>(Util.getOnlinePlayerList(user));
return Optional.of(Util.tabLimit(options, lastArg));
}
@Override
public boolean execute(User user, String label, List<String> args) {
String initialLevel = String.valueOf(addon.getManager().getInitialLevel(island));
long lv = Long.parseLong(args.get(1));
addon.getManager().setInitialIslandLevel(island, lv);
user.sendMessage("admin.level.sethandicap.changed", TextVariables.NUMBER, initialLevel, "[new_number]", String.valueOf(lv));
return true;
}
@Override
public boolean canExecute(User user, String label, List<String> args) {
if (args.size() != 2) {
showHelp(this, user);
return false;
}
targetUUID = getAddon().getPlayers().getUUID(args.get(0));
if (targetUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return false;
}
// Check value
if (!Util.isInteger(args.get(1), true)) {
user.sendMessage("admin.level.sethandicap.invalid-level");
return false;
}
// Check island
island = getAddon().getIslands().getIsland(getWorld(), targetUUID);
if (island == null) {
user.sendMessage("general.errors.player-has-no-island");
return false;
}
return true;
}
}

View File

@ -38,7 +38,7 @@ public class IslandLevelCommand extends CompositeCommand {
final UUID playerUUID = getPlugin().getPlayers().getUUID(args.get(0)); final UUID playerUUID = getPlugin().getPlayers().getUUID(args.get(0));
if (playerUUID == null) { if (playerUUID == null) {
user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0));
return true; return false;
} }
// Ops, console and admin perms can request and calculate other player levels // Ops, console and admin perms can request and calculate other player levels
if (!user.isPlayer() || user.isOp() || user.hasPermission(this.getPermissionPrefix() + "admin.level")) { if (!user.isPlayer() || user.isOp() || user.hasPermission(this.getPermissionPrefix() + "admin.level")) {

View File

@ -8,6 +8,7 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import world.bentobox.bentobox.api.events.island.IslandEvent.IslandCreatedEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.IslandCreatedEvent;
import world.bentobox.bentobox.api.events.island.IslandEvent.IslandDeleteEvent;
import world.bentobox.bentobox.api.events.island.IslandEvent.IslandPreclearEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.IslandPreclearEvent;
import world.bentobox.bentobox.api.events.island.IslandEvent.IslandRegisteredEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.IslandRegisteredEvent;
import world.bentobox.bentobox.api.events.island.IslandEvent.IslandResettedEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.IslandResettedEvent;
@ -65,6 +66,12 @@ public class IslandActivitiesListeners implements Listener {
remove(world, uuid); remove(world, uuid);
} }
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onIslandDeleted(IslandDeleteEvent e) {
// Remove island
addon.getManager().deleteIsland(e.getIsland().getUniqueId());
}
private void remove(World world, UUID uuid) { private void remove(World world, UUID uuid) {
if (uuid != null && world != null) { if (uuid != null && world != null) {
addon.getManager().removeEntry(world, uuid); addon.getManager().removeEntry(world, uuid);
@ -88,14 +95,14 @@ public class IslandActivitiesListeners implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onIsland(IslandUnregisteredEvent e) { public void onIsland(IslandUnregisteredEvent e) {
// Remove player from the top ten and level // Remove player from the top ten
remove(e.getIsland().getWorld(), e.getPlayerUUID()); remove(e.getIsland().getWorld(), e.getPlayerUUID());
} }
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onIsland(IslandRegisteredEvent e) { public void onIsland(IslandRegisteredEvent e) {
// Remove player from the top ten and level // Remove player from the top ten
remove(e.getIsland().getWorld(), e.getPlayerUUID()); remove(e.getIsland().getWorld(), e.getPlayerUUID());
} }

View File

@ -27,8 +27,6 @@ public class JoinLeaveListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent e) { public void onPlayerJoin(PlayerJoinEvent e) {
// Load player into cache
addon.getManager().getLevelsData(e.getPlayer().getUniqueId());
// If level calc on login is enabled, run through all the worlds and calculate the level // If level calc on login is enabled, run through all the worlds and calculate the level
if (addon.getSettings().isCalcOnLogin()) { if (addon.getSettings().isCalcOnLogin()) {
addon.getPlugin().getAddonsManager().getGameModeAddons().stream() addon.getPlugin().getAddonsManager().getGameModeAddons().stream()

View File

@ -0,0 +1,154 @@
package world.bentobox.level.objects;
import java.util.EnumMap;
import java.util.Map;
import org.bukkit.Material;
import com.google.gson.annotations.Expose;
import world.bentobox.bentobox.database.objects.DataObject;
import world.bentobox.bentobox.database.objects.Table;
/**
* Stores the levels data of the island.
* A note - if this class is extended to support new exposed fields and legacy data doesn't include those fields
* they will be set to null by GSON. They will not be initialized and if any attempt is made to use them, then
* the JVM will give up WITHOUT AN ERROR!!! That is why there are null checks throughout this class.
*
* @author tastybento
*
*/
@Table(name = "IslandLevels")
public class IslandLevels implements DataObject {
// uniqueId is the island's UUID
@Expose
private String uniqueId = "";
/**
* Island level
*/
@Expose
private long level;
/**
* Initial level
*/
@Expose
private long initialLevel;
/**
* Points to next level
*/
@Expose
private long pointsToNextLevel;
/**
* Underwater count
*/
@Expose
private Map<Material, Integer> uwCount;
/**
* MaterialData count - count of all blocks
*/
@Expose
private Map<Material, Integer> mdCount;
/**
* Constructor for new island
* @param islandUUID - island UUID
*/
public IslandLevels(String islandUUID) {
uniqueId = islandUUID;
uwCount = new EnumMap<>(Material.class);
mdCount = new EnumMap<>(Material.class);
}
/**
* @return the uniqueId
*/
@Override
public String getUniqueId() {
return uniqueId;
}
/**
* @param uniqueId the uniqueId to set
*/
@Override
public void setUniqueId(String uniqueId) {
this.uniqueId = uniqueId;
}
/**
* @return the level
*/
public long getLevel() {
return level;
}
/**
* @param level the level to set
*/
public void setLevel(long level) {
this.level = level;
}
/**
* @return the initialLevel
*/
public long getInitialLevel() {
return initialLevel;
}
/**
* @param initialLevel the initialLevel to set
*/
public void setInitialLevel(long initialLevel) {
this.initialLevel = initialLevel;
}
/**
* @return the pointsToNextLevel
*/
public long getPointsToNextLevel() {
return pointsToNextLevel;
}
/**
* @param pointsToNextLevel the pointsToNextLevel to set
*/
public void setPointsToNextLevel(long pointsToNextLevel) {
this.pointsToNextLevel = pointsToNextLevel;
}
/**
* @return the uwCount
*/
public Map<Material, Integer> getUwCount() {
return uwCount;
}
/**
* @param uwCount the uwCount to set
*/
public void setUwCount(Map<Material, Integer> uwCount) {
this.uwCount = uwCount;
}
/**
* @return the mdCount
*/
public Map<Material, Integer> getMdCount() {
return mdCount;
}
/**
* @param mdCount the mdCount to set
*/
public void setMdCount(Map<Material, Integer> mdCount) {
this.mdCount = mdCount;
}
}

View File

@ -27,7 +27,7 @@ import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.Util;
import world.bentobox.level.Level; import world.bentobox.level.Level;
import world.bentobox.level.objects.LevelsData; import world.bentobox.level.objects.IslandLevels;
/** /**
* @author tastybento * @author tastybento
@ -131,20 +131,20 @@ public class DetailsGUITab implements Tab, ClickHandler {
private void generateReport(DetailsType type) { private void generateReport(DetailsType type) {
items = new ArrayList<>(); items = new ArrayList<>();
LevelsData ld = addon.getManager().getLevelsData(island.getOwner()); IslandLevels ld = addon.getManager().getLevelsData(island);
// Get the items from the report // Get the items from the report
Map<Material, Integer> sumTotal = new EnumMap<>(Material.class); Map<Material, Integer> sumTotal = new EnumMap<>(Material.class);
sumTotal.putAll(ld.getMdCount(world)); sumTotal.putAll(ld.getMdCount());
sumTotal.putAll(ld.getUwCount(world)); sumTotal.putAll(ld.getUwCount());
switch(type) { switch(type) {
case ABOVE_SEA_LEVEL_BLOCKS: case ABOVE_SEA_LEVEL_BLOCKS:
ld.getMdCount(world).forEach(this::createItem); ld.getMdCount().forEach(this::createItem);
break; break;
case SPAWNERS: case SPAWNERS:
sumTotal.entrySet().stream().filter(m -> m.getKey().equals(Material.SPAWNER)).forEach(e -> createItem(e.getKey(), e.getValue())); sumTotal.entrySet().stream().filter(m -> m.getKey().equals(Material.SPAWNER)).forEach(e -> createItem(e.getKey(), e.getValue()));
break; break;
case UNDERWATER_BLOCKS: case UNDERWATER_BLOCKS:
ld.getUwCount(world).forEach(this::createItem); ld.getUwCount().forEach(this::createItem);
break; break;
default: default:
sumTotal.forEach(this::createItem); sumTotal.forEach(this::createItem);

View File

@ -7,6 +7,11 @@ admin:
level: level:
parameters: "<player>" parameters: "<player>"
description: "calculate the island level for player" description: "calculate the island level for player"
sethandicap:
parameters: <player> <handicap>
description: "set the island handicap, usually the level of the starter island"
changed: "&a Initial island handicap changed from [number] to [new_number]."
invalid-level: "&c Invalid handicap. Use an integer."
levelstatus: levelstatus:
description: "show how many islands are in the queue for scanning" description: "show how many islands are in the queue for scanning"
islands-in-queue: "&a Islands in queue: [number]" islands-in-queue: "&a Islands in queue: [number]"
@ -18,6 +23,7 @@ admin:
description: "remove player from Top Ten" description: "remove player from Top Ten"
parameters: "<player>" parameters: "<player>"
island: island:
level: level:
parameters: "[player]" parameters: "[player]"

View File

@ -275,8 +275,8 @@ public class LevelTest {
addon.onEnable(); addon.onEnable();
verify(plugin).logWarning("[Level] Level Addon: No such world in blockconfig.yml : acidisland_world"); verify(plugin).logWarning("[Level] Level Addon: No such world in blockconfig.yml : acidisland_world");
verify(plugin).log("[Level] Level hooking into BSkyBlock"); verify(plugin).log("[Level] Level hooking into BSkyBlock");
verify(cmd, times(3)).getAddon(); // Three commands verify(cmd, times(3)).getAddon(); // 3 commands
verify(adminCmd, times(3)).getAddon(); // Three commands verify(adminCmd, times(4)).getAddon(); // Four commands
// Placeholders // Placeholders
verify(phm).registerPlaceholder(eq(addon), eq("bskyblock_island_level"), any()); 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_visited_island_level"), any());

View File

@ -16,6 +16,7 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -58,7 +59,7 @@ import world.bentobox.bentobox.managers.PlayersManager;
import world.bentobox.level.calculators.Pipeliner; import world.bentobox.level.calculators.Pipeliner;
import world.bentobox.level.calculators.Results; import world.bentobox.level.calculators.Results;
import world.bentobox.level.config.ConfigSettings; import world.bentobox.level.config.ConfigSettings;
import world.bentobox.level.objects.LevelsData; import world.bentobox.level.objects.IslandLevels;
import world.bentobox.level.objects.TopTenData; import world.bentobox.level.objects.TopTenData;
/** /**
@ -104,7 +105,7 @@ public class LevelsManagerTest {
@Mock @Mock
private PluginManager pim; private PluginManager pim;
@Mock @Mock
private LevelsData levelsData; private IslandLevels levelsData;
@Mock @Mock
private IslandsManager im; private IslandsManager im;
@ -125,6 +126,7 @@ public class LevelsManagerTest {
/** /**
* @throws java.lang.Exception * @throws java.lang.Exception
*/ */
@SuppressWarnings("unchecked")
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
when(addon.getPlugin()).thenReturn(plugin); when(addon.getPlugin()).thenReturn(plugin);
@ -154,9 +156,11 @@ public class LevelsManagerTest {
when(island.getMemberSet()).thenReturn(iset); when(island.getMemberSet()).thenReturn(iset);
when(island.getOwner()).thenReturn(uuid); when(island.getOwner()).thenReturn(uuid);
when(island.getWorld()).thenReturn(world); when(island.getWorld()).thenReturn(world);
when(island.getUniqueId()).thenReturn(UUID.randomUUID().toString());
// Default to uuid's being island owners // Default to uuid's being island owners
when(im.isOwner(eq(world), any())).thenReturn(true); when(im.isOwner(eq(world), any())).thenReturn(true);
when(im.getOwner(any(), any(UUID.class))).thenAnswer(in -> in.getArgument(1, UUID.class)); when(im.getOwner(any(), any(UUID.class))).thenAnswer(in -> in.getArgument(1, UUID.class));
when(im.getIsland(eq(world), eq(uuid))).thenReturn(island);
// Player // Player
when(player.getUniqueId()).thenReturn(uuid); when(player.getUniqueId()).thenReturn(uuid);
@ -205,9 +209,10 @@ public class LevelsManagerTest {
// Include a known UUID // Include a known UUID
ttd.getTopTen().put(uuid, 456789L); ttd.getTopTen().put(uuid, 456789L);
topTen.add(ttd); topTen.add(ttd);
when(handler.loadObjects()).thenReturn(topTen); // Supply no island levels first, then topTen
when(handler.loadObjects()).thenReturn(Collections.emptyList(), topTen);
when(handler.objectExists(anyString())).thenReturn(true); 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.getLevel()).thenReturn(-5L, -4L, -3L, -2L, -1L, 0L, 1L, 2L, 3L, 4L, 5L, 45678L);
when(levelsData.getUniqueId()).thenReturn(uuid.toString()); when(levelsData.getUniqueId()).thenReturn(uuid.toString());
when(handler.loadObject(anyString())).thenReturn(levelsData ); when(handler.loadObject(anyString())).thenReturn(levelsData );
@ -220,6 +225,7 @@ public class LevelsManagerTest {
when(iwm.getPermissionPrefix(any())).thenReturn("bskyblock."); when(iwm.getPermissionPrefix(any())).thenReturn("bskyblock.");
lm = new LevelsManager(addon); lm = new LevelsManager(addon);
lm.migrate();
} }
/** /**
@ -252,7 +258,7 @@ public class LevelsManagerTest {
lm.calculateLevel(uuid, island); lm.calculateLevel(uuid, island);
cf.complete(results); cf.complete(results);
assertEquals(Long.valueOf(10000), lm.getLevelsData(uuid).getLevel(world)); assertEquals(10000L, lm.getLevelsData(island).getLevel());
//Map<UUID, Long> tt = lm.getTopTen(world, 10); //Map<UUID, Long> tt = lm.getTopTen(world, 10);
//assertEquals(1, tt.size()); //assertEquals(1, tt.size());
//assertTrue(tt.get(uuid) == 10000); //assertTrue(tt.get(uuid) == 10000);
@ -280,7 +286,9 @@ public class LevelsManagerTest {
*/ */
@Test @Test
public void testGetPointsToNextString() { public void testGetPointsToNextString() {
assertEquals("0", lm.getPointsToNextString(world, UUID.randomUUID())); // No island player
assertEquals("", lm.getPointsToNextString(world, UUID.randomUUID()));
// Player has island
assertEquals("0", lm.getPointsToNextString(world, uuid)); assertEquals("0", lm.getPointsToNextString(world, uuid));
} }
@ -297,7 +305,7 @@ public class LevelsManagerTest {
*/ */
@Test @Test
public void testGetLevelsData() { public void testGetLevelsData() {
assertEquals(levelsData, lm.getLevelsData(uuid)); assertEquals(levelsData, lm.getLevelsData(island));
} }