diff --git a/pom.xml b/pom.xml index c1b5315..a094ae0 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,10 @@ spigot-repo https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + placeholderapi + https://repo.extendedclip.com/content/repositories/placeholderapi/ + @@ -79,5 +83,11 @@ 5.9.2 test + + me.clip + placeholderapi + 2.10.0 + provided + diff --git a/src/main/java/net/knarcraft/dropper/Dropper.java b/src/main/java/net/knarcraft/dropper/Dropper.java index 17ccb6c..44eb659 100644 --- a/src/main/java/net/knarcraft/dropper/Dropper.java +++ b/src/main/java/net/knarcraft/dropper/Dropper.java @@ -1,5 +1,6 @@ package net.knarcraft.dropper; +import net.knarcraft.dropper.arena.ArenaRecord; import net.knarcraft.dropper.arena.DropperArenaData; import net.knarcraft.dropper.arena.DropperArenaGroup; import net.knarcraft.dropper.arena.DropperArenaHandler; @@ -25,7 +26,9 @@ import net.knarcraft.dropper.listener.CommandListener; import net.knarcraft.dropper.listener.DamageListener; import net.knarcraft.dropper.listener.MoveListener; import net.knarcraft.dropper.listener.PlayerLeaveListener; +import net.knarcraft.dropper.placeholder.RecordExpansion; import net.knarcraft.dropper.property.ArenaGameMode; +import org.bukkit.Bukkit; import org.bukkit.command.CommandExecutor; import org.bukkit.command.PluginCommand; import org.bukkit.command.TabCompleter; @@ -94,6 +97,7 @@ public final class Dropper extends JavaPlugin { ConfigurationSerialization.registerClass(DropperArenaData.class); ConfigurationSerialization.registerClass(DropperArenaGroup.class); ConfigurationSerialization.registerClass(ArenaGameMode.class); + ConfigurationSerialization.registerClass(ArenaRecord.class); } @Override @@ -121,6 +125,12 @@ public final class Dropper extends JavaPlugin { registerCommand("dropperGroupSet", new GroupSetCommand(), null); registerCommand("dropperGroupSwap", new GroupSwapCommand(), null); registerCommand("dropperGroupList", new GroupListCommand(), null); + + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + if (!new RecordExpansion(this).register()) { + getLogger().log(Level.WARNING, "Unable to register PlaceholderAPI expansion!"); + } + } } @Override diff --git a/src/main/java/net/knarcraft/dropper/arena/ArenaRecord.java b/src/main/java/net/knarcraft/dropper/arena/ArenaRecord.java new file mode 100644 index 0000000..eae2200 --- /dev/null +++ b/src/main/java/net/knarcraft/dropper/arena/ArenaRecord.java @@ -0,0 +1,52 @@ +package net.knarcraft.dropper.arena; + +import net.knarcraft.dropper.container.SerializableUUID; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * A record stored for an arena + * + * @param userId

The id of the player that achieved the record

+ * @param record

The record achieved

+ * @param

The comparable type of the record

+ */ +public record ArenaRecord>(UUID userId, K record) implements Comparable>, + ConfigurationSerializable { + + @Override + public boolean equals(Object other) { + return other instanceof ArenaRecord && userId.equals(((ArenaRecord) other).userId); + } + + @Override + public int compareTo(@NotNull ArenaRecord other) { + return record.compareTo(other.record); + } + + @NotNull + @Override + public Map serialize() { + Map data = new HashMap<>(); + data.put("userId", new SerializableUUID(userId())); + data.put("record", record); + return data; + } + + /** + * Deserializes the saved arena record + * + * @param data

The data to deserialize

+ * @param

The type of the deserialized record

+ * @return

The deserialized data

+ */ + @SuppressWarnings({"unused", "unchecked"}) + public static > ArenaRecord deserialize(@NotNull Map data) { + return new ArenaRecord<>(((SerializableUUID) data.get("userId")).uuid(), (K) data.get("record")); + } + +} diff --git a/src/main/java/net/knarcraft/dropper/arena/DropperArenaRecordsRegistry.java b/src/main/java/net/knarcraft/dropper/arena/DropperArenaRecordsRegistry.java index f57d48f..9988f48 100644 --- a/src/main/java/net/knarcraft/dropper/arena/DropperArenaRecordsRegistry.java +++ b/src/main/java/net/knarcraft/dropper/arena/DropperArenaRecordsRegistry.java @@ -7,9 +7,11 @@ import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.jetbrains.annotations.NotNull; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.UUID; -import java.util.stream.Stream; +import java.util.concurrent.atomic.AtomicReference; /** * A registry keeping track of all records @@ -17,16 +19,16 @@ import java.util.stream.Stream; public class DropperArenaRecordsRegistry implements ConfigurationSerializable { private final UUID arenaId; - private final @NotNull Map leastDeaths; - private final @NotNull Map shortestTimeMilliSeconds; + private final @NotNull Set> leastDeaths; + private final @NotNull Set> shortestTimeMilliSeconds; /** * Instantiates a new empty records registry */ public DropperArenaRecordsRegistry(@NotNull UUID arenaId) { this.arenaId = arenaId; - this.leastDeaths = new HashMap<>(); - this.shortestTimeMilliSeconds = new HashMap<>(); + this.leastDeaths = new HashSet<>(); + this.shortestTimeMilliSeconds = new HashSet<>(); } /** @@ -35,11 +37,11 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable { * @param leastDeaths

The existing least death records to use

* @param shortestTimeMilliSeconds

The existing leash time records to use

*/ - private DropperArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Map leastDeaths, - @NotNull Map shortestTimeMilliSeconds) { + private DropperArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Set> leastDeaths, + @NotNull Set> shortestTimeMilliSeconds) { this.arenaId = arenaId; - this.leastDeaths = new HashMap<>(leastDeaths); - this.shortestTimeMilliSeconds = new HashMap<>(shortestTimeMilliSeconds); + this.leastDeaths = new HashSet<>(leastDeaths); + this.shortestTimeMilliSeconds = new HashSet<>(shortestTimeMilliSeconds); } /** @@ -47,12 +49,8 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable { * * @return

Existing death records

*/ - public Map getLeastDeathsRecords() { - Map leastDeathRecords = new HashMap<>(); - for (Map.Entry entry : this.leastDeaths.entrySet()) { - leastDeathRecords.put(entry.getKey(), entry.getValue().intValue()); - } - return leastDeathRecords; + public Set> getLeastDeathsRecords() { + return new HashSet<>(this.leastDeaths); } /** @@ -60,12 +58,8 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable { * * @return

Existing time records

*/ - public Map getShortestTimeMilliSecondsRecords() { - Map leastTimeRecords = new HashMap<>(); - for (Map.Entry entry : this.shortestTimeMilliSeconds.entrySet()) { - leastTimeRecords.put(entry.getKey(), entry.getValue().longValue()); - } - return leastTimeRecords; + public Set> getShortestTimeMilliSecondsRecords() { + return new HashSet<>(this.shortestTimeMilliSeconds); } /** @@ -105,25 +99,26 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable { * @param amount

The amount of whatever the player achieved

* @return

The result of the player's record attempt

*/ - private @NotNull RecordResult registerRecord(@NotNull Map existingRecords, @NotNull UUID playerId, - Number amount) { + private > @NotNull RecordResult registerRecord(@NotNull Set> existingRecords, + @NotNull UUID playerId, T amount) { RecordResult result; - Stream> records = existingRecords.entrySet().stream(); - long amountLong = amount.longValue(); - - if (records.allMatch((entry) -> amountLong < entry.getValue().longValue())) { + if (existingRecords.stream().allMatch((entry) -> amount.compareTo(entry.record()) < 0)) { // If the given value is less than all other values, that's a world record! result = RecordResult.WORLD_RECORD; - existingRecords.put(playerId, amount); + existingRecords.add(new ArenaRecord<>(playerId, amount)); save(); - } else if (existingRecords.containsKey(playerId) && amountLong < existingRecords.get(playerId).longValue()) { + return result; + } + + ArenaRecord playerRecord = getRecord(existingRecords, playerId); + if (playerRecord != null && amount.compareTo(playerRecord.record()) < 0) { // If the given value is less than the player's previous value, that's a personal best! result = RecordResult.PERSONAL_BEST; - existingRecords.put(playerId, amount); + existingRecords.add(new ArenaRecord<>(playerId, amount)); save(); } else { // Make sure to save the record if this is the user's first attempt - if (!existingRecords.containsKey(playerId)) { + if (playerRecord == null) { save(); } result = RecordResult.NONE; @@ -132,23 +127,24 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable { return result; } + private > ArenaRecord getRecord(@NotNull Set> existingRecords, + @NotNull UUID playerId) { + AtomicReference> record = new AtomicReference<>(); + existingRecords.forEach((item) -> { + if (item.userId().equals(playerId)) { + record.set(item); + } + }); + return record.get(); + } + @NotNull @Override public Map serialize() { Map data = new HashMap<>(); data.put("arenaId", new SerializableUUID(this.arenaId)); - - Map leastDeaths = new HashMap<>(); - for (Map.Entry entry : this.leastDeaths.entrySet()) { - leastDeaths.put(new SerializableUUID(entry.getKey()), entry.getValue()); - } - data.put("leastDeaths", leastDeaths); - - Map shortestTimeMilliSeconds = new HashMap<>(); - for (Map.Entry entry : this.shortestTimeMilliSeconds.entrySet()) { - shortestTimeMilliSeconds.put(new SerializableUUID(entry.getKey()), entry.getValue()); - } - data.put("shortestTime", shortestTimeMilliSeconds); + data.put("leastDeaths", this.leastDeaths); + data.put("shortestTime", this.shortestTimeMilliSeconds); return data; } @@ -161,20 +157,10 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable { @SuppressWarnings({"unused", "unchecked"}) public static DropperArenaRecordsRegistry deserialize(Map data) { UUID arenaId = ((SerializableUUID) data.get("arenaId")).uuid(); - Map leastDeathsData = - (Map) data.getOrDefault("leastDeaths", new HashMap<>()); - Map leastDeaths = new HashMap<>(); - for (Map.Entry entry : leastDeathsData.entrySet()) { - leastDeaths.put(entry.getKey().uuid(), entry.getValue()); - } - - Map shortestTimeMillisecondsData = - (Map) data.getOrDefault("shortestTime", new HashMap<>()); - Map shortestTimeMilliseconds = new HashMap<>(); - for (Map.Entry entry : shortestTimeMillisecondsData.entrySet()) { - shortestTimeMilliseconds.put(entry.getKey().uuid(), entry.getValue().longValue()); - } - + Set> leastDeaths = + (Set>) data.getOrDefault("leastDeaths", new HashMap<>()); + Set> shortestTimeMilliseconds = + (Set>) data.getOrDefault("shortestTime", new HashMap<>()); return new DropperArenaRecordsRegistry(arenaId, leastDeaths, shortestTimeMilliseconds); } diff --git a/src/main/java/net/knarcraft/dropper/arena/PlayerEntryState.java b/src/main/java/net/knarcraft/dropper/arena/PlayerEntryState.java index 02f2e27..058669b 100644 --- a/src/main/java/net/knarcraft/dropper/arena/PlayerEntryState.java +++ b/src/main/java/net/knarcraft/dropper/arena/PlayerEntryState.java @@ -21,7 +21,7 @@ public class PlayerEntryState { private final boolean originalAllowFlight; private final boolean originalInvulnerable; private final boolean originalIsSwimming; - private final boolean originalCollidable; + private final boolean originalCollideAble; private final ArenaGameMode arenaGameMode; /** @@ -39,7 +39,7 @@ public class PlayerEntryState { this.originalInvulnerable = player.isInvulnerable(); this.originalIsSwimming = player.isSwimming(); this.arenaGameMode = arenaGameMode; - this.originalCollidable = player.isCollidable(); + this.originalCollideAble = player.isCollidable(); } /** @@ -73,7 +73,7 @@ public class PlayerEntryState { this.player.setFlySpeed(this.originalFlySpeed); this.player.setInvulnerable(this.originalInvulnerable); this.player.setSwimming(this.originalIsSwimming); - this.player.setCollidable(this.originalCollidable); + this.player.setCollidable(this.originalCollideAble); this.player.removePotionEffect(PotionEffectType.INVISIBILITY); } diff --git a/src/main/java/net/knarcraft/dropper/placeholder/RecordExpansion.java b/src/main/java/net/knarcraft/dropper/placeholder/RecordExpansion.java new file mode 100644 index 0000000..cd15f70 --- /dev/null +++ b/src/main/java/net/knarcraft/dropper/placeholder/RecordExpansion.java @@ -0,0 +1,89 @@ +package net.knarcraft.dropper.placeholder; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import me.clip.placeholderapi.expansion.Relational; +import net.knarcraft.dropper.Dropper; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +public class RecordExpansion extends PlaceholderExpansion implements Relational { + + private final Dropper plugin; + + public RecordExpansion(Dropper plugin) { + this.plugin = plugin; + } + + @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.length < 7 || !parts[0].equals("record")) { + return parameters; + } + boolean timeRecords = parts[1].equals("time"); + ArenaGameMode gameMode = ArenaGameMode.matchGamemode(parts[2]); + boolean isGroup = parts[3].equalsIgnoreCase("group"); + String identifier = parts[4]; + int recordNumber = Integer.parseInt(parts[5]); + boolean value = parts[6].equalsIgnoreCase("value"); + + DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler(); + if (isGroup) { + DropperArenaGroup group = arenaHandler.getGroup(identifier); + } else { + DropperArena arena = arenaHandler.getArena(identifier); + if (arena == null) { + return parameters; + } + @NotNull Map registries = arena.getData().recordRegistries(); + DropperArenaRecordsRegistry recordsRegistry = registries.get(gameMode); + if (timeRecords) { + recordsRegistry.getShortestTimeMilliSecondsRecords(); + } else { + recordsRegistry.getLeastDeathsRecords(); + } + }*/ + + /* + Format: + %dropper_record_time_random_arena_arenaname_1_player% + %dropper_record_time_random_arena_arenaname_1_value% + + dropper_record: Denotes that it's a placeholder for a dropper record + deaths/time: The type of record to get + default/inverted/random: The game-mode to get the record for + arena/group: Denoting if the following name is the name of an arena or an arena group + 1,2,3,...: The placing to get: 1 for first place, etc. + player/value: Whether to get the name of the player, or the player's record + */ + // TODO: Figure out how placeholders should work. %dropper_group_1% should display the top player of the arena group + return parameters; + } + + @Override + public String onPlaceholderRequest(Player player1, Player player2, String parameters) { + return null; + } + +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 83d3235..21d4a09 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -3,6 +3,8 @@ version: '${project.version}' main: net.knarcraft.dropper.Dropper api-version: 1.19 description: A plugin for dropper mini-games +softdepend: + - PlaceholderAPI # Note to self: Aliases must be lowercase! commands: