Implements #35

This commit is contained in:
Kristian Knarvik 2024-04-28 16:21:54 +02:00
parent 3a68d30044
commit 87788e60a3
13 changed files with 566 additions and 151 deletions

View File

@ -282,7 +282,9 @@ These are all the options that can be changed for an arena.
- LIGHTNING_ROD
- CHAIN
## Record placeholders
## Placeholders
### Record Placeholders
Player records can be displayed on a leaderboard by using PlaceholderAPI. If you want to display a sign-based
leaderboard, you can use the [Placeholder Signs](https://git.knarcraft.net/EpicKnarvik97/PlaceholderSigns) plugin. The
@ -290,16 +292,34 @@ format for the built-in placeholders is as follows:
`%gameMode_record_recordType_gameModeType_identifierType_identifier_recordPosition_infoType%`
| Variable | Values | Description |
|----------------|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------|
| gameMode | dropper / parkour | A selection of which game-mode you are getting a record for |
| record | | This must be as-is. It's a selector in case placeholders are added for more than records. |
| recordType | deaths / time | Selects the type of record to get (deaths or time). |
| gameModeType | default / inverted / random | Selects the game-mode to get the record for. |
| identifierType | arena / group | The type of thing the following identifier points to (an arena or an arena group). |
| identifier | ? | An identifier (the name or UUID) for an arena or a group (whichever was chosen as identifierType). |
| recordPosition | 1 / 2 / 3 / ... | The position of the record to get (1 = first place, 2 = second place, etc.). |
| infoType | player / value / combined | The type of info to get. Player gets the player name, Value gets the value of the achieved record. Combined gets "Player: Record". |
| Variable | Values | Description |
|----------------|----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|
| gameMode | dropper / parkour | A selection of which game-mode you are getting a record for |
| record | | This must be as-is. It's a selector for the type of placeholder to get. |
| recordType | deaths / time | Selects the type of record to get (deaths or time). |
| gameModeType | default / inverted / random / hardcore | Selects the game-mode to get the record for. |
| identifierType | arena / group | The type of thing the following identifier points to (an arena or an arena group). |
| identifier | ? | An identifier (the name or UUID) for an arena or a group (whichever was chosen as identifierType). |
| recordPosition | 1 / 2 / 3 / ... | The position of the record to get (1 = first place, 2 = second place, etc.). |
| infoType | player / value / combined | The type of info to get. Player gets the player name, Value gets the value of the achieved record. Combined gets "Player: Record". |
### Player Placeholders
The number of currently playing players can be displayed using PlaceholderAPI. If you want to display a sign-based
leaderboard, you can use the [Placeholder Signs](https://git.knarcraft.net/EpicKnarvik97/PlaceholderSigns) plugin. The
format for the built-in placeholders is as follows:
`%gameMode_players_playing_gameModeType_identifierType_identifier_infoType_additionalSpecifier%`
| Variable | Values | Description |
|-----------------|----------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
| gameMode | dropper / parkour | A selection of which game-mode you are getting a record for |
| players_playing | | This must be as-is. It's a selector in case placeholders are added for more than playing players. |
| gameModeType | default / inverted / random / hardcore / all | Selects the game-mode to get the players for. Note that "all" combines players for all game-modes. |
| identifierType | arena / group | The type of thing the following identifier points to (an arena or an arena group). |
| identifier | ? | An identifier (the name or UUID) for an arena or a group (whichever was chosen as identifierType). |
| playerInfoType | player / count | The type of info to get. Player gets the player name, and count gets the total number of players. |
| playerNumber | 1 / 2 / 3 / ... | The player number to get the name of. Players are numbered after their sorted names. This argument is not used when getting the player count. |
## Notes about material tags

View File

@ -70,8 +70,8 @@ import net.knarcraft.minigames.listener.MoveListener;
import net.knarcraft.minigames.listener.PlayerStateChangeListener;
import net.knarcraft.minigames.manager.EconomyManager;
import net.knarcraft.minigames.manager.PermissionManager;
import net.knarcraft.minigames.placeholder.DropperRecordExpansion;
import net.knarcraft.minigames.placeholder.ParkourRecordExpansion;
import net.knarcraft.minigames.placeholder.DropperExpansion;
import net.knarcraft.minigames.placeholder.ParkourExpansion;
import net.md_5.bungee.api.ChatColor;
import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.permission.Permission;
@ -102,8 +102,8 @@ public final class MiniGames extends JavaPlugin {
private ParkourConfiguration parkourConfiguration;
private DropperArenaHandler dropperArenaHandler;
private ArenaPlayerRegistry<DropperArena> dropperArenaPlayerRegistry;
private DropperRecordExpansion dropperRecordExpansion;
private ParkourRecordExpansion parkourRecordExpansion;
private DropperExpansion dropperExpansion;
private ParkourExpansion parkourExpansion;
private ParkourArenaHandler parkourArenaHandler;
private ArenaPlayerRegistry<ParkourArena> parkourArenaPlayerRegistry;
private PlayerVisibilityManager playerVisibilityManager;
@ -253,8 +253,8 @@ public final class MiniGames extends JavaPlugin {
this.parkourConfiguration.load(this.getConfig());
// Clear record caches
this.dropperRecordExpansion.clearCaches();
this.parkourRecordExpansion.clearCaches();
this.dropperExpansion.clearCaches();
this.parkourExpansion.clearCaches();
}
@Override
@ -316,12 +316,12 @@ public final class MiniGames extends JavaPlugin {
*/
private void doPluginIntegration() {
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
this.dropperRecordExpansion = new DropperRecordExpansion(this);
if (!this.dropperRecordExpansion.register()) {
this.dropperExpansion = new DropperExpansion(this);
if (!this.dropperExpansion.register()) {
log(Level.WARNING, "Unable to register PlaceholderAPI dropper expansion!");
}
this.parkourRecordExpansion = new ParkourRecordExpansion(this);
if (!this.parkourRecordExpansion.register()) {
this.parkourExpansion = new ParkourExpansion(this);
if (!this.parkourExpansion.register()) {
log(Level.WARNING, "Unable to register PlaceholderAPI parkour expansion!");
}
}

View File

@ -52,4 +52,11 @@ public interface ArenaSession {
*/
void reset();
/**
* Gets the game-mode the player is playing
*
* @return <p>The game-mode the player is playing</p>
*/
@NotNull ArenaGameMode getGameMode();
}

View File

@ -123,6 +123,10 @@ public class ParkourArenaSession extends AbstractArenaSession {
// Teleport the player out of the arena
teleportToExit(false);
if (MiniGames.getInstance().getParkourArenaPlayerRegistry().getPlayingPlayers(this.arena).isEmpty()) {
resetLevers();
}
}
@Override

View File

@ -0,0 +1,73 @@
package net.knarcraft.minigames.placeholder;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
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.DropperArenaHandler;
import net.knarcraft.minigames.placeholder.parsing.PlayerPlaceholderParser;
import net.knarcraft.minigames.placeholder.parsing.RecordPlaceholderParser;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
/**
* A placeholderAPI expansion for Dropper-related placeholders
*/
public class DropperExpansion extends PlaceholderExpansion {
private final @NotNull RecordPlaceholderParser recordPlaceholderParser;
private final @NotNull PlayerPlaceholderParser<DropperArena> playerPlaceholderParser;
/**
* Instantiates a new dropper expansion
*
* @param plugin <p>A reference to the mini-games plugin</p>
*/
public DropperExpansion(@NotNull MiniGames plugin) {
DropperArenaHandler arenaHandler = plugin.getDropperArenaHandler();
this.recordPlaceholderParser = new RecordPlaceholderParser(arenaHandler, DropperArenaGameMode::matchGameMode);
this.playerPlaceholderParser = new PlayerPlaceholderParser<>(arenaHandler, DropperArenaGameMode::matchGameMode,
plugin.getDropperArenaPlayerRegistry());
}
/**
* Clears record caches
*/
public void clearCaches() {
this.recordPlaceholderParser.clearCaches();
}
@Override
public String getIdentifier() {
return "dropper";
}
@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[0].equalsIgnoreCase("record") && parts.length >= 7) {
return recordPlaceholderParser.onRequest(parameters, parts);
} else if (parts[0].equalsIgnoreCase("players")) {
return this.playerPlaceholderParser.onRequest(parameters, parts);
} else {
return parameters;
}
}
}

View File

@ -1,32 +0,0 @@
package net.knarcraft.minigames.placeholder;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaGameMode;
import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
import org.jetbrains.annotations.NotNull;
/**
* A placeholder expansion for dropper record placeholders
*/
public class DropperRecordExpansion extends RecordExpansion {
/**
* Initializes a new record expansion
*
* @param plugin <p>A reference to the dropper plugin</p>
*/
public DropperRecordExpansion(MiniGames plugin) {
super(plugin.getDropperArenaHandler());
}
@Override
public String getIdentifier() {
return "dropper";
}
@Override
protected @NotNull ArenaGameMode parseGameMode(@NotNull String gameMode) {
return DropperArenaGameMode.matchGameMode(gameMode);
}
}

View File

@ -0,0 +1,73 @@
package net.knarcraft.minigames.placeholder;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.parkour.ParkourArena;
import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode;
import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
import net.knarcraft.minigames.placeholder.parsing.PlayerPlaceholderParser;
import net.knarcraft.minigames.placeholder.parsing.RecordPlaceholderParser;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
/**
* A placeholderAPI expansion for Parkour-related placeholders
*/
public class ParkourExpansion extends PlaceholderExpansion {
private final @NotNull RecordPlaceholderParser recordPlaceholderParser;
private final @NotNull PlayerPlaceholderParser<ParkourArena> playerPlaceholderParser;
/**
* Instantiates a new dropper expansion
*
* @param plugin <p>A reference to the mini-games plugin</p>
*/
public ParkourExpansion(@NotNull MiniGames plugin) {
ParkourArenaHandler arenaHandler = plugin.getParkourArenaHandler();
this.recordPlaceholderParser = new RecordPlaceholderParser(arenaHandler, ParkourArenaGameMode::matchGamemode);
this.playerPlaceholderParser = new PlayerPlaceholderParser<>(arenaHandler, ParkourArenaGameMode::matchGamemode,
plugin.getParkourArenaPlayerRegistry());
}
/**
* Clears record caches
*/
public void clearCaches() {
this.recordPlaceholderParser.clearCaches();
}
@Override
public String getIdentifier() {
return "parkour";
}
@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[0].equalsIgnoreCase("record") && parts.length >= 7) {
return recordPlaceholderParser.onRequest(parameters, parts);
} else if (parts[0].equalsIgnoreCase("players")) {
return this.playerPlaceholderParser.onRequest(parameters, parts);
} else {
return parameters;
}
}
}

View File

@ -1,32 +0,0 @@
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 <p>A reference to the dropper plugin</p>
*/
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);
}
}

View File

@ -0,0 +1,37 @@
package net.knarcraft.minigames.placeholder.parsing;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* The types of player information that can be retrieved in placeholders
*/
public enum PlayerInfoType {
/**
* The number of current players
*/
COUNT,
/**
* Information about a single player
*/
PLAYER,
;
/**
* Gets the info type specified in the given string
*
* @param type <p>The string specifying the info type</p>
* @return <p>The info type, or null if not found</p>
*/
public static @Nullable PlayerInfoType getFromString(@NotNull String type) {
for (PlayerInfoType infoType : PlayerInfoType.values()) {
if (infoType.name().equalsIgnoreCase(type)) {
return infoType;
}
}
return null;
}
}

View File

@ -0,0 +1,271 @@
package net.knarcraft.minigames.placeholder.parsing;
import net.knarcraft.minigames.MiniGames;
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.ArenaPlayerRegistry;
import net.knarcraft.minigames.arena.ArenaSession;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.logging.Level;
/**
* A parser for player-related placeholders
*/
public class PlayerPlaceholderParser<K extends Arena> {
private final @NotNull ArenaHandler<K, ?> arenaHandler;
private final @NotNull Function<String, ArenaGameMode> gameModeParser;
private final ArenaPlayerRegistry<K> playerRegistry;
/**
* Initializes a new player placeholder parser
*
* @param arenaHandler <p>The arena handler to get arenas from</p>
* @param gameModeParser <p>The function to use for parsing the specified game-mode</p>
* @param playerRegistry <p>The player registry to get player info from</p>
*/
public PlayerPlaceholderParser(@NotNull ArenaHandler<K, ?> arenaHandler,
@NotNull Function<String, ArenaGameMode> gameModeParser,
ArenaPlayerRegistry<K> playerRegistry) {
this.arenaHandler = arenaHandler;
this.gameModeParser = gameModeParser;
this.playerRegistry = playerRegistry;
}
/**
* The method to run when parsing a record placeholder request
*
* @param parameters <p>The parameters specified</p>
* @return <p>The resulting string</p>
*/
@NotNull
public String onRequest(@NotNull String parameters, @NotNull String[] parts) {
if (parts.length < 6) {
return parameters;
}
// String selector = parts[1]; // The selector for which aspect of players to get. Playing is the only one available yet.
String gameModeName = parts[2];
ArenaGameMode gameMode = gameModeParser.apply(gameModeName);
if (gameModeName.equalsIgnoreCase("combined") || gameModeName.equalsIgnoreCase("all")) {
gameMode = null;
}
SelectionType selectionType = SelectionType.getFromString(parts[3]);
String identifier = parts[4];
// The type of info to get. Either count (number of players) or player_position (a named player).
PlayerInfoType infoType = PlayerInfoType.getFromString(parts[5]);
if (infoType == null) {
return parameters;
}
String info = null;
if (selectionType == SelectionType.GROUP) {
ArenaGroup<?, ?> group = arenaHandler.getGroup(identifier);
if (group != null) {
info = getGroupInfo(group, gameMode, infoType, parts);
}
} else if (selectionType == SelectionType.ARENA) {
info = getArenaInfo(identifier, gameMode, infoType, parts);
}
return Objects.requireNonNullElse(info, parameters);
}
/**
* Gets information about an arena group's players
*
* @param group <p>The group to get info about</p>
* @param gameMode <p>The game-mode to get information for</p>
* @param infoType <p>The type of information to get</p>
* @param parts <p>The placeholder arguments specified by a user</p>
* @return <p>The specified group info, or null if the placeholder is invalid</p>
*/
@Nullable
private String getGroupInfo(@NotNull ArenaGroup<?, ?> group, @Nullable ArenaGameMode gameMode,
@NotNull PlayerInfoType infoType, @NotNull String[] parts) {
List<UUID> arenaIds = group.getArenas();
List<K> arenas = new ArrayList<>();
for (UUID arenaId : arenaIds) {
K arena = arenaHandler.getArena(arenaId);
if (arena != null) {
arenas.add(arena);
}
}
if (infoType == PlayerInfoType.COUNT) {
int playerCount = 0;
for (K arena : arenas) {
playerCount += getArenaPlayers(arena, gameMode).size();
}
return String.valueOf(playerCount);
} else if (infoType == PlayerInfoType.PLAYER) {
if (parts.length < 7) {
return null;
}
Integer playerNumber = getPositionNumber(parts[6]);
List<String> arenaPlayerNames = new ArrayList<>();
for (K arena : arenas) {
arenaPlayerNames.addAll(getArenaPlayersSorted(arena, gameMode));
}
arenaPlayerNames.sort(Comparator.naturalOrder());
if (playerNumber != null) {
if (playerNumber >= arenaPlayerNames.size()) {
return "";
} else {
return arenaPlayerNames.get(playerNumber);
}
}
}
return null;
}
/**
* Gets information about an arena's players
*
* @param arenaName <p>The name of the arena</p>
* @param gameMode <p>The game-mode to get information for</p>
* @param infoType <p>The type of information to get</p>
* @param parts <p>The placeholder arguments specified by a user</p>
* @return <p>The specified arena info, or null if the placeholder is invalid</p>
*/
@Nullable
private String getArenaInfo(@NotNull String arenaName, @Nullable ArenaGameMode gameMode,
@NotNull PlayerInfoType infoType, @NotNull String[] parts) {
if (infoType == PlayerInfoType.COUNT) {
Set<UUID> arenaPlayers = getArenaPlayers(arenaName, gameMode);
if (arenaPlayers != null) {
return String.valueOf(arenaPlayers.size());
}
} else if (infoType == PlayerInfoType.PLAYER) {
if (parts.length < 7) {
return null;
}
Integer playerNumber = getPositionNumber(parts[6]);
List<String> players = getArenaPlayersSorted(arenaName, gameMode);
if (playerNumber != null && players != null) {
if (playerNumber >= players.size()) {
return "";
} else {
return players.get(playerNumber);
}
}
}
return null;
}
/**
* Gets the position number from the given string
*
* @param positionNumber <p>The position number to parse</p>
* @return <p>The position number, or null if not valid</p>
*/
@Nullable
private Integer getPositionNumber(@NotNull String positionNumber) {
try {
return Integer.parseInt(positionNumber) - 1;
} catch (NumberFormatException exception) {
MiniGames.log(Level.WARNING, "Invalid placeholder given. " + positionNumber +
" supplied instead of player number.");
return null;
}
}
/**
* Gets names of all players in an arena in sorted order
*
* @param arenaName <p>The name of the arena to get players from</p>
* @param arenaGameMode <p>The game-mode to get players for</p>
* @return <p>Player names in sorted order, or null if the arena name is invalid</p>
*/
@Nullable
private List<String> getArenaPlayersSorted(@NotNull String arenaName, @Nullable ArenaGameMode arenaGameMode) {
K arena = arenaHandler.getArena(arenaName);
if (arena == null) {
return null;
}
return getArenaPlayersSorted(arena, arenaGameMode);
}
/**
* Gets names of all players in an arena in sorted order
*
* @param arena <p>The arena to get players from</p>
* @param arenaGameMode <p>The game-mode to get players for</p>
* @return <p>Player names in sorted order, or null if the arena name is invalid</p>
*/
@NotNull
private List<String> getArenaPlayersSorted(@NotNull K arena, @Nullable ArenaGameMode arenaGameMode) {
Set<UUID> players = getArenaPlayers(arena, arenaGameMode);
List<String> playerNames = new ArrayList<>(players.size());
for (UUID playerId : players) {
Player player = Bukkit.getPlayer(playerId);
if (player != null) {
playerNames.add(player.getName());
}
}
playerNames.sort(Comparator.naturalOrder());
return playerNames;
}
/**
* Gets all players from the given arena
*
* @param arenaName <p>The name of the arena to get players from</p>
* @param arenaGameMode <p>The game-mode to get players for</p>
* @return <p>The players in the given arena playing the given game-mode</p>
*/
@Nullable
private Set<UUID> getArenaPlayers(@NotNull String arenaName, @Nullable ArenaGameMode arenaGameMode) {
K arena = arenaHandler.getArena(arenaName);
if (arena == null) {
return null;
}
return getArenaPlayers(arena, arenaGameMode);
}
/**
* Gets all players from the given arena
*
* @param arena <p>The arena to get players from</p>
* @param arenaGameMode <p>The game-mode to get players for</p>
* @return <p>The players in the given arena playing the given game-mode</p>
*/
@NotNull
private Set<UUID> getArenaPlayers(@NotNull K arena, @Nullable ArenaGameMode arenaGameMode) {
// If getting count for any game-mode, skip filtering
Set<UUID> players = playerRegistry.getPlayingPlayers(arena);
if (arenaGameMode == null) {
return players;
}
Set<UUID> output = new HashSet<>();
for (UUID playerId : players) {
ArenaSession arenaSession = playerRegistry.getArenaSession(playerId);
if (arenaSession == null || arenaSession.getGameMode() != arenaGameMode) {
continue;
}
output.add(playerId);
}
return output;
}
}

View File

@ -1,6 +1,5 @@
package net.knarcraft.minigames.placeholder;
package net.knarcraft.minigames.placeholder.parsing;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.Arena;
import net.knarcraft.minigames.arena.ArenaGameMode;
@ -8,12 +7,10 @@ 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.placeholder.GroupRecordCache;
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;
@ -26,51 +23,45 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.logging.Level;
/**
* A placeholder expansion for parkour record placeholders
* A parser for record-related placeholders
*/
public abstract class RecordExpansion extends PlaceholderExpansion {
public class RecordPlaceholderParser {
private final ArenaHandler<?, ?> arenaHandler;
private final Map<UUID, Set<GroupRecordCache<Integer>>> groupRecordDeathsCache;
private final Map<UUID, Set<GroupRecordCache<Long>>> groupRecordTimeCache;
private final @NotNull ArenaHandler<?, ?> arenaHandler;
private final @NotNull Map<UUID, Set<GroupRecordCache<Integer>>> groupRecordDeathsCache;
private final @NotNull Map<UUID, Set<GroupRecordCache<Long>>> groupRecordTimeCache;
private final @NotNull Function<String, ArenaGameMode> gameModeParser;
/**
* Initializes a new record expansion
* Initializes a new record placeholder parser
*/
public RecordExpansion(ArenaHandler<?, ?> arenaHandler) {
public RecordPlaceholderParser(@NotNull ArenaHandler<?, ?> arenaHandler,
@NotNull Function<String, ArenaGameMode> gameModeParser) {
this.groupRecordDeathsCache = new HashMap<>();
this.groupRecordTimeCache = new HashMap<>();
this.arenaHandler = arenaHandler;
this.gameModeParser = gameModeParser;
}
@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")) {
/**
* The method to run when parsing a record placeholder request
*
* @param parameters <p>The parameters specified</p>
* @return <p>The resulting string</p>
*/
@NotNull
public String onRequest(@NotNull String parameters, @NotNull String[] parts) {
if (parts.length < 7) {
return parameters;
}
RecordType recordType = RecordType.getFromString(parts[1]);
ArenaGameMode gameMode = parseGameMode(parts[2]);
ArenaGameMode gameMode = gameModeParser.apply(parts[2]);
SelectionType selectionType = SelectionType.getFromString(parts[3]);
String identifier = parts[4];
int recordNumber;
@ -78,7 +69,7 @@ public abstract class RecordExpansion extends PlaceholderExpansion {
recordNumber = Integer.parseInt(parts[5]) - 1;
} catch (NumberFormatException exception) {
MiniGames.log(Level.WARNING, "Invalid placeholder given. " + parts[5] +
" supplied instead of record placement.");
" supplied instead of record position.");
return parameters;
}
InfoType infoType = InfoType.getFromString(parts[6]);
@ -97,14 +88,6 @@ public abstract class RecordExpansion extends PlaceholderExpansion {
return Objects.requireNonNullElse(info, parameters);
}
/**
* Parses the game-mode specified in the given string
*
* @param gameMode <p>The game-mode to parse</p>
* @return <p>The parsed game-mode</p>
*/
protected abstract @NotNull ArenaGameMode parseGameMode(@NotNull String gameMode);
/**
* Clears all record caches
*/
@ -124,9 +107,10 @@ public abstract class RecordExpansion extends PlaceholderExpansion {
* @param infoType <p>The type of info (player, value, combined) to get</p>
* @return <p>The selected information about the record, or null if not found</p>
*/
private @Nullable String getGroupRecord(@NotNull ArenaHandler<?, ?> arenaHandler, @NotNull String identifier,
@NotNull ArenaGameMode gameMode, @NotNull RecordType recordType,
int recordNumber, @NotNull InfoType infoType) {
@Nullable
private 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 {
@ -162,9 +146,10 @@ public abstract class RecordExpansion extends PlaceholderExpansion {
* @param arenaHandler <p>The handler to get arenas from</p>
* @return <p>The record, or null if not found</p>
*/
private @Nullable ArenaRecord<?> getGroupTimeRecord(@NotNull ArenaGroup<?, ?> group,
@NotNull ArenaGameMode gameMode, int recordNumber,
@NotNull ArenaHandler<?, ?> arenaHandler) {
@Nullable
private 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));
}
@ -178,9 +163,10 @@ public abstract class RecordExpansion extends PlaceholderExpansion {
* @param arenaHandler <p>The handler to get arenas from</p>
* @return <p>The record, or null if not found</p>
*/
private @Nullable ArenaRecord<?> getGroupDeathRecord(@NotNull ArenaGroup<?, ?> group,
@NotNull ArenaGameMode gameMode, int recordNumber,
@NotNull ArenaHandler<?, ?> arenaHandler) {
@Nullable
private 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));
}
@ -197,12 +183,13 @@ public abstract class RecordExpansion extends PlaceholderExpansion {
* @param <K> <p>The type of the provided records</p>
* @return <p>The specified record, or null if not found</p>
*/
private <K extends Comparable<K>> @Nullable ArenaRecord<?> getCachedGroupRecord(@NotNull ArenaGroup<?, ?> group,
@NotNull ArenaGameMode gameMode,
@NotNull RecordType recordType,
int recordNumber,
@NotNull Map<UUID, Set<GroupRecordCache<K>>> caches,
@NotNull Supplier<Set<ArenaRecord<K>>> recordProvider) {
@Nullable
private <K extends Comparable<K>> ArenaRecord<?> getCachedGroupRecord(@NotNull ArenaGroup<?, ?> group,
@NotNull ArenaGameMode gameMode,
@NotNull RecordType recordType,
int recordNumber,
@NotNull Map<UUID, Set<GroupRecordCache<K>>> caches,
@NotNull Supplier<Set<ArenaRecord<K>>> recordProvider) {
UUID groupId = group.getGroupId();
if (!caches.containsKey(groupId)) {
caches.put(groupId, new HashSet<>());
@ -244,9 +231,10 @@ public abstract class RecordExpansion extends PlaceholderExpansion {
* @param infoType <p>The type of info (player, value, combined) to get</p>
* @return <p>The selected information about the record, or null if not found</p>
*/
private @Nullable String getArenaRecord(@NotNull ArenaHandler<?, ?> arenaHandler, @NotNull String identifier,
@NotNull ArenaGameMode gameMode, @NotNull RecordType recordType,
int recordNumber, @NotNull InfoType infoType) {
@Nullable
private 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 {
@ -278,8 +266,9 @@ public abstract class RecordExpansion extends PlaceholderExpansion {
* @param recordNumber <p>The position of the record to get (1st place, 2nd place, etc.)</p>
* @return <p>The record, or null if not found</p>
*/
private @Nullable ArenaRecord<?> getRecord(@NotNull ArenaRecordsRegistry recordsRegistry,
@NotNull RecordType recordType, int recordNumber) {
@Nullable
private 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);
@ -294,7 +283,8 @@ public abstract class RecordExpansion extends PlaceholderExpansion {
* @param <K> <p>The type of record in the record list</p>
* @return <p>The record, or null if index is out of bounds</p>
*/
private <K extends Comparable<K>> @Nullable ArenaRecord<K> getRecord(Set<ArenaRecord<K>> records, int index) {
@Nullable
private <K extends Comparable<K>> ArenaRecord<K> getRecord(@NotNull Set<ArenaRecord<K>> records, int index) {
List<ArenaRecord<K>> sorted = getSortedRecords(records);
if (index < sorted.size() && index >= 0) {
return sorted.get(index);
@ -310,6 +300,7 @@ public abstract class RecordExpansion extends PlaceholderExpansion {
* @param arenaRecord <p>The record to get the data from</p>
* @return <p>The requested data as a string, or null</p>
*/
@Nullable
private String getRecordData(@NotNull InfoType infoType, @NotNull ArenaRecord<?> arenaRecord) {
return switch (infoType) {
case PLAYER -> getPlayerName(arenaRecord.getUserId());
@ -325,8 +316,8 @@ public abstract class RecordExpansion extends PlaceholderExpansion {
* @param <K> <p>The type of the records</p>
* @return <p>The sorted records</p>
*/
private <K extends Comparable<K>> @NotNull List<ArenaRecord<K>> getSortedRecords(
@NotNull Set<ArenaRecord<K>> recordSet) {
@NotNull
private <K extends Comparable<K>> List<ArenaRecord<K>> getSortedRecords(@NotNull Set<ArenaRecord<K>> recordSet) {
List<ArenaRecord<K>> records = new ArrayList<>(recordSet);
Collections.sort(records);
return records;
@ -338,6 +329,7 @@ public abstract class RecordExpansion extends PlaceholderExpansion {
* @param playerId <p>The id of the player to get the name for</p>
* @return <p>The name of the player, or a string representation of the UUID if not found</p>
*/
@Nullable
private String getPlayerName(@NotNull UUID playerId) {
return Bukkit.getOfflinePlayer(playerId).getName();
}

View File

@ -7,6 +7,8 @@ description: A plugin that adds various mini-games
softdepend:
- PlaceholderAPI
- Vault
- Geyser-Spigot
- floodgate
# Note to self: Aliases must be lowercase!
commands: