diff --git a/src/main/java/net/knarcraft/minigames/MiniGames.java b/src/main/java/net/knarcraft/minigames/MiniGames.java index 1edd422..9a0f6d0 100644 --- a/src/main/java/net/knarcraft/minigames/MiniGames.java +++ b/src/main/java/net/knarcraft/minigames/MiniGames.java @@ -50,6 +50,7 @@ import net.knarcraft.minigames.listener.DamageListener; import net.knarcraft.minigames.listener.MoveListener; import net.knarcraft.minigames.listener.PlayerLeaveListener; import net.knarcraft.minigames.placeholder.DropperRecordExpansion; +import net.knarcraft.minigames.placeholder.ParkourRecordExpansion; import org.bukkit.Bukkit; import org.bukkit.command.CommandExecutor; import org.bukkit.command.PluginCommand; @@ -77,6 +78,7 @@ public final class MiniGames extends JavaPlugin { private DropperArenaHandler dropperArenaHandler; private DropperArenaPlayerRegistry dropperArenaPlayerRegistry; private DropperRecordExpansion dropperRecordExpansion; + private ParkourRecordExpansion parkourRecordExpansion; private ParkourArenaHandler parkourArenaHandler; private ParkourArenaPlayerRegistry parkourArenaPlayerRegistry; @@ -195,6 +197,7 @@ public final class MiniGames extends JavaPlugin { // Clear record caches this.dropperRecordExpansion.clearCaches(); + this.parkourRecordExpansion.clearCaches(); } @Override @@ -229,7 +232,7 @@ public final class MiniGames extends JavaPlugin { this.dropperArenaPlayerRegistry = new DropperArenaPlayerRegistry(); this.dropperArenaHandler = new DropperArenaHandler(this.dropperArenaPlayerRegistry); this.dropperArenaHandler.load(); - + this.parkourArenaPlayerRegistry = new ParkourArenaPlayerRegistry(); this.parkourArenaHandler = new ParkourArenaHandler(this.parkourArenaPlayerRegistry); this.parkourArenaHandler.load(); @@ -263,7 +266,11 @@ public final class MiniGames extends JavaPlugin { if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { this.dropperRecordExpansion = new DropperRecordExpansion(this); if (!this.dropperRecordExpansion.register()) { - log(Level.WARNING, "Unable to register PlaceholderAPI expansion!"); + log(Level.WARNING, "Unable to register PlaceholderAPI dropper expansion!"); + } + this.parkourRecordExpansion = new ParkourRecordExpansion(this); + if (!this.parkourRecordExpansion.register()) { + log(Level.WARNING, "Unable to register PlaceholderAPI parkour expansion!"); } } } diff --git a/src/main/java/net/knarcraft/minigames/placeholder/DropperRecordExpansion.java b/src/main/java/net/knarcraft/minigames/placeholder/DropperRecordExpansion.java index 00b5ed2..3357f68 100644 --- a/src/main/java/net/knarcraft/minigames/placeholder/DropperRecordExpansion.java +++ b/src/main/java/net/knarcraft/minigames/placeholder/DropperRecordExpansion.java @@ -1,43 +1,14 @@ package net.knarcraft.minigames.placeholder; -import me.clip.placeholderapi.expansion.PlaceholderExpansion; import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.ArenaGameMode; -import net.knarcraft.minigames.arena.ArenaRecordsRegistry; -import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode; -import net.knarcraft.minigames.arena.dropper.DropperArenaGroup; -import net.knarcraft.minigames.arena.dropper.DropperArenaHandler; -import net.knarcraft.minigames.arena.dropper.DropperArenaRecordsRegistry; -import net.knarcraft.minigames.arena.record.ArenaRecord; -import net.knarcraft.minigames.placeholder.parsing.InfoType; -import net.knarcraft.minigames.placeholder.parsing.SelectionType; -import net.knarcraft.minigames.property.RecordType; -import net.knarcraft.minigames.util.DropperGroupRecordHelper; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; -import java.util.function.Supplier; /** * A placeholder expansion for dropper record placeholders */ -public class DropperRecordExpansion extends PlaceholderExpansion { - - private final MiniGames plugin; - private final Map>> groupRecordDeathsCache; - private final Map>> groupRecordTimeCache; +public class DropperRecordExpansion extends RecordExpansion { /** * Initializes a new record expansion @@ -45,9 +16,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion { * @param plugin

A reference to the dropper plugin

*/ public DropperRecordExpansion(MiniGames plugin) { - this.plugin = plugin; - this.groupRecordDeathsCache = new HashMap<>(); - this.groupRecordTimeCache = new HashMap<>(); + super(plugin.getDropperArenaHandler()); } @Override @@ -56,280 +25,8 @@ public class DropperRecordExpansion extends PlaceholderExpansion { } @Override - public String getAuthor() { - return "EpicKnarvik97"; - } - - @Override - public String getVersion() { - return "1.0.0"; - } - - @Override - public boolean persist() { - return true; - } - - @Override - public String onRequest(OfflinePlayer player, String parameters) { - String[] parts = parameters.split("_"); - // Record is used as the prefix for all record placeholders in case more placeholder types are added - if (parts.length < 7 || !parts[0].equals("record")) { - return parameters; - } - RecordType recordType = RecordType.getFromString(parts[1]); - DropperArenaGameMode gameMode = DropperArenaGameMode.matchGamemode(parts[2]); - SelectionType selectionType = SelectionType.getFromString(parts[3]); - String identifier = parts[4]; - int recordNumber = Integer.parseInt(parts[5]) - 1; - InfoType infoType = InfoType.getFromString(parts[6]); - - if (recordType == null || infoType == null) { - return parameters; - } - - String info = null; - DropperArenaHandler arenaHandler = plugin.getDropperArenaHandler(); - if (selectionType == SelectionType.GROUP) { - info = getGroupRecord(arenaHandler, identifier, gameMode, recordType, recordNumber, infoType); - } else if (selectionType == SelectionType.ARENA) { - info = getArenaRecord(arenaHandler, identifier, gameMode, recordType, recordNumber, infoType); - } - - return Objects.requireNonNullElse(info, parameters); - } - - /** - * Clears all record caches - */ - public void clearCaches() { - this.groupRecordDeathsCache.clear(); - this.groupRecordTimeCache.clear(); - } - - /** - * Gets a piece of record information from a dropper arena group - * - * @param arenaHandler

The arena handler to get the group from

- * @param identifier

The identifier (name/uuid) selecting the group

- * @param gameMode

The game-mode to get a record for

- * @param recordType

The type of record to get

- * @param recordNumber

The placing of the record to get (1st place, 2nd place, etc.)

- * @param infoType

The type of info (player, value, combined) to get

- * @return

The selected information about the record, or null if not found

- */ - private @Nullable String getGroupRecord(@NotNull DropperArenaHandler arenaHandler, @NotNull String identifier, - @NotNull DropperArenaGameMode gameMode, @NotNull RecordType recordType, - int recordNumber, @NotNull InfoType infoType) { - // Allow specifying the group UUID or the arena name - DropperArenaGroup group; - try { - group = arenaHandler.getGroup(UUID.fromString(identifier)); - } catch (IllegalArgumentException exception) { - group = arenaHandler.getGroup(identifier); - } - if (group == null) { - return null; - } - - ArenaRecord record; - if (recordType == RecordType.DEATHS) { - record = getGroupDeathRecord(group, gameMode, recordNumber); - } else { - record = getGroupTimeRecord(group, gameMode, recordNumber); - } - - // If a record number is not found, leave it blank, so it looks neat - if (record == null) { - return ""; - } - - return getRecordData(infoType, record); - } - - /** - * Gets a time record from a group, using the cache if possible - * - * @param group

The group to get the record from

- * @param gameMode

The game-mode to get the record from

- * @param recordNumber

The placing of the record to get (1st place, 2nd place, etc.)

- * @return

The record, or null if not found

- */ - private @Nullable ArenaRecord getGroupTimeRecord(@NotNull DropperArenaGroup group, - @NotNull DropperArenaGameMode gameMode, int recordNumber) { - return getCachedGroupRecord(group, gameMode, RecordType.TIME, recordNumber, groupRecordTimeCache, - () -> DropperGroupRecordHelper.getCombinedTime(group, gameMode)); - } - - /** - * Gets a death record from a group, using the cache if possible - * - * @param group

The group to get the record from

- * @param gameMode

The game-mode to get the record from

- * @param recordNumber

The placing of the record to get (1st place, 2nd place, etc.)

- * @return

The record, or null if not found

- */ - private @Nullable ArenaRecord getGroupDeathRecord(@NotNull DropperArenaGroup group, - @NotNull DropperArenaGameMode gameMode, int recordNumber) { - return getCachedGroupRecord(group, gameMode, RecordType.DEATHS, recordNumber, groupRecordDeathsCache, - () -> DropperGroupRecordHelper.getCombinedDeaths(group, gameMode)); - } - - /** - * Gets a group record, fetching from a cache if possible - * - * @param group

The group to get the record for

- * @param gameMode

The game-mode to get the record for

- * @param recordType

The type of record to get

- * @param recordNumber

The placing of the record to get (1st place, 2nd place, etc.)

- * @param caches

The caches to use for looking for and saving the record

- * @param recordProvider

The provider of records if the cache cannot provide the record

- * @param

The type of the provided records

- * @return

The specified record, or null if not found

- */ - private > @Nullable ArenaRecord getCachedGroupRecord(@NotNull DropperArenaGroup group, - @NotNull DropperArenaGameMode gameMode, - @NotNull RecordType recordType, - int recordNumber, - @NotNull Map>> caches, - @NotNull Supplier>> recordProvider) { - UUID groupId = group.getGroupId(); - if (!caches.containsKey(groupId)) { - caches.put(groupId, new HashSet<>()); - } - - Set> existingCaches = caches.get(groupId); - Set> expired = new HashSet<>(); - Set> cachedRecords = null; - - for (GroupRecordCache cache : existingCaches) { - // Expire caches after 30 seconds - if (System.currentTimeMillis() - cache.createdTime() > 30000) { - expired.add(cache); - } - // If of the correct type, and not expired, use the cache - if (cache.gameMode() == gameMode && cache.recordType() == recordType) { - cachedRecords = cache.records(); - break; - } - } - existingCaches.removeAll(expired); - - // If not found, generate and cache the specified record - if (cachedRecords == null) { - cachedRecords = recordProvider.get(); - existingCaches.add(new GroupRecordCache<>(gameMode, recordType, cachedRecords, System.currentTimeMillis())); - } - return getRecord(cachedRecords, recordNumber); - } - - /** - * Gets a piece of record information from a dropper arena - * - * @param arenaHandler

The arena handler to get the arena from

- * @param identifier

The identifier (name/uuid) selecting the arena

- * @param gameMode

The game-mode to get a record for

- * @param recordType

The type of record to get

- * @param recordNumber

The placing of the record to get (1st place, 2nd place, etc.)

- * @param infoType

The type of info (player, value, combined) to get

- * @return

The selected information about the record, or null if not found

- */ - private @Nullable String getArenaRecord(@NotNull DropperArenaHandler arenaHandler, @NotNull String identifier, - @NotNull DropperArenaGameMode gameMode, @NotNull RecordType recordType, - int recordNumber, @NotNull InfoType infoType) { - // Allow specifying the arena UUID or the arena name - DropperArena arena; - try { - arena = arenaHandler.getArena(UUID.fromString(identifier)); - } catch (IllegalArgumentException exception) { - arena = arenaHandler.getArena(identifier); - } - if (arena == null) { - return null; - } - @NotNull Map registries = arena.getData().getRecordRegistries(); - ArenaRecordsRegistry recordsRegistry = registries.get(gameMode); - - ArenaRecord record = getRecord((DropperArenaRecordsRegistry) recordsRegistry, recordType, recordNumber); - - // If a record number is not found, leave it blank, so it looks neat - if (record == null) { - return ""; - } - - return getRecordData(infoType, record); - } - - /** - * Gets the specified record - * - * @param recordsRegistry

The records registry to get the record from

- * @param recordType

The type of record to get

- * @param recordNumber

The placing of the record to get (1st place, 2nd place, etc.)

- * @return

The record, or null if not found

- */ - private @Nullable ArenaRecord getRecord(@NotNull DropperArenaRecordsRegistry recordsRegistry, - @NotNull RecordType recordType, int recordNumber) { - return switch (recordType) { - case TIME -> getRecord(new HashSet<>(recordsRegistry.getShortestTimeMilliSecondsRecords()), recordNumber); - case DEATHS -> getRecord(new HashSet<>(recordsRegistry.getLeastDeathsRecords()), recordNumber); - }; - } - - /** - * Gets the record at the given index - * - * @param records

The records to search through

- * @param index

The index of the record to get

- * @param

The type of record in the record list

- * @return

The record, or null if index is out of bounds

- */ - private > @Nullable ArenaRecord getRecord(Set> records, int index) { - List> sorted = getSortedRecords(records); - if (index < sorted.size() && index >= 0) { - return sorted.get(index); - } else { - return null; - } - } - - /** - * Gets a piece of data from a record as a string - * - * @param infoType

The type of info to get data for

- * @param arenaRecord

The record to get the data from

- * @return

The requested data as a string, or null

- */ - private String getRecordData(@NotNull InfoType infoType, @NotNull ArenaRecord arenaRecord) { - return switch (infoType) { - case PLAYER -> getPlayerName(arenaRecord.getUserId()); - case VALUE -> arenaRecord.getRecord().toString(); - case COMBINED -> getPlayerName(arenaRecord.getUserId()) + ": " + arenaRecord.getRecord().toString(); - }; - } - - /** - * Gets the given set of records as a sorted list - * - * @param recordSet

The set of records to sort

- * @param

The type of the records

- * @return

The sorted records

- */ - private > @NotNull List> getSortedRecords( - @NotNull Set> recordSet) { - List> records = new ArrayList<>(recordSet); - Collections.sort(records); - return records; - } - - /** - * Gets the name of a player, given the player's UUID - * - * @param playerId

The id of the player to get the name for

- * @return

The name of the player, or a string representation of the UUID if not found

- */ - private String getPlayerName(@NotNull UUID playerId) { - return Bukkit.getOfflinePlayer(playerId).getName(); + protected @NotNull ArenaGameMode parseGameMode(@NotNull String gameMode) { + return DropperArenaGameMode.matchGamemode(gameMode); } } diff --git a/src/main/java/net/knarcraft/minigames/placeholder/GroupRecordCache.java b/src/main/java/net/knarcraft/minigames/placeholder/GroupRecordCache.java index e1f36b2..872ebe2 100644 --- a/src/main/java/net/knarcraft/minigames/placeholder/GroupRecordCache.java +++ b/src/main/java/net/knarcraft/minigames/placeholder/GroupRecordCache.java @@ -1,6 +1,6 @@ package net.knarcraft.minigames.placeholder; -import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode; +import net.knarcraft.minigames.arena.ArenaGameMode; import net.knarcraft.minigames.arena.record.ArenaRecord; import net.knarcraft.minigames.property.RecordType; import org.jetbrains.annotations.NotNull; @@ -15,7 +15,7 @@ import java.util.Set; * @param records

The stored records

* @param createdTime

The time this cache was created

*/ -public record GroupRecordCache>(@NotNull DropperArenaGameMode gameMode, +public record GroupRecordCache>(@NotNull ArenaGameMode gameMode, @NotNull RecordType recordType, @NotNull Set> records, @NotNull Long createdTime) { diff --git a/src/main/java/net/knarcraft/minigames/placeholder/ParkourRecordExpansion.java b/src/main/java/net/knarcraft/minigames/placeholder/ParkourRecordExpansion.java new file mode 100644 index 0000000..11fdcbe --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/placeholder/ParkourRecordExpansion.java @@ -0,0 +1,32 @@ +package net.knarcraft.minigames.placeholder; + +import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.arena.ArenaGameMode; +import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode; +import org.jetbrains.annotations.NotNull; + +/** + * A placeholder expansion for parkour record placeholders + */ +public class ParkourRecordExpansion extends RecordExpansion { + + /** + * Initializes a new record expansion + * + * @param plugin

A reference to the dropper plugin

+ */ + public ParkourRecordExpansion(MiniGames plugin) { + super(plugin.getParkourArenaHandler()); + } + + @Override + public String getIdentifier() { + return "parkour"; + } + + @Override + protected @NotNull ArenaGameMode parseGameMode(@NotNull String gameMode) { + return ParkourArenaGameMode.matchGamemode(gameMode); + } + +} diff --git a/src/main/java/net/knarcraft/minigames/placeholder/RecordExpansion.java b/src/main/java/net/knarcraft/minigames/placeholder/RecordExpansion.java new file mode 100644 index 0000000..9dca66c --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/placeholder/RecordExpansion.java @@ -0,0 +1,336 @@ +package net.knarcraft.minigames.placeholder; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import net.knarcraft.minigames.arena.Arena; +import net.knarcraft.minigames.arena.ArenaGameMode; +import net.knarcraft.minigames.arena.ArenaGroup; +import net.knarcraft.minigames.arena.ArenaHandler; +import net.knarcraft.minigames.arena.ArenaRecordsRegistry; +import net.knarcraft.minigames.arena.record.ArenaRecord; +import net.knarcraft.minigames.placeholder.parsing.InfoType; +import net.knarcraft.minigames.placeholder.parsing.SelectionType; +import net.knarcraft.minigames.property.RecordType; +import net.knarcraft.minigames.util.GroupRecordHelper; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.function.Supplier; + +/** + * A placeholder expansion for parkour record placeholders + */ +public abstract class RecordExpansion extends PlaceholderExpansion { + + private final ArenaHandler arenaHandler; + private final Map>> groupRecordDeathsCache; + private final Map>> groupRecordTimeCache; + + /** + * Initializes a new record expansion + */ + public RecordExpansion(ArenaHandler arenaHandler) { + this.groupRecordDeathsCache = new HashMap<>(); + this.groupRecordTimeCache = new HashMap<>(); + this.arenaHandler = arenaHandler; + } + + @Override + public String getAuthor() { + return "EpicKnarvik97"; + } + + @Override + public String getVersion() { + return "1.0.0"; + } + + @Override + public boolean persist() { + return true; + } + + @Override + public String onRequest(OfflinePlayer player, String parameters) { + String[] parts = parameters.split("_"); + // Record is used as the prefix for all record placeholders in case more placeholder types are added + if (parts.length < 7 || !parts[0].equals("record")) { + return parameters; + } + RecordType recordType = RecordType.getFromString(parts[1]); + ArenaGameMode gameMode = parseGameMode(parts[2]); + SelectionType selectionType = SelectionType.getFromString(parts[3]); + String identifier = parts[4]; + int recordNumber = Integer.parseInt(parts[5]) - 1; + InfoType infoType = InfoType.getFromString(parts[6]); + + if (recordType == null || infoType == null) { + return parameters; + } + + String info = null; + if (selectionType == SelectionType.GROUP) { + info = getGroupRecord(arenaHandler, identifier, gameMode, recordType, recordNumber, infoType); + } else if (selectionType == SelectionType.ARENA) { + info = getArenaRecord(arenaHandler, identifier, gameMode, recordType, recordNumber, infoType); + } + + return Objects.requireNonNullElse(info, parameters); + } + + /** + * Parses the game-mode specified in the given string + * + * @param gameMode

The game-mode to parse

+ * @return

The parsed game-mode

+ */ + protected abstract @NotNull ArenaGameMode parseGameMode(@NotNull String gameMode); + + /** + * Clears all record caches + */ + public void clearCaches() { + this.groupRecordDeathsCache.clear(); + this.groupRecordTimeCache.clear(); + } + + /** + * Gets a piece of record information from an arena group + * + * @param arenaHandler

The arena handler to get the group from

+ * @param identifier

The identifier (name/uuid) selecting the group

+ * @param gameMode

The game-mode to get a record for

+ * @param recordType

The type of record to get

+ * @param recordNumber

The placing of the record to get (1st place, 2nd place, etc.)

+ * @param infoType

The type of info (player, value, combined) to get

+ * @return

The selected information about the record, or null if not found

+ */ + private @Nullable String getGroupRecord(@NotNull ArenaHandler arenaHandler, @NotNull String identifier, + @NotNull ArenaGameMode gameMode, @NotNull RecordType recordType, + int recordNumber, @NotNull InfoType infoType) { + // Allow specifying the group UUID or the arena name + ArenaGroup group; + try { + group = arenaHandler.getGroup(UUID.fromString(identifier)); + } catch (IllegalArgumentException exception) { + group = arenaHandler.getGroup(identifier); + } + if (group == null) { + return null; + } + + ArenaRecord record; + if (recordType == RecordType.DEATHS) { + record = getGroupDeathRecord(group, gameMode, recordNumber, arenaHandler); + } else { + record = getGroupTimeRecord(group, gameMode, recordNumber, arenaHandler); + } + + // If a record number is not found, leave it blank, so it looks neat + if (record == null) { + return ""; + } + + return getRecordData(infoType, record); + } + + /** + * Gets a time record from a group, using the cache if possible + * + * @param group

The group to get the record from

+ * @param gameMode

The game-mode to get the record from

+ * @param recordNumber

The placing of the record to get (1st place, 2nd place, etc.)

+ * @param arenaHandler

The handler to get arenas from

+ * @return

The record, or null if not found

+ */ + private @Nullable ArenaRecord getGroupTimeRecord(@NotNull ArenaGroup group, + @NotNull ArenaGameMode gameMode, int recordNumber, + @NotNull ArenaHandler arenaHandler) { + return getCachedGroupRecord(group, gameMode, RecordType.TIME, recordNumber, groupRecordTimeCache, + () -> GroupRecordHelper.getCombinedTime(group, gameMode, arenaHandler)); + } + + /** + * Gets a death record from a group, using the cache if possible + * + * @param group

The group to get the record from

+ * @param gameMode

The game-mode to get the record from

+ * @param recordNumber

The placing of the record to get (1st place, 2nd place, etc.)

+ * @param arenaHandler

The handler to get arenas from

+ * @return

The record, or null if not found

+ */ + private @Nullable ArenaRecord getGroupDeathRecord(@NotNull ArenaGroup group, + @NotNull ArenaGameMode gameMode, int recordNumber, + @NotNull ArenaHandler arenaHandler) { + return getCachedGroupRecord(group, gameMode, RecordType.DEATHS, recordNumber, groupRecordDeathsCache, + () -> GroupRecordHelper.getCombinedDeaths(group, gameMode, arenaHandler)); + } + + /** + * Gets a group record, fetching from a cache if possible + * + * @param group

The group to get the record for

+ * @param gameMode

The game-mode to get the record for

+ * @param recordType

The type of record to get

+ * @param recordNumber

The placing of the record to get (1st place, 2nd place, etc.)

+ * @param caches

The caches to use for looking for and saving the record

+ * @param recordProvider

The provider of records if the cache cannot provide the record

+ * @param

The type of the provided records

+ * @return

The specified record, or null if not found

+ */ + private > @Nullable ArenaRecord getCachedGroupRecord(@NotNull ArenaGroup group, + @NotNull ArenaGameMode gameMode, + @NotNull RecordType recordType, + int recordNumber, + @NotNull Map>> caches, + @NotNull Supplier>> recordProvider) { + UUID groupId = group.getGroupId(); + if (!caches.containsKey(groupId)) { + caches.put(groupId, new HashSet<>()); + } + + Set> existingCaches = caches.get(groupId); + Set> expired = new HashSet<>(); + Set> cachedRecords = null; + + for (GroupRecordCache cache : existingCaches) { + // Expire caches after 30 seconds + if (System.currentTimeMillis() - cache.createdTime() > 30000) { + expired.add(cache); + } + // If of the correct type, and not expired, use the cache + if (cache.gameMode() == gameMode && cache.recordType() == recordType) { + cachedRecords = cache.records(); + break; + } + } + existingCaches.removeAll(expired); + + // If not found, generate and cache the specified record + if (cachedRecords == null) { + cachedRecords = recordProvider.get(); + existingCaches.add(new GroupRecordCache<>(gameMode, recordType, cachedRecords, System.currentTimeMillis())); + } + return getRecord(cachedRecords, recordNumber); + } + + /** + * Gets a piece of record information from an arena + * + * @param arenaHandler

The arena handler to get the arena from

+ * @param identifier

The identifier (name/uuid) selecting the arena

+ * @param gameMode

The game-mode to get a record for

+ * @param recordType

The type of record to get

+ * @param recordNumber

The placing of the record to get (1st place, 2nd place, etc.)

+ * @param infoType

The type of info (player, value, combined) to get

+ * @return

The selected information about the record, or null if not found

+ */ + private @Nullable String getArenaRecord(@NotNull ArenaHandler arenaHandler, @NotNull String identifier, + @NotNull ArenaGameMode gameMode, @NotNull RecordType recordType, + int recordNumber, @NotNull InfoType infoType) { + // Allow specifying the arena UUID or the arena name + Arena arena; + try { + arena = arenaHandler.getArena(UUID.fromString(identifier)); + } catch (IllegalArgumentException exception) { + arena = arenaHandler.getArena(identifier); + } + if (arena == null) { + return null; + } + @NotNull Map registries = arena.getData().getRecordRegistries(); + ArenaRecordsRegistry recordsRegistry = registries.get(gameMode); + + ArenaRecord record = getRecord(recordsRegistry, recordType, recordNumber); + + // If a record number is not found, leave it blank, so it looks neat + if (record == null) { + return ""; + } + + return getRecordData(infoType, record); + } + + /** + * Gets the specified record + * + * @param recordsRegistry

The records registry to get the record from

+ * @param recordType

The type of record to get

+ * @param recordNumber

The placing of the record to get (1st place, 2nd place, etc.)

+ * @return

The record, or null if not found

+ */ + private @Nullable ArenaRecord getRecord(@NotNull ArenaRecordsRegistry recordsRegistry, + @NotNull RecordType recordType, int recordNumber) { + return switch (recordType) { + case TIME -> getRecord(new HashSet<>(recordsRegistry.getShortestTimeMilliSecondsRecords()), recordNumber); + case DEATHS -> getRecord(new HashSet<>(recordsRegistry.getLeastDeathsRecords()), recordNumber); + }; + } + + /** + * Gets the record at the given index + * + * @param records

The records to search through

+ * @param index

The index of the record to get

+ * @param

The type of record in the record list

+ * @return

The record, or null if index is out of bounds

+ */ + private > @Nullable ArenaRecord getRecord(Set> records, int index) { + List> sorted = getSortedRecords(records); + if (index < sorted.size() && index >= 0) { + return sorted.get(index); + } else { + return null; + } + } + + /** + * Gets a piece of data from a record as a string + * + * @param infoType

The type of info to get data for

+ * @param arenaRecord

The record to get the data from

+ * @return

The requested data as a string, or null

+ */ + private String getRecordData(@NotNull InfoType infoType, @NotNull ArenaRecord arenaRecord) { + return switch (infoType) { + case PLAYER -> getPlayerName(arenaRecord.getUserId()); + case VALUE -> arenaRecord.getRecord().toString(); + case COMBINED -> getPlayerName(arenaRecord.getUserId()) + ": " + arenaRecord.getRecord().toString(); + }; + } + + /** + * Gets the given set of records as a sorted list + * + * @param recordSet

The set of records to sort

+ * @param

The type of the records

+ * @return

The sorted records

+ */ + private > @NotNull List> getSortedRecords( + @NotNull Set> recordSet) { + List> records = new ArrayList<>(recordSet); + Collections.sort(records); + return records; + } + + /** + * Gets the name of a player, given the player's UUID + * + * @param playerId

The id of the player to get the name for

+ * @return

The name of the player, or a string representation of the UUID if not found

+ */ + private String getPlayerName(@NotNull UUID playerId) { + return Bukkit.getOfflinePlayer(playerId).getName(); + } + +} diff --git a/src/main/java/net/knarcraft/minigames/util/DropperGroupRecordHelper.java b/src/main/java/net/knarcraft/minigames/util/GroupRecordHelper.java similarity index 75% rename from src/main/java/net/knarcraft/minigames/util/DropperGroupRecordHelper.java rename to src/main/java/net/knarcraft/minigames/util/GroupRecordHelper.java index bedd001..4c14e74 100644 --- a/src/main/java/net/knarcraft/minigames/util/DropperGroupRecordHelper.java +++ b/src/main/java/net/knarcraft/minigames/util/GroupRecordHelper.java @@ -1,10 +1,9 @@ package net.knarcraft.minigames.util; -import net.knarcraft.minigames.MiniGames; -import net.knarcraft.minigames.arena.dropper.DropperArena; -import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode; -import net.knarcraft.minigames.arena.dropper.DropperArenaGroup; -import net.knarcraft.minigames.arena.dropper.DropperArenaHandler; +import net.knarcraft.minigames.arena.Arena; +import net.knarcraft.minigames.arena.ArenaGameMode; +import net.knarcraft.minigames.arena.ArenaGroup; +import net.knarcraft.minigames.arena.ArenaHandler; import net.knarcraft.minigames.arena.record.ArenaRecord; import net.knarcraft.minigames.arena.record.SummableArenaRecord; import org.jetbrains.annotations.NotNull; @@ -19,42 +18,46 @@ import java.util.function.BiFunction; /** * A helper class for getting combined record data for a dropper group */ -public final class DropperGroupRecordHelper { +public final class GroupRecordHelper { - private DropperGroupRecordHelper() { + private GroupRecordHelper() { } /** * Gets the combined least-death records for the given group and game-mode * - * @param group

The group to get records from

- * @param gameMode

The game-mode to get records for

+ * @param group

The group to get records from

+ * @param gameMode

The game-mode to get records for

+ * @param arenaHandler

The handler to get arenas from

* @return

The combined death records

*/ - public static @NotNull Set> getCombinedDeaths(@NotNull DropperArenaGroup group, - @NotNull DropperArenaGameMode gameMode) { + public static @NotNull Set> getCombinedDeaths(@NotNull ArenaGroup group, + @NotNull ArenaGameMode gameMode, + @NotNull ArenaHandler arenaHandler) { Map> records = new HashMap<>(); - @NotNull BiFunction>> recordSupplier = + @NotNull BiFunction>> recordSupplier = (arena, aGameMode) -> arena.getData().getRecordRegistries().get(gameMode).getLeastDeathsRecords(); - return getCombined(group, gameMode, records, recordSupplier); + return getCombined(group, gameMode, records, recordSupplier, arenaHandler); } /** * Gets the combined least-time records for the given group and game-mode * - * @param group

The group to get records from

- * @param gameMode

The game-mode to get records for

+ * @param group

The group to get records from

+ * @param gameMode

The game-mode to get records for

+ * @param arenaHandler

The handler to get arenas from

* @return

The combined least-time records

*/ - public static @NotNull Set> getCombinedTime(@NotNull DropperArenaGroup group, - @NotNull DropperArenaGameMode gameMode) { + public static @NotNull Set> getCombinedTime(@NotNull ArenaGroup group, + @NotNull ArenaGameMode gameMode, + @NotNull ArenaHandler arenaHandler) { Map> records = new HashMap<>(); - @NotNull BiFunction>> recordSupplier = + @NotNull BiFunction>> recordSupplier = (arena, aGameMode) -> arena.getData().getRecordRegistries().get(gameMode).getShortestTimeMilliSecondsRecords(); - return getCombined(group, gameMode, records, recordSupplier); + return getCombined(group, gameMode, records, recordSupplier, arenaHandler); } /** @@ -64,20 +67,20 @@ public final class DropperGroupRecordHelper { * @param gameMode

The game-mode to get records for

* @param records

The map to store the combined records to

* @param recordSupplier

The function that supplies records of this type

+ * @param arenaHandler

The handler to get arenas from

* @param

The type of the records to combine

* @return

The combined records

*/ - private static > @NotNull Set> getCombined(@NotNull DropperArenaGroup group, - @NotNull DropperArenaGameMode gameMode, + private static > @NotNull Set> getCombined(@NotNull ArenaGroup group, + @NotNull ArenaGameMode gameMode, @NotNull Map> records, - @NotNull BiFunction>> recordSupplier) { - DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler(); - + @NotNull BiFunction>> recordSupplier, + @NotNull ArenaHandler arenaHandler) { // Get all arenas in the group - Set arenas = getArenas(arenaHandler, group); + Set arenas = getArenas(arenaHandler, group); // Calculate the combined records Map recordsFound = new HashMap<>(); @@ -113,12 +116,12 @@ public final class DropperGroupRecordHelper { * @param group

The group to get arenas for

* @return

The arenas found in the group

*/ - private static @NotNull Set getArenas(@NotNull DropperArenaHandler arenaHandler, - @NotNull DropperArenaGroup group) { + private static @NotNull Set getArenas(@NotNull ArenaHandler arenaHandler, + @NotNull ArenaGroup group) { // Get all arenas in the group - Set arenas = new HashSet<>(); + Set arenas = new HashSet<>(); for (UUID arenaId : group.getArenas()) { - DropperArena arena = arenaHandler.getArena(arenaId); + Arena arena = arenaHandler.getArena(arenaId); if (arena != null) { arenas.add(arena); } @@ -136,14 +139,14 @@ public final class DropperGroupRecordHelper { * @param recordSupplier

The function that supplies record data of this type

* @param

The type of record to combine

*/ - private static > void combineRecords(@NotNull Set arenas, - @NotNull DropperArenaGameMode gameMode, + private static > void combineRecords(@NotNull Set arenas, + @NotNull ArenaGameMode gameMode, @NotNull Map> combinedRecords, @NotNull Map recordsFound, - @NotNull BiFunction>> recordSupplier) { - for (DropperArena arena : arenas) { + for (Arena arena : arenas) { Set> existingRecords = recordSupplier.apply(arena, gameMode); // For each arena's record registry, calculate the combined records for (SummableArenaRecord value : existingRecords) { diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index b5a6fab..f81f447 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,4 +1,4 @@ -name: Dropper +name: MiniGames version: '${project.version}' main: net.knarcraft.minigames.MiniGames api-version: 1.19