mirror of
https://github.com/BentoBoxWorld/Level.git
synced 2024-09-28 05:37:29 +02:00
Implement a cache for top tens (#309)
This commit is contained in:
parent
a4f8d12138
commit
4d16e9c227
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
30
src/main/java/world/bentobox/level/util/CachedData.java
Normal file
30
src/main/java/world/bentobox/level/util/CachedData.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user