diff --git a/plugin/src/main/java/com/alttd/cometskyblock/CometSkyBlockPlugin.java b/plugin/src/main/java/com/alttd/cometskyblock/CometSkyBlockPlugin.java index 8a39cae..98b4b2b 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/CometSkyBlockPlugin.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/CometSkyBlockPlugin.java @@ -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 diff --git a/plugin/src/main/java/com/alttd/cometskyblock/commands/island/IslandCommand.java b/plugin/src/main/java/com/alttd/cometskyblock/commands/island/IslandCommand.java index 7713fa1..c5337b9 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/commands/island/IslandCommand.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/commands/island/IslandCommand.java @@ -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)); diff --git a/plugin/src/main/java/com/alttd/cometskyblock/commands/island/IslandTop.java b/plugin/src/main/java/com/alttd/cometskyblock/commands/island/IslandTop.java index 78e84cc..26a19ee 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/commands/island/IslandTop.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/commands/island/IslandTop.java @@ -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("Not implemented yet, please wait for a future update."); + int playerIslandId = islandPlayer.islandId(); + + //TODO allow players to iterate through the list + List 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("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 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" + playerIslandPosition + ". " + islandData.format() + ""; + } + } + return ""; + } } diff --git a/plugin/src/main/java/com/alttd/cometskyblock/configuration/PluginConfiguration.java b/plugin/src/main/java/com/alttd/cometskyblock/configuration/PluginConfiguration.java index 6fab54b..652b906 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/configuration/PluginConfiguration.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/configuration/PluginConfiguration.java @@ -29,5 +29,6 @@ public class PluginConfiguration implements Configuration { } private int requestTimeOut = 30; + private int topRefreshMinutesCoolDown = 10; } diff --git a/plugin/src/main/java/com/alttd/cometskyblock/island/Island.java b/plugin/src/main/java/com/alttd/cometskyblock/island/Island.java index 2950eba..deeb4f9 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/island/Island.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/island/Island.java @@ -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 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(); } diff --git a/plugin/src/main/java/com/alttd/cometskyblock/island/IslandData.java b/plugin/src/main/java/com/alttd/cometskyblock/island/IslandData.java new file mode 100644 index 0000000..5567b0c --- /dev/null +++ b/plugin/src/main/java/com/alttd/cometskyblock/island/IslandData.java @@ -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 islandDataMap = new Int2ObjectOpenHashMap<>(); + private static List 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 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 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 "" + format() + ""; + } else { + return format(); + } + } + + public String format() { + return name + " - " + level; + } +} diff --git a/plugin/src/main/java/com/alttd/cometskyblock/request/LeaveRequest.java b/plugin/src/main/java/com/alttd/cometskyblock/request/LeaveRequest.java index bf290a5..8788329 100644 --- a/plugin/src/main/java/com/alttd/cometskyblock/request/LeaveRequest.java +++ b/plugin/src/main/java/com/alttd/cometskyblock/request/LeaveRequest.java @@ -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);