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: