Implement a cache for top tens

This commit is contained in:
tastybento 2024-05-04 12:28:28 -07:00
parent a4f8d12138
commit 05de528d0d
2 changed files with 268 additions and 220 deletions

View File

@ -2,6 +2,7 @@ package world.bentobox.level;
import java.math.BigInteger; import java.math.BigInteger;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.time.Instant;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -31,18 +32,19 @@ import world.bentobox.level.events.IslandPreLevelEvent;
import world.bentobox.level.objects.IslandLevels; import world.bentobox.level.objects.IslandLevels;
import world.bentobox.level.objects.LevelsData; import world.bentobox.level.objects.LevelsData;
import world.bentobox.level.objects.TopTenData; import world.bentobox.level.objects.TopTenData;
import world.bentobox.level.util.CachedData;
public class LevelsManager { public class LevelsManager {
private static final String INTOPTEN = "intopten"; private static final String INTOPTEN = "intopten";
private static final TreeMap<BigInteger, String> LEVELS; private static final TreeMap<BigInteger, String> LEVELS;
private static final BigInteger THOUSAND = BigInteger.valueOf(1000); private static final BigInteger THOUSAND = BigInteger.valueOf(1000);
static { static {
LEVELS = new TreeMap<>(); LEVELS = new TreeMap<>();
LEVELS.put(THOUSAND, "k"); LEVELS.put(THOUSAND, "k");
LEVELS.put(THOUSAND.pow(2), "M"); LEVELS.put(THOUSAND.pow(2), "M");
LEVELS.put(THOUSAND.pow(3), "G"); LEVELS.put(THOUSAND.pow(3), "G");
LEVELS.put(THOUSAND.pow(4), "T"); LEVELS.put(THOUSAND.pow(4), "T");
} }
private final Level addon; private final Level addon;
@ -52,49 +54,51 @@ public class LevelsManager {
private final Map<String, IslandLevels> levelsCache; private final Map<String, IslandLevels> levelsCache;
// Top ten lists // Top ten lists
private final Map<World, TopTenData> topTenLists; private final Map<World, TopTenData> topTenLists;
// Cache for top tens
private Map<World, CachedData> cache = new HashMap<>();
public LevelsManager(Level addon) { public LevelsManager(Level addon) {
this.addon = addon; this.addon = addon;
// 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, IslandLevels.class); handler = new Database<>(addon, IslandLevels.class);
// Initialize the cache // Initialize the cache
levelsCache = new HashMap<>(); levelsCache = new HashMap<>();
// Initialize top ten lists // Initialize top ten lists
topTenLists = new ConcurrentHashMap<>(); topTenLists = new ConcurrentHashMap<>();
} }
public void migrate() { public void migrate() {
Database<LevelsData> oldDb = new Database<>(addon, LevelsData.class); Database<LevelsData> oldDb = new Database<>(addon, LevelsData.class);
oldDb.loadObjects().forEach(ld -> { oldDb.loadObjects().forEach(ld -> {
try { try {
UUID owner = UUID.fromString(ld.getUniqueId()); UUID owner = UUID.fromString(ld.getUniqueId());
// Step through each world // Step through each world
ld.getLevels().keySet().stream() ld.getLevels().keySet().stream()
// World // World
.map(Bukkit::getWorld).filter(Objects::nonNull) .map(Bukkit::getWorld).filter(Objects::nonNull)
// Island // Island
.map(w -> addon.getIslands().getIsland(w, owner)).filter(Objects::nonNull).forEach(i -> { .map(w -> addon.getIslands().getIsland(w, owner)).filter(Objects::nonNull).forEach(i -> {
// Make new database entry // Make new database entry
World w = i.getWorld(); World w = i.getWorld();
IslandLevels il = new IslandLevels(i.getUniqueId()); IslandLevels il = new IslandLevels(i.getUniqueId());
il.setInitialLevel(ld.getInitialLevel(w)); il.setInitialLevel(ld.getInitialLevel(w));
il.setLevel(ld.getLevel(w)); il.setLevel(ld.getLevel(w));
il.setMdCount(ld.getMdCount(w)); il.setMdCount(ld.getMdCount(w));
il.setPointsToNextLevel(ld.getPointsToNextLevel(w)); il.setPointsToNextLevel(ld.getPointsToNextLevel(w));
il.setUwCount(ld.getUwCount(w)); il.setUwCount(ld.getUwCount(w));
// Save it // Save it
handler.saveObjectAsync(il); handler.saveObjectAsync(il);
}); });
// Now delete the old database entry // Now delete the old database entry
oldDb.deleteID(ld.getUniqueId()); oldDb.deleteID(ld.getUniqueId());
} catch (Exception e) { } catch (Exception e) {
addon.logError("Could not migrate level data database! " + e.getMessage()); addon.logError("Could not migrate level data database! " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
return; return;
} }
}); });
} }
/** /**
@ -105,12 +109,12 @@ public class LevelsManager {
* @return true if successful, false if not added * @return true if successful, false if not added
*/ */
private boolean addToTopTen(Island island, long lv) { private boolean addToTopTen(Island island, long lv) {
if (island != null && island.getOwner() != null && hasTopTenPerm(island.getWorld(), island.getOwner())) { if (island != null && island.getOwner() != null && hasTopTenPerm(island.getWorld(), island.getOwner())) {
topTenLists.computeIfAbsent(island.getWorld(), k -> new TopTenData(island.getWorld())).getTopTen() topTenLists.computeIfAbsent(island.getWorld(), k -> new TopTenData(island.getWorld())).getTopTen()
.put(island.getUniqueId(), lv); .put(island.getUniqueId(), lv);
return true; return true;
} }
return false; return false;
} }
/** /**
@ -122,26 +126,26 @@ public class LevelsManager {
* @return completable future with the results of the calculation * @return completable future with the results of the calculation
*/ */
public CompletableFuture<Results> calculateLevel(UUID targetPlayer, Island island) { public CompletableFuture<Results> calculateLevel(UUID targetPlayer, Island island) {
CompletableFuture<Results> result = new CompletableFuture<>(); CompletableFuture<Results> result = new CompletableFuture<>();
// Fire pre-level calc event // Fire pre-level calc event
IslandPreLevelEvent e = new IslandPreLevelEvent(targetPlayer, island); IslandPreLevelEvent e = new IslandPreLevelEvent(targetPlayer, island);
Bukkit.getPluginManager().callEvent(e); Bukkit.getPluginManager().callEvent(e);
if (e.isCancelled()) { if (e.isCancelled()) {
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
} }
// Add island to the pipeline // Add island to the pipeline
addon.getPipeliner().addIsland(island).thenAccept(r -> { addon.getPipeliner().addIsland(island).thenAccept(r -> {
// Results are irrelevant because the island is unowned or deleted, or // Results are irrelevant because the island is unowned or deleted, or
// IslandLevelCalcEvent is cancelled // IslandLevelCalcEvent is cancelled
if (r == null || fireIslandLevelCalcEvent(targetPlayer, island, r)) { if (r == null || fireIslandLevelCalcEvent(targetPlayer, island, r)) {
result.complete(null); result.complete(null);
} }
// Save result // Save result
setIslandResults(island, r); setIslandResults(island, r);
// Save the island scan details // Save the island scan details
result.complete(r); result.complete(r);
}); });
return result; return result;
} }
/** /**
@ -153,19 +157,19 @@ public class LevelsManager {
* @return true if canceled * @return true if canceled
*/ */
private boolean fireIslandLevelCalcEvent(UUID targetPlayer, Island island, Results results) { private boolean fireIslandLevelCalcEvent(UUID targetPlayer, Island island, Results results) {
// Fire post calculation event // Fire post calculation event
IslandLevelCalculatedEvent ilce = new IslandLevelCalculatedEvent(targetPlayer, island, results); IslandLevelCalculatedEvent ilce = new IslandLevelCalculatedEvent(targetPlayer, island, results);
Bukkit.getPluginManager().callEvent(ilce); Bukkit.getPluginManager().callEvent(ilce);
if (ilce.isCancelled()) if (ilce.isCancelled())
return true; return true;
// Set the values if they were altered // Set the values if they were altered
results.setLevel((Long) ilce.getKeyValues().getOrDefault("level", results.getLevel())); results.setLevel((Long) ilce.getKeyValues().getOrDefault("level", results.getLevel()));
results.setInitialLevel((Long) ilce.getKeyValues().getOrDefault("initialLevel", results.getInitialLevel())); results.setInitialLevel((Long) ilce.getKeyValues().getOrDefault("initialLevel", results.getInitialLevel()));
results.setDeathHandicap((int) ilce.getKeyValues().getOrDefault("deathHandicap", results.getDeathHandicap())); results.setDeathHandicap((int) ilce.getKeyValues().getOrDefault("deathHandicap", results.getDeathHandicap()));
results.setPointsToNextLevel( results.setPointsToNextLevel(
(Long) ilce.getKeyValues().getOrDefault("pointsToNextLevel", results.getPointsToNextLevel())); (Long) ilce.getKeyValues().getOrDefault("pointsToNextLevel", results.getPointsToNextLevel()));
results.setTotalPoints((Long) ilce.getKeyValues().getOrDefault("totalPoints", results.getTotalPoints())); results.setTotalPoints((Long) ilce.getKeyValues().getOrDefault("totalPoints", results.getTotalPoints()));
return ((Boolean) ilce.getKeyValues().getOrDefault("isCancelled", false)); return ((Boolean) ilce.getKeyValues().getOrDefault("isCancelled", false));
} }
/** /**
@ -176,25 +180,25 @@ public class LevelsManager {
* @return string of the level. * @return string of the level.
*/ */
public String formatLevel(@Nullable Long lvl) { public String formatLevel(@Nullable Long lvl) {
if (lvl == null) if (lvl == null)
return ""; return "";
String level = String.valueOf(lvl); String level = String.valueOf(lvl);
// Asking for the level of another player // Asking for the level of another player
if (addon.getSettings().isShorthand()) { if (addon.getSettings().isShorthand()) {
BigInteger levelValue = BigInteger.valueOf(lvl); BigInteger levelValue = BigInteger.valueOf(lvl);
Map.Entry<BigInteger, String> stage = LEVELS.floorEntry(levelValue); Map.Entry<BigInteger, String> stage = LEVELS.floorEntry(levelValue);
if (stage != null) { // level > 1000 if (stage != null) { // level > 1000
// 1 052 -> 1.0k // 1 052 -> 1.0k
// 1 527 314 -> 1.5M // 1 527 314 -> 1.5M
// 3 874 130 021 -> 3.8G // 3 874 130 021 -> 3.8G
// 4 002 317 889 -> 4.0T // 4 002 317 889 -> 4.0T
level = new DecimalFormat("#.#").format( level = new DecimalFormat("#.#").format(
levelValue.divide(stage.getKey().divide(THOUSAND)).doubleValue() / 1000.0) + stage.getValue(); levelValue.divide(stage.getKey().divide(THOUSAND)).doubleValue() / 1000.0) + stage.getValue();
} }
} }
return level; return level;
} }
/** /**
@ -204,7 +208,7 @@ public class LevelsManager {
* @return initial level of island * @return initial level of island
*/ */
public long getInitialLevel(Island island) { public long getInitialLevel(Island island) {
return getLevelsData(island).getInitialLevel(); return getLevelsData(island).getInitialLevel();
} }
/** /**
@ -216,11 +220,11 @@ public class LevelsManager {
* null * null
*/ */
public long getIslandLevel(@NonNull World world, @Nullable UUID targetPlayer) { public long getIslandLevel(@NonNull World world, @Nullable UUID targetPlayer) {
if (targetPlayer == null) if (targetPlayer == null)
return 0L; return 0L;
// Get the island // Get the island
Island island = addon.getIslands().getIsland(world, targetPlayer); Island island = addon.getIslands().getIsland(world, targetPlayer);
return island == null ? 0L : getLevelsData(island).getLevel(); return island == null ? 0L : getLevelsData(island).getLevel();
} }
/** /**
@ -232,11 +236,11 @@ public class LevelsManager {
* is null * is null
*/ */
public long getIslandMaxLevel(@NonNull World world, @Nullable UUID targetPlayer) { public long getIslandMaxLevel(@NonNull World world, @Nullable UUID targetPlayer) {
if (targetPlayer == null) if (targetPlayer == null)
return 0L; return 0L;
// Get the island // Get the island
Island island = addon.getIslands().getIsland(world, targetPlayer); Island island = addon.getIslands().getIsland(world, targetPlayer);
return island == null ? 0L : getLevelsData(island).getMaxLevel(); return island == null ? 0L : getLevelsData(island).getMaxLevel();
} }
/** /**
@ -248,7 +252,7 @@ public class LevelsManager {
* null * null
*/ */
public String getIslandLevelString(@NonNull World world, @Nullable UUID targetPlayer) { public String getIslandLevelString(@NonNull World world, @Nullable UUID targetPlayer) {
return formatLevel(getIslandLevel(world, targetPlayer)); return formatLevel(getIslandLevel(world, targetPlayer));
} }
/** /**
@ -259,24 +263,24 @@ public class LevelsManager {
*/ */
@NonNull @NonNull
public IslandLevels getLevelsData(@NonNull Island island) { public IslandLevels getLevelsData(@NonNull Island island) {
String id = island.getUniqueId(); String id = island.getUniqueId();
if (levelsCache.containsKey(id)) { if (levelsCache.containsKey(id)) {
return levelsCache.get(id); return levelsCache.get(id);
} }
// Get from database if not in cache // Get from database if not in cache
if (handler.objectExists(id)) { if (handler.objectExists(id)) {
IslandLevels ld = handler.loadObject(id); IslandLevels ld = handler.loadObject(id);
if (ld != null) { if (ld != null) {
levelsCache.put(id, ld); levelsCache.put(id, ld);
} else { } else {
handler.deleteID(id); handler.deleteID(id);
levelsCache.put(id, new IslandLevels(id)); levelsCache.put(id, new IslandLevels(id));
} }
} else { } else {
levelsCache.put(id, new IslandLevels(id)); levelsCache.put(id, new IslandLevels(id));
} }
// Return cached value // Return cached value
return levelsCache.get(id); return levelsCache.get(id);
} }
/** /**
@ -288,10 +292,10 @@ public class LevelsManager {
* @return string with the number required or blank if the player is unknown * @return string with the number required or blank if the player is unknown
*/ */
public String getPointsToNextString(@NonNull World world, @Nullable UUID targetPlayer) { public String getPointsToNextString(@NonNull World world, @Nullable UUID targetPlayer) {
if (targetPlayer == null) if (targetPlayer == null)
return ""; return "";
Island island = addon.getIslands().getIsland(world, targetPlayer); Island island = addon.getIslands().getIsland(world, targetPlayer);
return island == null ? "" : String.valueOf(getLevelsData(island).getPointsToNextLevel()); return island == null ? "" : String.valueOf(getLevelsData(island).getPointsToNextLevel());
} }
/** /**
@ -304,27 +308,27 @@ public class LevelsManager {
*/ */
@NonNull @NonNull
public Map<Island, Long> getWeightedTopTen(@NonNull World world, int size) { public Map<Island, Long> getWeightedTopTen(@NonNull World world, int size) {
createAndCleanRankings(world); createAndCleanRankings(world);
Map<Island, Long> weightedTopTen = topTenLists.get(world).getTopTen().entrySet().stream() Map<Island, Long> weightedTopTen = topTenLists.get(world).getTopTen().entrySet().stream()
.map(en -> addon.getIslands().getIslandById(en.getKey()).map(island -> { .map(en -> addon.getIslands().getIslandById(en.getKey()).map(island -> {
long value = (long) (en.getValue() / (double) Math.max(1, island.getMemberSet().size())); // Calculate long value = (long) (en.getValue() / (double) Math.max(1, island.getMemberSet().size())); // Calculate
// weighted // weighted
// value // value
return new AbstractMap.SimpleEntry<>(island, value); return new AbstractMap.SimpleEntry<>(island, value);
}).orElse(null)) // Handle islands that do not exist according to this ID - old deleted ones }).orElse(null)) // Handle islands that do not exist according to this ID - old deleted ones
.filter(Objects::nonNull) // Filter out null entries .filter(Objects::nonNull) // Filter out null entries
.filter(en -> en.getValue() > 0) // Filter out entries with non-positive values .filter(en -> en.getValue() > 0) // Filter out entries with non-positive values
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) // Sort in descending order of values .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) // Sort in descending order of values
.limit(size) // Limit to the top 'size' entries .limit(size) // Limit to the top 'size' entries
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, // In case of key .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, // In case of key
// collision, choose // collision, choose
// the first one // the first one
LinkedHashMap::new // Preserves the order of entries LinkedHashMap::new // Preserves the order of entries
)); ));
// Return the unmodifiable map // Return the unmodifiable map
return Collections.unmodifiableMap(weightedTopTen); return Collections.unmodifiableMap(weightedTopTen);
} }
@ -338,26 +342,38 @@ public class LevelsManager {
*/ */
@NonNull @NonNull
public Map<String, Long> getTopTen(@NonNull World world, int size) { public Map<String, Long> getTopTen(@NonNull World world, int size) {
createAndCleanRankings(world); createAndCleanRankings(world);
// Return the sorted map CachedData cachedData = cache.get(world);
return Collections.unmodifiableMap(topTenLists.get(world).getTopTen().entrySet().stream() Instant now = Instant.now();
.filter(l -> l.getValue() > 0).sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.limit(size) if (cachedData != null && cachedData.getLastUpdated().plusSeconds(1).isAfter(now)) {
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new))); return cachedData.getCachedMap();
} else {
Map<String, Long> newTopTen = calculateTopTen(world, size);
cache.put(world, new CachedData(newTopTen, now));
return newTopTen;
}
}
private Map<String, Long> calculateTopTen(@NonNull World world, int size) {
return Collections.unmodifiableMap(topTenLists.get(world).getTopTen().entrySet().stream()
.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)));
} }
void createAndCleanRankings(@NonNull World world) { void createAndCleanRankings(@NonNull World world) {
topTenLists.computeIfAbsent(world, TopTenData::new); topTenLists.computeIfAbsent(world, TopTenData::new);
// Remove player from top ten if they are online and do not have the perm // Remove player from top ten if they are online and do not have the perm
topTenLists.get(world).getTopTen().keySet().removeIf(u -> addon.getIslands().getIslandById(u) topTenLists.get(world).getTopTen().keySet().removeIf(u -> addon.getIslands().getIslandById(u)
.filter(i -> i.getOwner() == null || !hasTopTenPerm(world, i.getOwner())).isPresent()); .filter(i -> i.getOwner() == null || !hasTopTenPerm(world, i.getOwner())).isPresent());
} }
/** /**
* @return the topTenLists * @return the topTenLists
*/ */
public Map<World, TopTenData> getTopTenLists() { public Map<World, TopTenData> getTopTenLists() {
return topTenLists; return topTenLists;
} }
/** /**
@ -368,13 +384,13 @@ public class LevelsManager {
* @return rank placing - note - placing of 1 means top ranked * @return rank placing - note - placing of 1 means top ranked
*/ */
public int getRank(@NonNull World world, UUID uuid) { public int getRank(@NonNull World world, UUID uuid) {
createAndCleanRankings(world); createAndCleanRankings(world);
Stream<Entry<String, Long>> stream = topTenLists.get(world).getTopTen().entrySet().stream() Stream<Entry<String, Long>> stream = topTenLists.get(world).getTopTen().entrySet().stream()
.filter(l -> l.getValue() > 0).sorted(Collections.reverseOrder(Map.Entry.comparingByValue())); .filter(l -> l.getValue() > 0).sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
// Get player's current island // Get player's current island
Island island = addon.getIslands().getIsland(world, uuid); Island island = addon.getIslands().getIsland(world, uuid);
String id = island == null ? null : island.getUniqueId(); String id = island == null ? null : island.getUniqueId();
return (int) (stream.takeWhile(x -> !x.getKey().equals(id)).map(Map.Entry::getKey).count() + 1); return (int) (stream.takeWhile(x -> !x.getKey().equals(id)).map(Map.Entry::getKey).count() + 1);
} }
/** /**
@ -385,26 +401,26 @@ public class LevelsManager {
* @return true if player has the perm or the player is offline * @return true if player has the perm or the player is offline
*/ */
boolean hasTopTenPerm(@NonNull World world, @NonNull UUID targetPlayer) { boolean hasTopTenPerm(@NonNull World world, @NonNull UUID targetPlayer) {
String permPrefix = addon.getPlugin().getIWM().getPermissionPrefix(world); String permPrefix = addon.getPlugin().getIWM().getPermissionPrefix(world);
return Bukkit.getPlayer(targetPlayer) == null return Bukkit.getPlayer(targetPlayer) == null
|| Bukkit.getPlayer(targetPlayer).hasPermission(permPrefix + INTOPTEN); || Bukkit.getPlayer(targetPlayer).hasPermission(permPrefix + INTOPTEN);
} }
/** /**
* Loads all the top tens from the database * Loads all the top tens from the database
*/ */
public void loadTopTens() { public void loadTopTens() {
topTenLists.clear(); topTenLists.clear();
Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> { Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> {
addon.log("Generating rankings"); addon.log("Generating rankings");
handler.loadObjects().forEach(il -> { handler.loadObjects().forEach(il -> {
if (il.getLevel() > 0) { if (il.getLevel() > 0) {
addon.getIslands().getIslandById(il.getUniqueId()) addon.getIslands().getIslandById(il.getUniqueId())
.ifPresent(i -> this.addToTopTen(i, il.getLevel())); .ifPresent(i -> this.addToTopTen(i, il.getLevel()));
} }
}); });
topTenLists.keySet().forEach(w -> addon.log("Generated rankings for " + w.getName())); topTenLists.keySet().forEach(w -> addon.log("Generated rankings for " + w.getName()));
}); });
} }
/** /**
@ -414,9 +430,11 @@ public class LevelsManager {
* @param uuid - the island's uuid * @param uuid - the island's uuid
*/ */
public void removeEntry(World world, String uuid) { public void removeEntry(World world, String uuid) {
if (topTenLists.containsKey(world)) { if (topTenLists.containsKey(world)) {
topTenLists.get(world).getTopTen().remove(uuid); topTenLists.get(world).getTopTen().remove(uuid);
} // Invalidate the cache because of this deletion
cache.remove(world);
}
} }
@ -427,10 +445,10 @@ public class LevelsManager {
* @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.getWorld() == null) if (island.getWorld() == null)
return; return;
levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new).setInitialLevel(lv); levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new).setInitialLevel(lv);
handler.saveObjectAsync(levelsCache.get(island.getUniqueId())); handler.saveObjectAsync(levelsCache.get(island.getUniqueId()));
} }
/** /**
@ -442,21 +460,21 @@ 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) {
// Get the island // Get the island
Island island = addon.getIslands().getIsland(world, targetPlayer); Island island = addon.getIslands().getIsland(world, targetPlayer);
if (island != null) { if (island != null) {
String id = island.getUniqueId(); String id = island.getUniqueId();
IslandLevels il = levelsCache.computeIfAbsent(id, IslandLevels::new); IslandLevels il = levelsCache.computeIfAbsent(id, IslandLevels::new);
// Remove the initial level // Remove the initial level
if (addon.getSettings().isZeroNewIslandLevels()) { if (addon.getSettings().isZeroNewIslandLevels()) {
il.setLevel(lv - il.getInitialLevel()); il.setLevel(lv - il.getInitialLevel());
} else { } else {
il.setLevel(lv); il.setLevel(lv);
} }
handler.saveObjectAsync(levelsCache.get(id)); handler.saveObjectAsync(levelsCache.get(id));
// Update TopTen // Update TopTen
addToTopTen(island, levelsCache.get(id).getLevel()); addToTopTen(island, levelsCache.get(id).getLevel());
} }
} }
/** /**
@ -468,18 +486,18 @@ public class LevelsManager {
* @param r - results of the calculation * @param r - results of the calculation
*/ */
private void setIslandResults(Island island, Results r) { private void setIslandResults(Island island, Results r) {
if (island == null) if (island == null)
return; return;
IslandLevels ld = levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new); IslandLevels ld = levelsCache.computeIfAbsent(island.getUniqueId(), IslandLevels::new);
ld.setLevel(r.getLevel()); ld.setLevel(r.getLevel());
ld.setUwCount(Maps.asMap(r.getUwCount().elementSet(), elem -> r.getUwCount().count(elem))); 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.setMdCount(Maps.asMap(r.getMdCount().elementSet(), elem -> r.getMdCount().count(elem)));
ld.setPointsToNextLevel(r.getPointsToNextLevel()); ld.setPointsToNextLevel(r.getPointsToNextLevel());
ld.setTotalPoints(r.getTotalPoints()); ld.setTotalPoints(r.getTotalPoints());
levelsCache.put(island.getUniqueId(), ld); levelsCache.put(island.getUniqueId(), ld);
handler.saveObjectAsync(ld); handler.saveObjectAsync(ld);
// Update TopTen // Update TopTen
addToTopTen(island, ld.getLevel()); addToTopTen(island, ld.getLevel());
} }
/** /**
@ -488,8 +506,8 @@ public class LevelsManager {
* @param uniqueId - id of island * @param uniqueId - id of island
*/ */
public void deleteIsland(String uniqueId) { public void deleteIsland(String uniqueId) {
levelsCache.remove(uniqueId); levelsCache.remove(uniqueId);
handler.deleteID(uniqueId); handler.deleteID(uniqueId);
} }
} }

View File

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