Add IslandTop command with refresh cooldown (#1)

* Add IslandTop command with refresh cooldown

The "IslandTop" command has been added. This provides a sorted list of top-performing islands. A configuration for refresh cooldown was also added. The sorted island list is updated once the specified minutes in the configuration have passed to improve performance. The ranking display for the player's island is highlighted in green text.

* Update IslandTop command

On plugin startup the IslandData is now loaded in. IslandData was moved to its own file and gets updated when a new island is created or when one levels up. The IslandData gets deleted when the owner leaves the island, destroying it.
This commit is contained in:
Stijn 2024-02-18 14:22:17 +01:00 committed by GitHub
parent d09e8567ac
commit d1c8d29cde
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 148 additions and 6 deletions

View File

@ -4,6 +4,7 @@ import com.alttd.cometskyblock.commands.challenges.ChallengeCommand;
import com.alttd.cometskyblock.commands.island.IslandCommand;
import com.alttd.cometskyblock.configuration.*;
import com.alttd.cometskyblock.gui.GUIListener;
import com.alttd.cometskyblock.island.IslandData;
import com.alttd.cometskyblock.listeners.BedListener;
import com.alttd.cometskyblock.listeners.CobbestoneGeneratorListener;
import com.alttd.cometskyblock.listeners.PlayerJoinListener;
@ -48,6 +49,9 @@ public class CometSkyBlockPlugin extends JavaPlugin implements CometSkyBlockAPI
// Load event listeners
loadEventListeners();
// Reload island data for top list
IslandData.reloadAllIslandData();
// load data from storage
// run cleanup tasks

View File

@ -16,6 +16,7 @@ public class IslandCommand extends PlayerSubCommand {
this.plugin = plugin;
registerSubCommand(new IslandGo(plugin)); // TODO -- Add some more output
registerSubCommand(new IslandTop(plugin));
registerSubCommand(new IslandRestart(plugin)); // TODO -- Add IslandRestartCommand
registerSubCommand(new IslandAccept(plugin));
registerSubCommand(new IslandDeny(plugin));

View File

@ -2,19 +2,58 @@ package com.alttd.cometskyblock.commands.island;
import com.alttd.cometskyblock.CometSkyBlockPlugin;
import com.alttd.cometskyblock.commands.PlayerSubCommand;
import com.alttd.cometskyblock.configuration.PluginConfiguration;
import com.alttd.cometskyblock.island.IslandData;
import com.alttd.cometskyblock.island.IslandPlayer;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.OptionalInt;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class IslandTop extends PlayerSubCommand {
private final PluginConfiguration pluginConfiguration;
public IslandTop(CometSkyBlockPlugin plugin) {
super(plugin, "top");
pluginConfiguration = plugin.pluginConfiguration().get();
}
// TODO - Finish TOP command
@Override
public boolean execute(Player player, IslandPlayer islandPlayer, String[] args) {
// TODO -- Implement
player.sendRichMessage("<red>Not implemented yet, please wait for a future update.");
int playerIslandId = islandPlayer.islandId();
//TODO allow players to iterate through the list
List<IslandData> islandData = IslandData.getIslandData(pluginConfiguration.topRefreshMinutesCoolDown());
String islandRankings = IntStream.range(0, 10)
.mapToObj(i -> (i + 1) + ". " + islandData.get(i).format(playerIslandId))
.collect(Collectors.joining("\n"));
islandRankings += getFormattedPlayerIslandRanking(islandData, playerIslandId, 0, 10);
player.sendRichMessage("<gold>Island Top:\n" + islandRankings);
return true;
}
/**
* Retrieves the formatted ranking of a player's island based on their island ID.
*
* @param playerIslandId The ID of the player's island.
* @return The formatted ranking of the player's island. Returns an empty string if the player's island is not ranked in the top 10.
*/
private String getFormattedPlayerIslandRanking(List<IslandData> islandDataList, int playerIslandId, int minPos, int maxPos) {
OptionalInt position = IntStream.range(0, islandDataList.size())
.filter(i -> playerIslandId == islandDataList.get(i).islandId())
.findFirst();
if (position.isPresent()) {
int playerIslandPosition = position.getAsInt();
if (playerIslandId < minPos || playerIslandPosition > maxPos) {
IslandData islandData = islandDataList.get(playerIslandPosition);
return "\n<green>" + playerIslandPosition + ". " + islandData.format() + "</green>";
}
}
return "";
}
}

View File

@ -29,5 +29,6 @@ public class PluginConfiguration implements Configuration {
}
private int requestTimeOut = 30;
private int topRefreshMinutesCoolDown = 10;
}

View File

@ -30,6 +30,10 @@ public class Island extends YamlConfiguration {
}
}
public static Island loadIslandFromFile(File file) {
return new Island(file);
}
public static void remove(UUID uuid) {
synchronized (configs) {
configs.remove(uuid);
@ -54,6 +58,16 @@ public class Island extends YamlConfiguration {
reload();
}
private Island(File file) {
this.islandUUID = Island.NILL_UUID;
this.file = file;
reload();
}
public static Collection<Island> getIslands() {
return configs.values();
}
private void reload() {
synchronized (saveLock) {
try {
@ -103,8 +117,9 @@ public class Island extends YamlConfiguration {
return getInt("island.level", 0);
}
public void level(int id) {
set("island.level", id);
public void level(int level) {
IslandData.updateIsland(new IslandData(islandId(), islandName(), level));
set("island.level", level);
save();
}

View File

@ -0,0 +1,79 @@
package com.alttd.cometskyblock.island;
import com.alttd.cometskyblock.CometSkyBlockPlugin;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Logger;
import java.util.stream.Stream;
public record IslandData(int islandId, String name, int level) {
private static final Int2ObjectOpenHashMap<IslandData> islandDataMap = new Int2ObjectOpenHashMap<>();
private static List<IslandData> sortedIslandData = new ArrayList<>();
private static Instant lastUpdated = Instant.MIN;
public synchronized static void updateIsland(IslandData islandData) {
IslandData.islandDataMap.put(islandData.islandId, islandData);
}
public synchronized static void removeIsland(int islandId) {
IslandData.islandDataMap.remove(islandId);
}
public static List<IslandData> getIslandData(int maxAgeMinutes) {
updateIslandData(maxAgeMinutes);
return IslandData.sortedIslandData;
}
private static synchronized void updateIslandData(int maxAgeMinutes) {
if (Duration.between(lastUpdated, Instant.now()).toMinutes() <= maxAgeMinutes)
return;
lastUpdated = Instant.now();
sortedIslandData = islandDataMap.values().stream()
.sorted(Comparator.comparingInt(IslandData::level).reversed())
.toList();
lastUpdated = Instant.now();
}
public static synchronized void reloadAllIslandData() {
Logger logger = CometSkyBlockPlugin.instance().getLogger();
File islandDataDir = new File(CometSkyBlockPlugin.instance().getDataFolder(), "IslandData");
if (!islandDataDir.isDirectory()) {
logger.warning("No data folder found for IslandData, unable to load files");
return;
}
islandDataMap.clear();
try (Stream<Path> paths = Files.walk(islandDataDir.toPath(), 1)) {
paths.filter(Files::isRegularFile)
.filter(path -> path.toString().endsWith(".yml"))
.map(Path::toFile)
.map(Island::loadIslandFromFile)
.map(island -> new IslandData(island.islandId(), island.islandName(), island.level()))
.forEach(island -> islandDataMap.put(island.islandId, island));
} catch (IOException e) {
logger.severe("Encountered exception while reloading IslandData");
logger.throwing(IslandData.class.getName(), "reloadAllIslandData", e);
}
}
public String format(int playerIslandId) {
if (playerIslandId == islandId) {
return "<green>" + format() + "</green>";
} else {
return format();
}
}
public String format() {
return name + " - " + level;
}
}

View File

@ -2,6 +2,7 @@ package com.alttd.cometskyblock.request;
import com.alttd.cometskyblock.CometSkyBlockPlugin;
import com.alttd.cometskyblock.island.Island;
import com.alttd.cometskyblock.island.IslandData;
import com.alttd.cometskyblock.island.IslandPlayer;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@ -25,8 +26,10 @@ public class LeaveRequest extends Request {
requester().sendRichMessage(requests().leave().accept(), placeholders());
IslandPlayer islandPlayer = IslandPlayer.getIslandPlayer(requester().getUniqueId());
Island island = Island.getIsland(islandPlayer.islandUUID());
if (islandPlayer.islandOwner())
if (islandPlayer.islandOwner()) {
IslandData.removeIsland(island.islandId());
island.owner(Island.NILL_UUID);
}
islandPlayer.islandId(0);
islandPlayer.islandUUID(null);
World world = Bukkit.getWorlds().get(0);