arenaProperties = new ArrayList<>();
- for (ArenaEditableProperty property : ArenaEditableProperty.values()) {
- arenaProperties.add(property.getArgumentString());
- }
- return arenaProperties;
- }
-
-}
diff --git a/src/main/java/net/knarcraft/minigames/MiniGames.java b/src/main/java/net/knarcraft/minigames/MiniGames.java
new file mode 100644
index 0000000..f26726a
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/MiniGames.java
@@ -0,0 +1,309 @@
+package net.knarcraft.minigames;
+
+import net.knarcraft.minigames.arena.ArenaSession;
+import net.knarcraft.minigames.arena.dropper.DropperArenaData;
+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.DropperArenaPlayerRegistry;
+import net.knarcraft.minigames.arena.dropper.DropperArenaRecordsRegistry;
+import net.knarcraft.minigames.arena.dropper.DropperArenaSession;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaData;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaPlayerRegistry;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaRecordsRegistry;
+import net.knarcraft.minigames.arena.record.IntegerRecord;
+import net.knarcraft.minigames.arena.record.LongRecord;
+import net.knarcraft.minigames.command.LeaveArenaCommand;
+import net.knarcraft.minigames.command.ReloadCommand;
+import net.knarcraft.minigames.command.dropper.CreateDropperArenaCommand;
+import net.knarcraft.minigames.command.dropper.DropperGroupListCommand;
+import net.knarcraft.minigames.command.dropper.DropperGroupSetCommand;
+import net.knarcraft.minigames.command.dropper.DropperGroupSwapCommand;
+import net.knarcraft.minigames.command.dropper.EditDropperArenaCommand;
+import net.knarcraft.minigames.command.dropper.EditDropperArenaTabCompleter;
+import net.knarcraft.minigames.command.dropper.JoinDropperArenaCommand;
+import net.knarcraft.minigames.command.dropper.JoinDropperArenaTabCompleter;
+import net.knarcraft.minigames.command.dropper.ListDropperArenaCommand;
+import net.knarcraft.minigames.command.dropper.RemoveDropperArenaCommand;
+import net.knarcraft.minigames.command.dropper.RemoveDropperArenaTabCompleter;
+import net.knarcraft.minigames.command.parkour.CreateParkourArenaCommand;
+import net.knarcraft.minigames.command.parkour.EditParkourArenaCommand;
+import net.knarcraft.minigames.command.parkour.EditParkourArenaTabCompleter;
+import net.knarcraft.minigames.command.parkour.JoinParkourArenaCommand;
+import net.knarcraft.minigames.command.parkour.JoinParkourArenaTabCompleter;
+import net.knarcraft.minigames.command.parkour.ListParkourArenaCommand;
+import net.knarcraft.minigames.command.parkour.ParkourGroupListCommand;
+import net.knarcraft.minigames.command.parkour.ParkourGroupSetCommand;
+import net.knarcraft.minigames.command.parkour.ParkourGroupSwapCommand;
+import net.knarcraft.minigames.command.parkour.RemoveParkourArenaCommand;
+import net.knarcraft.minigames.command.parkour.RemoveParkourArenaTabCompleter;
+import net.knarcraft.minigames.config.DropperConfiguration;
+import net.knarcraft.minigames.config.ParkourConfiguration;
+import net.knarcraft.minigames.config.SharedConfiguration;
+import net.knarcraft.minigames.container.SerializableMaterial;
+import net.knarcraft.minigames.container.SerializableUUID;
+import net.knarcraft.minigames.listener.CommandListener;
+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;
+import org.bukkit.command.TabCompleter;
+import org.bukkit.configuration.serialization.ConfigurationSerialization;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.PluginManager;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.UUID;
+import java.util.logging.Level;
+
+/**
+ * The dropper plugin's main class
+ */
+@SuppressWarnings("unused")
+public final class MiniGames extends JavaPlugin {
+
+ private static MiniGames instance;
+ private SharedConfiguration sharedConfiguration;
+ private DropperConfiguration dropperConfiguration;
+ private ParkourConfiguration parkourConfiguration;
+ private DropperArenaHandler dropperArenaHandler;
+ private DropperArenaPlayerRegistry dropperArenaPlayerRegistry;
+ private DropperRecordExpansion dropperRecordExpansion;
+ private ParkourRecordExpansion parkourRecordExpansion;
+ private ParkourArenaHandler parkourArenaHandler;
+ private ParkourArenaPlayerRegistry parkourArenaPlayerRegistry;
+
+ /**
+ * Gets an instance of this plugin
+ *
+ * @return An instance of this plugin, or null if not initialized yet.
+ */
+ public static MiniGames getInstance() {
+ return instance;
+ }
+
+ /**
+ * Gets the dropper arena handler for this instance
+ *
+ * @return A dropper arena handler
+ */
+ public DropperArenaHandler getDropperArenaHandler() {
+ return this.dropperArenaHandler;
+ }
+
+ /**
+ * Gets the parkour arena handler for this instance
+ *
+ * @return A parkour arena handler
+ */
+ public ParkourArenaHandler getParkourArenaHandler() {
+ return this.parkourArenaHandler;
+ }
+
+ /**
+ * Gets the dropper arena player registry for this instance
+ *
+ * @return A dropper arena player registry
+ */
+ public DropperArenaPlayerRegistry getDropperArenaPlayerRegistry() {
+ return this.dropperArenaPlayerRegistry;
+ }
+
+ /**
+ * Gets the parkour arena player registry for this instance
+ *
+ * @return A parkour arena player registry
+ */
+ public ParkourArenaPlayerRegistry getParkourArenaPlayerRegistry() {
+ return this.parkourArenaPlayerRegistry;
+ }
+
+ /**
+ * Gets the shared configuration
+ *
+ * The configuration for options which don't affect specific types of mini-games.
+ *
+ * @return The shared configuration
+ */
+ public SharedConfiguration getSharedConfiguration() {
+ return this.sharedConfiguration;
+ }
+
+ /**
+ * Gets the dropper configuration
+ *
+ * @return The dropper configuration
+ */
+ public DropperConfiguration getDropperConfiguration() {
+ return this.dropperConfiguration;
+ }
+
+ /**
+ * Gets the parkour configuration
+ *
+ * @return The parkour configuration
+ */
+ public ParkourConfiguration getParkourConfiguration() {
+ return this.parkourConfiguration;
+ }
+
+ /**
+ * Gets the current session of the given player
+ *
+ * @param playerId The id of the player to get a session for
+ * @return The player's current session, or null if not found
+ */
+ public @Nullable ArenaSession getSession(@NotNull UUID playerId) {
+ DropperArenaSession dropperArenaSession = dropperArenaPlayerRegistry.getArenaSession(playerId);
+ if (dropperArenaSession != null) {
+ return dropperArenaSession;
+ }
+
+ return parkourArenaPlayerRegistry.getArenaSession(playerId);
+ }
+
+ /**
+ * Logs a message
+ *
+ * @param level The message level to log at
+ * @param message The message to log
+ */
+ public static void log(Level level, String message) {
+ MiniGames.getInstance().getLogger().log(level, message);
+ }
+
+ /**
+ * Reloads all configurations and data from disk
+ */
+ public void reload() {
+ // Load all arenas again
+ this.dropperArenaHandler.load();
+ this.parkourArenaHandler.load();
+
+ // Reload configuration
+ this.reloadConfig();
+ this.sharedConfiguration.load(this.getConfig());
+ this.dropperConfiguration.load(this.getConfig());
+ this.parkourConfiguration.load(this.getConfig());
+
+ // Clear record caches
+ this.dropperRecordExpansion.clearCaches();
+ this.parkourRecordExpansion.clearCaches();
+ }
+
+ @Override
+ public void onLoad() {
+ super.onLoad();
+ // Register serialization classes
+ ConfigurationSerialization.registerClass(SerializableMaterial.class);
+ ConfigurationSerialization.registerClass(DropperArenaRecordsRegistry.class);
+ ConfigurationSerialization.registerClass(SerializableUUID.class);
+ ConfigurationSerialization.registerClass(DropperArenaData.class);
+ ConfigurationSerialization.registerClass(DropperArenaGroup.class);
+ ConfigurationSerialization.registerClass(DropperArenaGameMode.class);
+ ConfigurationSerialization.registerClass(LongRecord.class);
+ ConfigurationSerialization.registerClass(IntegerRecord.class);
+ ConfigurationSerialization.registerClass(ParkourArenaRecordsRegistry.class);
+ ConfigurationSerialization.registerClass(ParkourArenaData.class);
+ ConfigurationSerialization.registerClass(ParkourArenaGroup.class);
+ ConfigurationSerialization.registerClass(ParkourArenaGameMode.class);
+ }
+
+ @Override
+ public void onEnable() {
+ // Plugin startup logic
+ instance = this;
+ this.saveDefaultConfig();
+ getConfig().options().copyDefaults(true);
+ saveConfig();
+ reloadConfig();
+ this.sharedConfiguration = new SharedConfiguration(this.getConfig());
+ this.dropperConfiguration = new DropperConfiguration(this.getConfig());
+ this.parkourConfiguration = new ParkourConfiguration(this.getConfig());
+ 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();
+
+ PluginManager pluginManager = getServer().getPluginManager();
+ pluginManager.registerEvents(new DamageListener(), this);
+ pluginManager.registerEvents(new MoveListener(this.dropperConfiguration, this.parkourConfiguration), this);
+ pluginManager.registerEvents(new PlayerLeaveListener(), this);
+ pluginManager.registerEvents(new CommandListener(), this);
+
+ registerCommand("miniGamesReload", new ReloadCommand(), null);
+ registerCommand("miniGamesLeave", new LeaveArenaCommand(), null);
+
+ registerCommand("dropperCreate", new CreateDropperArenaCommand(), null);
+ registerCommand("dropperList", new ListDropperArenaCommand(), null);
+ registerCommand("dropperJoin", new JoinDropperArenaCommand(), new JoinDropperArenaTabCompleter());
+ registerCommand("dropperEdit", new EditDropperArenaCommand(this.dropperConfiguration), new EditDropperArenaTabCompleter());
+ registerCommand("dropperRemove", new RemoveDropperArenaCommand(), new RemoveDropperArenaTabCompleter());
+ registerCommand("dropperGroupSet", new DropperGroupSetCommand(), null);
+ registerCommand("dropperGroupSwap", new DropperGroupSwapCommand(), null);
+ registerCommand("dropperGroupList", new DropperGroupListCommand(), null);
+
+ registerCommand("parkourCreate", new CreateParkourArenaCommand(), null);
+ registerCommand("parkourList", new ListParkourArenaCommand(), null);
+ registerCommand("parkourJoin", new JoinParkourArenaCommand(), new JoinParkourArenaTabCompleter());
+ registerCommand("parkourEdit", new EditParkourArenaCommand(), new EditParkourArenaTabCompleter());
+ registerCommand("parkourRemove", new RemoveParkourArenaCommand(), new RemoveParkourArenaTabCompleter());
+ registerCommand("parkourGroupSet", new ParkourGroupSetCommand(), null);
+ registerCommand("parkourGroupSwap", new ParkourGroupSwapCommand(), null);
+ registerCommand("parkourGroupList", new ParkourGroupListCommand(), null);
+
+ if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
+ this.dropperRecordExpansion = new DropperRecordExpansion(this);
+ if (!this.dropperRecordExpansion.register()) {
+ 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!");
+ }
+ }
+ }
+
+ @Override
+ public void onDisable() {
+ // Throw out currently playing players before exiting
+ for (Player player : getServer().getOnlinePlayers()) {
+ ArenaSession session = getSession(player.getUniqueId());
+ if (session != null) {
+ session.triggerQuit(true);
+ }
+ }
+ }
+
+ /**
+ * Registers a command
+ *
+ * @param commandName The name of the command to register (defined in plugin.yml)
+ * @param commandExecutor The executor for the command
+ * @param tabCompleter The tab-completer to use, or null
+ */
+ private void registerCommand(@NotNull String commandName, @NotNull CommandExecutor commandExecutor,
+ @Nullable TabCompleter tabCompleter) {
+ PluginCommand command = this.getCommand(commandName);
+ if (command != null) {
+ command.setExecutor(commandExecutor);
+ if (tabCompleter != null) {
+ command.setTabCompleter(tabCompleter);
+ }
+ } else {
+ log(Level.SEVERE, "Unable to register the command " + commandName);
+ }
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/AbstractPlayerEntryState.java b/src/main/java/net/knarcraft/minigames/arena/AbstractPlayerEntryState.java
new file mode 100644
index 0000000..c5cdf04
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/AbstractPlayerEntryState.java
@@ -0,0 +1,69 @@
+package net.knarcraft.minigames.arena;
+
+import org.bukkit.GameMode;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.potion.PotionEffectType;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * An abstract representation of a player's entry state
+ */
+public abstract class AbstractPlayerEntryState implements PlayerEntryState {
+
+ protected final Player player;
+ private final boolean makePlayerInvisible;
+ private final Location entryLocation;
+ private final boolean originalIsFlying;
+ private final GameMode originalGameMode;
+ private final boolean originalAllowFlight;
+ private final boolean originalInvulnerable;
+ private final boolean originalIsSwimming;
+ private final boolean originalCollideAble;
+
+ /**
+ * Instantiates a new abstract player entry state
+ *
+ * @param player The player whose state this should keep track of
+ * @param makePlayerInvisible Whether players should be made invisible while in the arena
+ */
+ public AbstractPlayerEntryState(@NotNull Player player, boolean makePlayerInvisible) {
+ this.player = player;
+ this.makePlayerInvisible = makePlayerInvisible;
+ this.entryLocation = player.getLocation().clone();
+ this.originalIsFlying = player.isFlying();
+ this.originalGameMode = player.getGameMode();
+ this.originalAllowFlight = player.getAllowFlight();
+ this.originalInvulnerable = player.isInvulnerable();
+ this.originalIsSwimming = player.isSwimming();
+ this.originalCollideAble = player.isCollidable();
+ }
+
+ @Override
+ public void setArenaState() {
+ if (this.makePlayerInvisible) {
+ this.player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY,
+ PotionEffect.INFINITE_DURATION, 3));
+ }
+ }
+
+ @Override
+ public void restore() {
+ this.player.setFlying(this.originalIsFlying);
+ this.player.setGameMode(this.originalGameMode);
+ this.player.setAllowFlight(this.originalAllowFlight);
+ this.player.setInvulnerable(this.originalInvulnerable);
+ this.player.setSwimming(this.originalIsSwimming);
+ this.player.setCollidable(this.originalCollideAble);
+ if (this.makePlayerInvisible) {
+ this.player.removePotionEffect(PotionEffectType.INVISIBILITY);
+ }
+ }
+
+ @Override
+ public Location getEntryLocation() {
+ return this.entryLocation;
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/Arena.java b/src/main/java/net/knarcraft/minigames/arena/Arena.java
new file mode 100644
index 0000000..97e46d4
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/Arena.java
@@ -0,0 +1,86 @@
+package net.knarcraft.minigames.arena;
+
+import org.bukkit.Location;
+import org.bukkit.block.Block;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.UUID;
+
+/**
+ * An interface describing an arena
+ */
+public interface Arena {
+
+ /**
+ * Gets the name of this arena
+ *
+ * @return The name of this arena
+ */
+ @NotNull String getArenaName();
+
+ /**
+ * Gets the data stored for this arena
+ *
+ * @return The stored data
+ */
+ @NotNull ArenaData getData();
+
+ /**
+ * Gets the id of this arena
+ *
+ * @return This arena's identifier
+ */
+ @NotNull UUID getArenaId();
+
+ /**
+ * Gets this arena's sanitized name
+ *
+ * @return This arena's sanitized name
+ */
+ @NotNull String getArenaNameSanitized();
+
+ /**
+ * Removes the data file belonging to this arena
+ *
+ * @return True if successfully removed
+ */
+ boolean removeData();
+
+ /**
+ * Saves this arena's data
+ *
+ * @return True if successfully saved
+ */
+ boolean saveData();
+
+ /**
+ * Gets whether standing on the given block should cause a win
+ *
+ * @param block The block to check
+ * @return True if standing on the block will cause a win
+ */
+ boolean willCauseWin(Block block);
+
+ /**
+ * Gets whether standing on the given block should cause a loss
+ *
+ * @param block The block to check
+ * @return True if standing on the block will cause a loss
+ */
+ boolean willCauseLoss(Block block);
+
+ /**
+ * Gets whether the win location is a solid block
+ *
+ * @return True if the location is a solid block
+ */
+ boolean winLocationIsSolid();
+
+ /**
+ * Gets the location of this arena's spawn
+ *
+ * @return This arena's spawn location
+ */
+ @NotNull Location getSpawnLocation();
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/ArenaData.java b/src/main/java/net/knarcraft/minigames/arena/ArenaData.java
new file mode 100644
index 0000000..90a375b
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/ArenaData.java
@@ -0,0 +1,108 @@
+package net.knarcraft.minigames.arena;
+
+import net.knarcraft.minigames.container.SerializableContainer;
+import net.knarcraft.minigames.container.SerializableUUID;
+import org.bukkit.configuration.serialization.ConfigurationSerializable;
+import org.bukkit.entity.Player;
+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 static net.knarcraft.minigames.util.SerializableConverter.makeSerializable;
+
+/**
+ * An interface describing generic arena data
+ */
+public abstract class ArenaData implements ConfigurationSerializable {
+
+ protected final @NotNull UUID arenaId;
+ private final @NotNull Map recordRegistries;
+ private final @NotNull Map> playersCompleted;
+
+ /**
+ * Instantiates arena data
+ *
+ * @param arenaId The id of the arena this data belongs to
+ * @param recordRegistries The registry storing records for this arena
+ * @param playersCompleted The players that have completed this arena
+ */
+ public ArenaData(@NotNull UUID arenaId, @NotNull Map recordRegistries,
+ @NotNull Map> playersCompleted) {
+ this.arenaId = arenaId;
+ this.recordRegistries = recordRegistries;
+ this.playersCompleted = playersCompleted;
+ }
+
+ /**
+ * Gets the id of this arena
+ *
+ * @return The id of this arena
+ */
+ public @NotNull UUID getArenaId() {
+ return this.arenaId;
+ }
+
+ /**
+ * Gets all record registries
+ *
+ * @return All record registries
+ */
+ public @NotNull Map getRecordRegistries() {
+ return new HashMap<>(this.recordRegistries);
+ }
+
+ /**
+ * Gets whether the given player has cleared this arena
+ *
+ * @param arenaGameMode The game-mode to check for
+ * @param player The player to check
+ * @return True if the player has cleared the arena this data belongs to
+ */
+ public boolean hasNotCompleted(@NotNull ArenaGameMode arenaGameMode, @NotNull Player player) {
+ return !this.playersCompleted.getOrDefault(arenaGameMode, new HashSet<>()).contains(player.getUniqueId());
+ }
+
+ /**
+ * Registers the given player as having completed this arena
+ *
+ * @param arenaGameMode The game-mode the player completed
+ * @param player The player that completed this data's arena
+ */
+ public boolean setCompleted(@NotNull ArenaGameMode arenaGameMode, @NotNull Player player) {
+ // Make sure to add an empty set to prevent a NullPointerException
+ if (!this.playersCompleted.containsKey(arenaGameMode)) {
+ this.playersCompleted.put(arenaGameMode, new HashSet<>());
+ }
+
+ boolean added = this.playersCompleted.get(arenaGameMode).add(player.getUniqueId());
+ // Persistently save the completion
+ if (added) {
+ saveData();
+ }
+ return added;
+ }
+
+ /**
+ * Saves this data to disk
+ */
+ public abstract void saveData();
+
+ @NotNull
+ @Override
+ public Map serialize() {
+ Map data = new HashMap<>();
+ data.put("arenaId", new SerializableUUID(this.arenaId));
+ data.put("recordsRegistry", this.recordRegistries);
+
+ // Convert normal UUIDs to serializable UUIDs
+ Map>> serializablePlayersCompleted = new HashMap<>();
+ makeSerializable(this.playersCompleted, serializablePlayersCompleted, new SerializableUUID(null));
+ data.put("playersCompleted", serializablePlayersCompleted);
+ return data;
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/ArenaGameMode.java b/src/main/java/net/knarcraft/minigames/arena/ArenaGameMode.java
new file mode 100644
index 0000000..e90249b
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/ArenaGameMode.java
@@ -0,0 +1,24 @@
+package net.knarcraft.minigames.arena;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * An interface describing any arena game-mode
+ */
+public interface ArenaGameMode {
+
+ /**
+ * Gets the name of this game-mode
+ *
+ * @return The name of this game-mode
+ */
+ @NotNull String name();
+
+ /**
+ * Gets a set of all available arena game-modes in the type definition of this game-mode
+ *
+ * @return All game-modes in this game-mode's class
+ */
+ @NotNull ArenaGameMode[] getValues();
+
+}
diff --git a/src/main/java/net/knarcraft/dropper/arena/DropperArenaGroup.java b/src/main/java/net/knarcraft/minigames/arena/ArenaGroup.java
similarity index 68%
rename from src/main/java/net/knarcraft/dropper/arena/DropperArenaGroup.java
rename to src/main/java/net/knarcraft/minigames/arena/ArenaGroup.java
index d76987f..8bb10cb 100644
--- a/src/main/java/net/knarcraft/dropper/arena/DropperArenaGroup.java
+++ b/src/main/java/net/knarcraft/minigames/arena/ArenaGroup.java
@@ -1,9 +1,8 @@
-package net.knarcraft.dropper.arena;
+package net.knarcraft.minigames.arena;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.container.SerializableUUID;
-import net.knarcraft.dropper.property.ArenaGameMode;
-import net.knarcraft.dropper.util.StringSanitizer;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.container.SerializableUUID;
+import net.knarcraft.minigames.util.StringSanitizer;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -16,9 +15,12 @@ import java.util.UUID;
import java.util.logging.Level;
/**
- * A sorted group of arenas that must be completed in sequence
+ * A group containing a list of arenas
+ *
+ * @param The type of arena stored
+ * @param The type of arena group stored in the given arena handler
*/
-public class DropperArenaGroup implements ConfigurationSerializable {
+public abstract class ArenaGroup> implements ConfigurationSerializable {
/**
* The unique id for this group of arenas
@@ -30,6 +32,11 @@ public class DropperArenaGroup implements ConfigurationSerializable {
*/
private final String groupName;
+ /**
+ * The arena handler used to convert uuids to arenas
+ */
+ private final ArenaHandler arenaHandler;
+
/**
* The arenas in this group, ordered from stage 1 to stage n
*/
@@ -38,29 +45,34 @@ public class DropperArenaGroup implements ConfigurationSerializable {
/**
* Instantiates a new dropper arena group
*
- * @param groupName The name of this group
+ * @param groupName The name of this group
+ * @param arenaHandler The arena handler used to convert uuids to arenas
*/
- public DropperArenaGroup(@NotNull String groupName) {
+ protected ArenaGroup(@NotNull String groupName, @NotNull ArenaHandler arenaHandler) {
this.groupId = UUID.randomUUID();
this.groupName = groupName;
this.arenas = new ArrayList<>();
+ this.arenaHandler = arenaHandler;
}
/**
- * Instantiates a new dropper arena group
+ * Instantiates a new arena group
*
- * @param groupId The unique id of this group
- * @param groupName The name of this group
- * @param arenas The arenas in this group
+ * @param groupId The unique id of this group
+ * @param groupName The name of this group
+ * @param arenas The arenas in this group
+ * @param arenaHandler The arena handler used to convert uuids to arenas
*/
- private DropperArenaGroup(@NotNull UUID groupId, @NotNull String groupName, @NotNull List arenas) {
+ protected ArenaGroup(@NotNull UUID groupId, @NotNull String groupName, @NotNull List arenas,
+ @NotNull ArenaHandler arenaHandler) {
this.groupId = groupId;
this.groupName = groupName;
this.arenas = new ArrayList<>(arenas);
+ this.arenaHandler = arenaHandler;
}
/**
- * Gets the id of this dropper arena group
+ * Gets the id of this arena group
*
* @return The id of this group
*/
@@ -69,7 +81,7 @@ public class DropperArenaGroup implements ConfigurationSerializable {
}
/**
- * Gets the name of this dropper arena group
+ * Gets the name of this arena group
*
* @return The name of this group
*/
@@ -87,9 +99,9 @@ public class DropperArenaGroup implements ConfigurationSerializable {
}
/**
- * Removes the given dropper arena from this group
+ * Removes the given arena from this group
*
- * @param arenaId The id of the dropper arena to remove
+ * @param arenaId The id of the arena to remove
*/
public void removeArena(UUID arenaId) {
this.arenas.remove(arenaId);
@@ -117,6 +129,15 @@ public class DropperArenaGroup implements ConfigurationSerializable {
}
}
+ /**
+ * Gets this group's name, but sanitized
+ *
+ * @return The sanitized group name
+ */
+ public @NotNull String getGroupNameSanitized() {
+ return StringSanitizer.sanitizeArenaName(this.getGroupName());
+ }
+
/**
* Checks whether the given player has beaten all arenas in this group on the given game-mode
*
@@ -125,17 +146,16 @@ public class DropperArenaGroup implements ConfigurationSerializable {
* @return True if the player has beaten all arenas, false otherwise
*/
public boolean hasBeatenAll(ArenaGameMode gameMode, Player player) {
- DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
for (UUID anArenaId : this.getArenas()) {
- DropperArena dropperArena = arenaHandler.getArena(anArenaId);
- if (dropperArena == null) {
+ K arena = this.arenaHandler.getArena(anArenaId);
+ if (arena == null) {
// The arena would only be null if the arena has been deleted, but not removed from this group
- Dropper.getInstance().getLogger().log(Level.WARNING, "The dropper group " + this.getGroupName() +
+ MiniGames.log(Level.WARNING, "The dropper group " + this.getGroupName() +
" contains the arena id " + anArenaId + " which is not a valid arena id!");
continue;
}
- if (dropperArena.getData().hasNotCompleted(gameMode, player)) {
+ if (arena.getData().hasNotCompleted(gameMode, player)) {
return false;
}
}
@@ -151,43 +171,32 @@ public class DropperArenaGroup implements ConfigurationSerializable {
* @return True if the player is allowed to play the arena
* @throws IllegalArgumentException If checking an arena not in this group
*/
- public boolean canPlay(ArenaGameMode gameMode, Player player, UUID arenaId) throws IllegalArgumentException {
+ public boolean cannotPlay(ArenaGameMode gameMode, Player player, UUID arenaId) throws IllegalArgumentException {
if (!this.arenas.contains(arenaId)) {
throw new IllegalArgumentException("Cannot check for playability for arena not in this group!");
}
- DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
-
for (UUID anArenaId : this.getArenas()) {
// If the target arena is reached, allow, as all previous arenas must have been cleared
if (arenaId.equals(anArenaId)) {
- return true;
+ return false;
}
- DropperArena dropperArena = arenaHandler.getArena(anArenaId);
- if (dropperArena == null) {
+ K arena = this.arenaHandler.getArena(anArenaId);
+ if (arena == null) {
// The arena would only be null if the arena has been deleted, but not removed from this group
- Dropper.getInstance().getLogger().log(Level.WARNING, String.format("The dropper group %s contains the" +
+ MiniGames.log(Level.WARNING, String.format("The dropper group %s contains the" +
" arena id %s which is not a valid arena id!", this.getGroupName(), anArenaId));
continue;
}
// This is a lower-numbered arena the player has yet to complete
- if (dropperArena.getData().hasNotCompleted(gameMode, player)) {
- return false;
+ if (arena.getData().hasNotCompleted(gameMode, player)) {
+ return true;
}
}
- return false;
- }
-
- /**
- * Gets this group's name, but sanitized
- *
- * @return The sanitized group name
- */
- public @NotNull String getGroupNameSanitized() {
- return StringSanitizer.sanitizeArenaName(this.getGroupName());
+ return true;
}
/**
@@ -224,27 +233,9 @@ public class DropperArenaGroup implements ConfigurationSerializable {
return data;
}
- /**
- * Deserializes the given data
- *
- * @param data The data to deserialize
- * @return The deserialized arena group
- */
- @SuppressWarnings({"unused", "unchecked"})
- public static @NotNull DropperArenaGroup deserialize(@NotNull Map data) {
- UUID id = ((SerializableUUID) data.get("groupId")).uuid();
- String name = (String) data.get("groupName");
- List serializableArenas = (List) data.get("arenas");
- List arenas = new ArrayList<>();
- for (SerializableUUID arenaId : serializableArenas) {
- arenas.add(arenaId.uuid());
- }
- return new DropperArenaGroup(id, name, arenas);
- }
-
@Override
public boolean equals(Object other) {
- if (!(other instanceof DropperArenaGroup otherGroup)) {
+ if (!(other instanceof ArenaGroup, ?> otherGroup)) {
return false;
}
return this.getGroupNameSanitized().equals(otherGroup.getGroupNameSanitized());
diff --git a/src/main/java/net/knarcraft/dropper/arena/DropperArenaHandler.java b/src/main/java/net/knarcraft/minigames/arena/ArenaHandler.java
similarity index 51%
rename from src/main/java/net/knarcraft/dropper/arena/DropperArenaHandler.java
rename to src/main/java/net/knarcraft/minigames/arena/ArenaHandler.java
index b9b38c5..3c71900 100644
--- a/src/main/java/net/knarcraft/dropper/arena/DropperArenaHandler.java
+++ b/src/main/java/net/knarcraft/minigames/arena/ArenaHandler.java
@@ -1,12 +1,10 @@
-package net.knarcraft.dropper.arena;
+package net.knarcraft.minigames.arena;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.util.ArenaStorageHelper;
-import net.knarcraft.dropper.util.StringSanitizer;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.util.StringSanitizer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -15,21 +13,34 @@ import java.util.UUID;
import java.util.logging.Level;
/**
- * A handler that keeps track of all dropper arenas
+ * An interface describing a generic arena handler
+ *
+ * @param The type of arena stored
+ * @param The type of arena group stored
*/
-public class DropperArenaHandler {
+public abstract class ArenaHandler> {
- private Map arenas = new HashMap<>();
- private Map arenaGroups = new HashMap<>();
- private Map arenaNameLookup = new HashMap<>();
+ protected Map arenas = new HashMap<>();
+ protected Map arenaGroups = new HashMap<>();
+ protected Map arenaNameLookup = new HashMap<>();
+ private final ArenaPlayerRegistry playerRegistry;
+
+ /**
+ * Instantiates a new arena handler
+ *
+ * @param playerRegistry The registry keeping track of player sessions
+ */
+ public ArenaHandler(ArenaPlayerRegistry playerRegistry) {
+ this.playerRegistry = playerRegistry;
+ }
/**
* Gets all arenas that are within a group
*
* @return All arenas in a group
*/
- public @NotNull Set getArenasInAGroup() {
- Set arenas = new HashSet<>();
+ public @NotNull Set getArenasInAGroup() {
+ Set arenas = new HashSet<>();
for (UUID arenaId : arenaGroups.keySet()) {
arenas.add(this.arenas.get(arenaId));
}
@@ -37,11 +48,11 @@ public class DropperArenaHandler {
}
/**
- * Gets a copy of all dropper groups
+ * Gets a copy of all arena groups
*
- * @return All dropper groups
+ * @return All arena groups
*/
- public Set getAllGroups() {
+ public Set getAllGroups() {
return new HashSet<>(arenaGroups.values());
}
@@ -51,7 +62,7 @@ public class DropperArenaHandler {
* @param arenaId The id of the arena to get the group of
* @return The group the arena belongs to, or null if not in a group
*/
- public @Nullable DropperArenaGroup getGroup(@NotNull UUID arenaId) {
+ public @Nullable S getGroup(@NotNull UUID arenaId) {
return this.arenaGroups.get(arenaId);
}
@@ -61,7 +72,7 @@ public class DropperArenaHandler {
* @param arenaId The id of the arena to change
* @param arenaGroup The group to add the arena to, or null to remove the current group
*/
- public void setGroup(@NotNull UUID arenaId, @Nullable DropperArenaGroup arenaGroup) {
+ public void setGroup(@NotNull UUID arenaId, @Nullable S arenaGroup) {
if (arenaGroup == null) {
// No need to remove something non-existing
if (!this.arenaGroups.containsKey(arenaId)) {
@@ -69,7 +80,7 @@ public class DropperArenaHandler {
}
// Remove the existing group
- DropperArenaGroup oldGroup = this.arenaGroups.remove(arenaId);
+ S oldGroup = this.arenaGroups.remove(arenaId);
oldGroup.removeArena(arenaId);
} else {
// Make sure to remove the arena from the old group's internal tracking
@@ -84,14 +95,14 @@ public class DropperArenaHandler {
}
/**
- * Gets the dropper arena group with the given name
+ * Gets the arena group with the given name
*
* @param groupName The name of the group to get
* @return The group, or null if not found
*/
- public @Nullable DropperArenaGroup getGroup(String groupName) {
+ public @Nullable S getGroup(String groupName) {
String sanitized = StringSanitizer.sanitizeArenaName(groupName);
- for (DropperArenaGroup arenaGroup : this.arenaGroups.values()) {
+ for (S arenaGroup : this.arenaGroups.values()) {
if (arenaGroup.getGroupNameSanitized().equals(sanitized)) {
return arenaGroup;
}
@@ -117,7 +128,7 @@ public class DropperArenaHandler {
*
* @param arena The arena to add
*/
- public void addArena(@NotNull DropperArena arena) {
+ public void addArena(@NotNull K arena) {
this.arenas.put(arena.getArenaId(), arena);
this.arenaNameLookup.put(arena.getArenaNameSanitized(), arena.getArenaId());
this.saveArenas();
@@ -129,7 +140,7 @@ public class DropperArenaHandler {
* @param arenaId The id of the arena to get
* @return The arena, or null if no arena could be found
*/
- public @Nullable DropperArena getArena(@NotNull UUID arenaId) {
+ public @Nullable K getArena(@NotNull UUID arenaId) {
return this.arenas.get(arenaId);
}
@@ -139,7 +150,7 @@ public class DropperArenaHandler {
* @param arenaName The arena to get
* @return The arena with the given name, or null if not found
*/
- public @Nullable DropperArena getArena(@NotNull String arenaName) {
+ public @Nullable K getArena(@NotNull String arenaName) {
return this.arenas.get(this.arenaNameLookup.get(StringSanitizer.sanitizeArenaName(arenaName)));
}
@@ -148,7 +159,7 @@ public class DropperArenaHandler {
*
* @return All known arenas
*/
- public @NotNull Map getArenas() {
+ public @NotNull Map getArenas() {
return new HashMap<>(this.arenas);
}
@@ -157,15 +168,15 @@ public class DropperArenaHandler {
*
* @param arena The arena to remove
*/
- public void removeArena(@NotNull DropperArena arena) {
+ public void removeArena(@NotNull K arena) {
UUID arenaId = arena.getArenaId();
- Dropper.getInstance().getPlayerRegistry().removeForArena(arena);
+ this.playerRegistry.removeForArena(arena);
this.arenas.remove(arenaId);
this.arenaNameLookup.remove(arena.getArenaNameSanitized());
this.arenaGroups.remove(arenaId);
- if (!ArenaStorageHelper.removeArenaData(arenaId)) {
- Dropper.getInstance().getLogger().log(Level.WARNING, "Unable to remove dropper arena data file " +
- arenaId + ".yml. You must remove it manually!");
+ if (!arena.removeData()) {
+ MiniGames.log(Level.WARNING, "Unable to remove arena data file " + arenaId + ".yml. " +
+ "You must remove it manually!");
}
this.saveArenas();
}
@@ -176,66 +187,44 @@ public class DropperArenaHandler {
* @param arenaId The id of the arena whose data should be saved
*/
public void saveData(UUID arenaId) {
- try {
- ArenaStorageHelper.saveArenaData(this.arenas.get(arenaId).getData());
- } catch (IOException e) {
- Dropper.getInstance().getLogger().log(Level.SEVERE, "Unable to save arena data! Data loss can occur!");
- Dropper.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
- }
- }
-
- /**
- * Saves all current dropper groups to disk
- */
- public void saveGroups() {
- try {
- ArenaStorageHelper.saveDropperArenaGroups(new HashSet<>(this.arenaGroups.values()));
- } catch (IOException e) {
- Dropper.getInstance().getLogger().log(Level.SEVERE, "Unable to save current arena groups! " +
- "Data loss can occur!");
- Dropper.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
- }
- }
-
- /**
- * Loads all dropper groups from disk
- */
- public void loadGroups() {
- Set arenaGroups = ArenaStorageHelper.loadDropperArenaGroups();
- Map arenaGroupMap = new HashMap<>();
- for (DropperArenaGroup arenaGroup : arenaGroups) {
- for (UUID arenaId : arenaGroup.getArenas()) {
- arenaGroupMap.put(arenaId, arenaGroup);
+ K arena = getArena(arenaId);
+ if (arena != null) {
+ if (!arena.saveData()) {
+ MiniGames.log(Level.WARNING, "Unable to save data for arena with id " + arenaId +
+ " because of a write exception!");
}
+ } else {
+ MiniGames.log(Level.WARNING, "Unable to save data for arena with id " + arenaId +
+ " because the arena could not be found!");
}
- this.arenaGroups = arenaGroupMap;
+ }
+
+ /**
+ * Saves all current groups to disk
+ */
+ public abstract void saveGroups();
+
+ /**
+ * Loads all groups from disk
+ */
+ protected abstract void loadGroups();
+
+ /**
+ * Loads all arenas and groups from disk
+ */
+ public void load() {
+ loadArenas();
+ loadGroups();
}
/**
* Saves all current arenas to disk
*/
- public void saveArenas() {
- try {
- ArenaStorageHelper.saveArenas(this.arenas);
- } catch (IOException e) {
- Dropper.getInstance().getLogger().log(Level.SEVERE, "Unable to save current arenas! " +
- "Data loss can occur!");
- Dropper.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
- }
- }
+ public abstract void saveArenas();
/**
* Loads all arenas from disk
*/
- public void loadArenas() {
- this.arenas = ArenaStorageHelper.loadArenas();
-
- // Save a map from arena name to arena id for improved performance
- this.arenaNameLookup = new HashMap<>();
- for (Map.Entry arena : this.arenas.entrySet()) {
- String sanitizedName = arena.getValue().getArenaNameSanitized();
- this.arenaNameLookup.put(sanitizedName, arena.getKey());
- }
- }
+ protected abstract void loadArenas();
}
diff --git a/src/main/java/net/knarcraft/minigames/arena/ArenaPlayerRegistry.java b/src/main/java/net/knarcraft/minigames/arena/ArenaPlayerRegistry.java
new file mode 100644
index 0000000..d7734a6
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/ArenaPlayerRegistry.java
@@ -0,0 +1,17 @@
+package net.knarcraft.minigames.arena;
+
+/**
+ * A registry keeping track of all player sessions for some arenas
+ *
+ * @param The type of arena this registry stores
+ */
+public interface ArenaPlayerRegistry {
+
+ /**
+ * Removes all active sessions for the given arena
+ *
+ * @param arena The arena to remove sessions for
+ */
+ void removeForArena(K arena);
+
+}
diff --git a/src/main/java/net/knarcraft/dropper/arena/DropperArenaRecordsRegistry.java b/src/main/java/net/knarcraft/minigames/arena/ArenaRecordsRegistry.java
similarity index 73%
rename from src/main/java/net/knarcraft/dropper/arena/DropperArenaRecordsRegistry.java
rename to src/main/java/net/knarcraft/minigames/arena/ArenaRecordsRegistry.java
index 829aacc..e1ed506 100644
--- a/src/main/java/net/knarcraft/dropper/arena/DropperArenaRecordsRegistry.java
+++ b/src/main/java/net/knarcraft/minigames/arena/ArenaRecordsRegistry.java
@@ -1,12 +1,11 @@
-package net.knarcraft.dropper.arena;
+package net.knarcraft.minigames.arena;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.record.ArenaRecord;
-import net.knarcraft.dropper.arena.record.IntegerRecord;
-import net.knarcraft.dropper.arena.record.LongRecord;
-import net.knarcraft.dropper.arena.record.SummableArenaRecord;
-import net.knarcraft.dropper.container.SerializableUUID;
-import net.knarcraft.dropper.property.RecordResult;
+import net.knarcraft.minigames.arena.record.ArenaRecord;
+import net.knarcraft.minigames.arena.record.IntegerRecord;
+import net.knarcraft.minigames.arena.record.LongRecord;
+import net.knarcraft.minigames.arena.record.SummableArenaRecord;
+import net.knarcraft.minigames.container.SerializableUUID;
+import net.knarcraft.minigames.property.RecordResult;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -14,25 +13,24 @@ import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
/**
- * A registry keeping track of all records
+ * A records registry storing records for an arena
*/
-public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
+public abstract class ArenaRecordsRegistry implements ConfigurationSerializable {
- private final UUID arenaId;
+ protected final UUID arenaId;
private final @NotNull Set leastDeaths;
private final @NotNull Set shortestTimeMilliSeconds;
/**
* Instantiates a new empty records registry
*/
- public DropperArenaRecordsRegistry(@NotNull UUID arenaId) {
+ public ArenaRecordsRegistry(@NotNull UUID arenaId) {
this.arenaId = arenaId;
this.leastDeaths = new HashSet<>();
this.shortestTimeMilliSeconds = new HashSet<>();
@@ -44,8 +42,8 @@ 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 Set leastDeaths,
- @NotNull Set shortestTimeMilliSeconds) {
+ protected ArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Set leastDeaths,
+ @NotNull Set shortestTimeMilliSeconds) {
this.arenaId = arenaId;
this.leastDeaths = new HashSet<>(leastDeaths);
this.shortestTimeMilliSeconds = new HashSet<>(shortestTimeMilliSeconds);
@@ -81,8 +79,7 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
leastDeaths.removeIf((item) -> item.getUserId().equals(playerId));
leastDeaths.add(new IntegerRecord(playerId, value));
};
- Set> asInt = new HashSet<>(leastDeaths);
- return registerRecord(asInt, consumer, playerId, deaths);
+ return registerRecord(new HashSet<>(leastDeaths), consumer, playerId, deaths);
}
/**
@@ -97,16 +94,13 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
shortestTimeMilliSeconds.removeIf((item) -> item.getUserId().equals(playerId));
shortestTimeMilliSeconds.add(new LongRecord(playerId, value));
};
- Set> asLong = new HashSet<>(shortestTimeMilliSeconds);
- return registerRecord(asLong, consumer, playerId, milliseconds);
+ return registerRecord(new HashSet<>(shortestTimeMilliSeconds), consumer, playerId, milliseconds);
}
/**
* Saves changed records
*/
- private void save() {
- Dropper.getInstance().getArenaHandler().saveData(this.arenaId);
- }
+ protected abstract void save();
/**
* Registers a new record if applicable
@@ -176,23 +170,4 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
return data;
}
- /**
- * Deserializes the given data
- *
- * @param data The data to deserialize
- * @return The deserialized records registry
- */
- @SuppressWarnings({"unused", "unchecked"})
- public static DropperArenaRecordsRegistry deserialize(Map data) {
- UUID arenaId = ((SerializableUUID) data.get("arenaId")).uuid();
- Set leastDeaths =
- (Set) data.getOrDefault("leastDeaths", new HashMap<>());
- Set shortestTimeMilliseconds =
- (Set) data.getOrDefault("shortestTime", new HashMap<>());
-
- leastDeaths.removeIf(Objects::isNull);
- shortestTimeMilliseconds.removeIf(Objects::isNull);
- return new DropperArenaRecordsRegistry(arenaId, leastDeaths, shortestTimeMilliseconds);
- }
-
}
diff --git a/src/main/java/net/knarcraft/minigames/arena/ArenaSession.java b/src/main/java/net/knarcraft/minigames/arena/ArenaSession.java
new file mode 100644
index 0000000..ffe4a99
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/ArenaSession.java
@@ -0,0 +1,56 @@
+package net.knarcraft.minigames.arena;
+
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * A player's session while in an arena
+ */
+public interface ArenaSession {
+
+ /**
+ * Gets the game-mode the player is playing in this session
+ *
+ * @return The game-mode for this session
+ */
+ @NotNull ArenaGameMode getGameMode();
+
+ /**
+ * Gets the state of the player when they joined the session
+ *
+ * @return The player's entry state
+ */
+ @NotNull PlayerEntryState getEntryState();
+
+ /**
+ * Triggers a win for the player playing in this session
+ */
+ void triggerWin();
+
+ /**
+ * Triggers a loss for the player playing in this session
+ */
+ void triggerLoss();
+
+ /**
+ * Triggers a quit for the player playing in this session
+ *
+ * @param immediately Whether to to the teleportation immediately, not using any timers
+ */
+ void triggerQuit(boolean immediately);
+
+ /**
+ * Gets the arena this session is being played in
+ *
+ * @return The session's arena
+ */
+ @NotNull Arena getArena();
+
+ /**
+ * Gets the player playing in this session
+ *
+ * @return This session's player
+ */
+ @NotNull Player getPlayer();
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/PlayerEntryState.java b/src/main/java/net/knarcraft/minigames/arena/PlayerEntryState.java
new file mode 100644
index 0000000..cfbc91f
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/PlayerEntryState.java
@@ -0,0 +1,27 @@
+package net.knarcraft.minigames.arena;
+
+import org.bukkit.Location;
+
+/**
+ * The stored state of a player
+ */
+public interface PlayerEntryState {
+
+ /**
+ * Sets the state of the stored player to the state used by the arena
+ */
+ void setArenaState();
+
+ /**
+ * Restores the stored state for the stored player
+ */
+ void restore();
+
+ /**
+ * Gets the location the player entered from
+ *
+ * @return The location the player entered from
+ */
+ Location getEntryLocation();
+
+}
diff --git a/src/main/java/net/knarcraft/dropper/arena/DropperArena.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArena.java
similarity index 82%
rename from src/main/java/net/knarcraft/dropper/arena/DropperArena.java
rename to src/main/java/net/knarcraft/minigames/arena/dropper/DropperArena.java
index 8caffc1..60cb457 100644
--- a/src/main/java/net/knarcraft/dropper/arena/DropperArena.java
+++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArena.java
@@ -1,21 +1,29 @@
-package net.knarcraft.dropper.arena;
+package net.knarcraft.minigames.arena.dropper;
-import net.knarcraft.dropper.property.ArenaGameMode;
-import net.knarcraft.dropper.util.StringSanitizer;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.Arena;
+import net.knarcraft.minigames.arena.ArenaGameMode;
+import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
+import net.knarcraft.minigames.config.DropperConfiguration;
+import net.knarcraft.minigames.util.DropperArenaStorageHelper;
+import net.knarcraft.minigames.util.StringSanitizer;
import org.bukkit.Location;
import org.bukkit.Material;
-import org.bukkit.World;
+import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
+import static net.knarcraft.minigames.util.InputValidationHelper.isInvalid;
+
/**
* A representation of one dropper arena
*/
-public class DropperArena {
+public class DropperArena implements Arena {
/**
* An unique and persistent identifier for this arena
@@ -62,6 +70,8 @@ public class DropperArena {
private final DropperArenaHandler dropperArenaHandler;
+ private static final DropperConfiguration dropperConfiguration = MiniGames.getInstance().getDropperConfiguration();
+
/**
* Instantiates a new dropper arena
*
@@ -102,15 +112,16 @@ public class DropperArena {
*/
public DropperArena(@NotNull String arenaName, @NotNull Location spawnLocation,
@NotNull DropperArenaHandler arenaHandler) {
+ DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration();
this.arenaId = UUID.randomUUID();
this.arenaName = arenaName;
this.spawnLocation = spawnLocation;
this.exitLocation = null;
- this.playerVerticalVelocity = 3.92;
- this.playerHorizontalVelocity = 1;
+ this.playerVerticalVelocity = configuration.getVerticalVelocity();
+ this.playerHorizontalVelocity = configuration.getHorizontalVelocity();
- Map recordRegistries = new HashMap<>();
- for (ArenaGameMode arenaGameMode : ArenaGameMode.values()) {
+ Map recordRegistries = new HashMap<>();
+ for (ArenaGameMode arenaGameMode : DropperArenaGameMode.values()) {
recordRegistries.put(arenaGameMode, new DropperArenaRecordsRegistry(this.arenaId));
}
@@ -119,42 +130,24 @@ public class DropperArena {
this.dropperArenaHandler = arenaHandler;
}
- /**
- * Gets this arena's data
- *
- * @return This arena's data
- */
+ @Override
public @NotNull DropperArenaData getData() {
return this.dropperArenaData;
}
- /**
- * Gets the id of this arena
- *
- * @return This arena's identifier
- */
+ @Override
public @NotNull UUID getArenaId() {
return this.arenaId;
}
- /**
- * Gets the name of this arena
- *
- * @return The name of this arena
- */
+ @Override
public @NotNull String getArenaName() {
return this.arenaName;
}
- /**
- * Gets this arena's spawn location
- *
- * The spawn location is the location every player starts from when entering the dropper.
- *
- * @return This arena's spawn location.
- */
+ @Override
public @NotNull Location getSpawnLocation() {
- return this.spawnLocation;
+ return this.spawnLocation.clone();
}
/**
@@ -163,7 +156,7 @@ public class DropperArena {
* @return This arena's exit location, or null if no such location is set.
*/
public @Nullable Location getExitLocation() {
- return this.exitLocation;
+ return this.exitLocation != null ? this.exitLocation.clone() : null;
}
/**
@@ -207,6 +200,36 @@ public class DropperArena {
return StringSanitizer.sanitizeArenaName(this.getArenaName());
}
+ @Override
+ public boolean removeData() {
+ return DropperArenaStorageHelper.removeDropperArenaData(getArenaId());
+ }
+
+ @Override
+ public boolean saveData() {
+ try {
+ DropperArenaStorageHelper.saveDropperArenaData(getData());
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean willCauseWin(Block block) {
+ return block.getType() == winBlockType;
+ }
+
+ @Override
+ public boolean willCauseLoss(Block block) {
+ return !dropperConfiguration.getBlockWhitelist().contains(block.getType());
+ }
+
+ @Override
+ public boolean winLocationIsSolid() {
+ return winBlockType.isSolid();
+ }
+
/**
* Sets the spawn location for this arena
*
@@ -318,15 +341,4 @@ public class DropperArena {
return this.getArenaNameSanitized().equals(otherArena.getArenaNameSanitized());
}
- /**
- * Checks whether the given location is valid
- *
- * @param location The location to validate
- * @return False if the location is valid
- */
- private boolean isInvalid(Location location) {
- World world = location.getWorld();
- return world == null || !world.getWorldBorder().isInside(location);
- }
-
}
diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaData.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaData.java
new file mode 100644
index 0000000..c6aa6cb
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaData.java
@@ -0,0 +1,72 @@
+package net.knarcraft.minigames.arena.dropper;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaData;
+import net.knarcraft.minigames.arena.ArenaGameMode;
+import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
+import net.knarcraft.minigames.container.SerializableContainer;
+import net.knarcraft.minigames.container.SerializableUUID;
+import net.knarcraft.minigames.util.SerializableConverter;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Data stored for an arena
+ */
+public class DropperArenaData extends ArenaData {
+
+ /**
+ * Instantiates a new dropper arena data object
+ *
+ * @param arenaId The id of the arena this data belongs to
+ * @param recordRegistries The registries of this arena's records
+ * @param playersCompleted The set of the players that have cleared this arena for each game-mode
+ */
+ public DropperArenaData(@NotNull UUID arenaId,
+ @NotNull Map recordRegistries,
+ @NotNull Map> playersCompleted) {
+ super(arenaId, recordRegistries, playersCompleted);
+ }
+
+ @Override
+ public void saveData() {
+ MiniGames.getInstance().getDropperArenaHandler().saveData(this.arenaId);
+ }
+
+ /**
+ * Deserializes a dropper arena data from the given data
+ *
+ * @param data The data to deserialize
+ * @return The deserialized dropper arena data
+ */
+ @SuppressWarnings({"unused", "unchecked"})
+ public static @NotNull DropperArenaData deserialize(@NotNull Map data) {
+ SerializableUUID serializableUUID = (SerializableUUID) data.get("arenaId");
+ Map recordsRegistry =
+ (Map) data.get("recordsRegistry");
+ Map>> playersCompletedData =
+ (Map>>) data.get("playersCompleted");
+
+ if (recordsRegistry == null) {
+ recordsRegistry = new HashMap<>();
+ } else if (playersCompletedData == null) {
+ playersCompletedData = new HashMap<>();
+ }
+
+ // Convert the serializable UUIDs to normal UUIDs
+ Map> allPlayersCompleted = new HashMap<>();
+ SerializableConverter.getRawValue(playersCompletedData, allPlayersCompleted);
+
+ for (ArenaGameMode arenaGameMode : playersCompletedData.keySet()) {
+ if (!recordsRegistry.containsKey(arenaGameMode) || recordsRegistry.get(arenaGameMode) == null) {
+ recordsRegistry.put(arenaGameMode, new DropperArenaRecordsRegistry(serializableUUID.getRawValue()));
+ }
+ }
+ return new DropperArenaData(serializableUUID.getRawValue(), recordsRegistry, allPlayersCompleted);
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/dropper/property/ArenaEditableProperty.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaEditableProperty.java
similarity index 85%
rename from src/main/java/net/knarcraft/dropper/property/ArenaEditableProperty.java
rename to src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaEditableProperty.java
index 7803021..9e77748 100644
--- a/src/main/java/net/knarcraft/dropper/property/ArenaEditableProperty.java
+++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaEditableProperty.java
@@ -1,6 +1,5 @@
-package net.knarcraft.dropper.property;
+package net.knarcraft.minigames.arena.dropper;
-import net.knarcraft.dropper.arena.DropperArena;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -9,7 +8,7 @@ import java.util.function.Function;
/**
* All editable properties of a dropper arena
*/
-public enum ArenaEditableProperty {
+public enum DropperArenaEditableProperty {
/**
* The name of the arena
@@ -50,7 +49,7 @@ public enum ArenaEditableProperty {
*
* @param argumentString The argument string used to specify this property
*/
- ArenaEditableProperty(@NotNull String argumentString, Function currentValueProvider) {
+ DropperArenaEditableProperty(@NotNull String argumentString, Function currentValueProvider) {
this.argumentString = argumentString;
this.currentValueProvider = currentValueProvider;
}
@@ -80,8 +79,8 @@ public enum ArenaEditableProperty {
* @param argumentString The argument string used to specify an editable property
* @return The corresponding editable property, or null if not found
*/
- public static @Nullable ArenaEditableProperty getFromArgumentString(String argumentString) {
- for (ArenaEditableProperty property : ArenaEditableProperty.values()) {
+ public static @Nullable DropperArenaEditableProperty getFromArgumentString(String argumentString) {
+ for (DropperArenaEditableProperty property : DropperArenaEditableProperty.values()) {
if (property.argumentString.equalsIgnoreCase(argumentString)) {
return property;
}
diff --git a/src/main/java/net/knarcraft/dropper/property/ArenaGameMode.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaGameMode.java
similarity index 66%
rename from src/main/java/net/knarcraft/dropper/property/ArenaGameMode.java
rename to src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaGameMode.java
index 37480fa..9e43f85 100644
--- a/src/main/java/net/knarcraft/dropper/property/ArenaGameMode.java
+++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaGameMode.java
@@ -1,5 +1,6 @@
-package net.knarcraft.dropper.property;
+package net.knarcraft.minigames.arena.dropper;
+import net.knarcraft.minigames.arena.ArenaGameMode;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.jetbrains.annotations.NotNull;
@@ -9,7 +10,7 @@ import java.util.Map;
/**
* A representation of possible arena game-modes
*/
-public enum ArenaGameMode implements ConfigurationSerializable {
+public enum DropperArenaGameMode implements ConfigurationSerializable, ArenaGameMode {
/**
* The default game-mode. Failing once throws the player out.
@@ -33,14 +34,14 @@ public enum ArenaGameMode implements ConfigurationSerializable {
* @param gameMode The game-mode string to match
* @return The specified arena game-mode
*/
- public static @NotNull ArenaGameMode matchGamemode(@NotNull String gameMode) {
+ public static @NotNull DropperArenaGameMode matchGamemode(@NotNull String gameMode) {
String sanitized = gameMode.trim().toLowerCase();
if (sanitized.matches("(invert(ed)?|inverse)")) {
- return ArenaGameMode.INVERTED;
+ return DropperArenaGameMode.INVERTED;
} else if (sanitized.matches("rand(om)?")) {
- return ArenaGameMode.RANDOM_INVERTED;
+ return DropperArenaGameMode.RANDOM_INVERTED;
} else {
- return ArenaGameMode.DEFAULT;
+ return DropperArenaGameMode.DEFAULT;
}
}
@@ -59,8 +60,13 @@ public enum ArenaGameMode implements ConfigurationSerializable {
* @return The deserialized arena game-mode
*/
@SuppressWarnings("unused")
- public static ArenaGameMode deserialize(Map data) {
- return ArenaGameMode.valueOf((String) data.get("name"));
+ public static DropperArenaGameMode deserialize(Map data) {
+ return DropperArenaGameMode.valueOf((String) data.get("name"));
+ }
+
+ @Override
+ public @NotNull DropperArenaGameMode[] getValues() {
+ return DropperArenaGameMode.values();
}
}
diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaGroup.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaGroup.java
new file mode 100644
index 0000000..4ed2236
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaGroup.java
@@ -0,0 +1,56 @@
+package net.knarcraft.minigames.arena.dropper;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaGroup;
+import net.knarcraft.minigames.container.SerializableUUID;
+import net.knarcraft.minigames.util.SerializableConverter;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * A sorted group of arenas that must be completed in sequence
+ */
+public class DropperArenaGroup extends ArenaGroup {
+
+ /**
+ * Instantiates a new dropper arena group
+ *
+ * @param groupName The name of this group
+ */
+ public DropperArenaGroup(@NotNull String groupName) {
+ super(groupName, MiniGames.getInstance().getDropperArenaHandler());
+ }
+
+ /**
+ * Instantiates a new dropper arena group
+ *
+ * @param groupId The unique id of this group
+ * @param groupName The name of this group
+ * @param arenas The arenas in this group
+ */
+ private DropperArenaGroup(@NotNull UUID groupId, @NotNull String groupName, @NotNull List arenas) {
+ super(groupId, groupName, arenas, MiniGames.getInstance().getDropperArenaHandler());
+ }
+
+ /**
+ * Deserializes the given data
+ *
+ * @param data The data to deserialize
+ * @return The deserialized arena group
+ */
+ @SuppressWarnings({"unused", "unchecked"})
+ public static @NotNull DropperArenaGroup deserialize(@NotNull Map data) {
+ UUID id = ((SerializableUUID) data.get("groupId")).getRawValue();
+ String name = (String) data.get("groupName");
+ List serializableArenas = (List) data.get("arenas");
+ List arenas = new ArrayList<>();
+
+ SerializableConverter.getRawValue(new ArrayList<>(serializableArenas), arenas);
+ return new DropperArenaGroup(id, name, arenas);
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaHandler.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaHandler.java
new file mode 100644
index 0000000..e2e4eac
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaHandler.java
@@ -0,0 +1,75 @@
+package net.knarcraft.minigames.arena.dropper;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaHandler;
+import net.knarcraft.minigames.util.DropperArenaStorageHelper;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.logging.Level;
+
+/**
+ * A handler that keeps track of all dropper arenas
+ */
+public class DropperArenaHandler extends ArenaHandler {
+
+ /**
+ * Instantiates a new arena handler
+ *
+ * @param playerRegistry The registry keeping track of player sessions
+ */
+ public DropperArenaHandler(DropperArenaPlayerRegistry playerRegistry) {
+ super(playerRegistry);
+ }
+
+ @Override
+ public void saveGroups() {
+ try {
+ DropperArenaStorageHelper.saveDropperArenaGroups(new HashSet<>(this.arenaGroups.values()));
+ } catch (IOException e) {
+ MiniGames.log(Level.SEVERE, "Unable to save current arena groups! " +
+ "Data loss can occur!");
+ MiniGames.log(Level.SEVERE, e.getMessage());
+ }
+ }
+
+ @Override
+ protected void loadGroups() {
+ Set arenaGroups = DropperArenaStorageHelper.loadDropperArenaGroups();
+ Map arenaGroupMap = new HashMap<>();
+ for (DropperArenaGroup arenaGroup : arenaGroups) {
+ for (UUID arenaId : arenaGroup.getArenas()) {
+ arenaGroupMap.put(arenaId, arenaGroup);
+ }
+ }
+ this.arenaGroups = arenaGroupMap;
+ }
+
+ @Override
+ public void saveArenas() {
+ try {
+ DropperArenaStorageHelper.saveDropperArenas(this.arenas);
+ } catch (IOException e) {
+ MiniGames.log(Level.SEVERE, "Unable to save current arenas! " +
+ "Data loss can occur!");
+ MiniGames.log(Level.SEVERE, e.getMessage());
+ }
+ }
+
+ @Override
+ protected void loadArenas() {
+ this.arenas = DropperArenaStorageHelper.loadDropperArenas();
+
+ // Save a map from arena name to arena id for improved performance
+ this.arenaNameLookup = new HashMap<>();
+ for (Map.Entry arena : this.arenas.entrySet()) {
+ String sanitizedName = arena.getValue().getArenaNameSanitized();
+ this.arenaNameLookup.put(sanitizedName, arena.getKey());
+ }
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/dropper/arena/DropperArenaPlayerRegistry.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaPlayerRegistry.java
similarity index 90%
rename from src/main/java/net/knarcraft/dropper/arena/DropperArenaPlayerRegistry.java
rename to src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaPlayerRegistry.java
index abdb7ab..8c3b327 100644
--- a/src/main/java/net/knarcraft/dropper/arena/DropperArenaPlayerRegistry.java
+++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaPlayerRegistry.java
@@ -1,5 +1,6 @@
-package net.knarcraft.dropper.arena;
+package net.knarcraft.minigames.arena.dropper;
+import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -10,7 +11,7 @@ import java.util.UUID;
/**
* A registry to keep track of which players are playing in which arenas
*/
-public class DropperArenaPlayerRegistry {
+public class DropperArenaPlayerRegistry implements ArenaPlayerRegistry {
private final Map arenaPlayers = new HashMap<>();
diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaRecordsRegistry.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaRecordsRegistry.java
new file mode 100644
index 0000000..b23266b
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaRecordsRegistry.java
@@ -0,0 +1,65 @@
+package net.knarcraft.minigames.arena.dropper;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
+import net.knarcraft.minigames.arena.record.IntegerRecord;
+import net.knarcraft.minigames.arena.record.LongRecord;
+import net.knarcraft.minigames.container.SerializableUUID;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * A registry keeping track of all records
+ */
+public class DropperArenaRecordsRegistry extends ArenaRecordsRegistry {
+
+ /**
+ * Instantiates a new empty records registry
+ */
+ public DropperArenaRecordsRegistry(@NotNull UUID arenaId) {
+ super(arenaId);
+ }
+
+ /**
+ * Instantiates a new records registry
+ *
+ * @param leastDeaths The existing least death records to use
+ * @param shortestTimeMilliSeconds The existing leash time records to use
+ */
+ private DropperArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Set leastDeaths,
+ @NotNull Set shortestTimeMilliSeconds) {
+ super(arenaId, leastDeaths, shortestTimeMilliSeconds);
+ }
+
+ /**
+ * Saves changed records
+ */
+ protected void save() {
+ MiniGames.getInstance().getDropperArenaHandler().saveData(this.arenaId);
+ }
+
+ /**
+ * Deserializes the given data
+ *
+ * @param data The data to deserialize
+ * @return The deserialized records registry
+ */
+ @SuppressWarnings({"unused", "unchecked"})
+ public static DropperArenaRecordsRegistry deserialize(Map data) {
+ UUID arenaId = ((SerializableUUID) data.get("arenaId")).getRawValue();
+ Set leastDeaths =
+ (Set) data.getOrDefault("leastDeaths", new HashMap<>());
+ Set shortestTimeMilliseconds =
+ (Set) data.getOrDefault("shortestTime", new HashMap<>());
+
+ leastDeaths.removeIf(Objects::isNull);
+ shortestTimeMilliseconds.removeIf(Objects::isNull);
+ return new DropperArenaRecordsRegistry(arenaId, leastDeaths, shortestTimeMilliseconds);
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/dropper/arena/DropperArenaSession.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaSession.java
similarity index 71%
rename from src/main/java/net/knarcraft/dropper/arena/DropperArenaSession.java
rename to src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaSession.java
index 7ece5d9..2ff11c3 100644
--- a/src/main/java/net/knarcraft/dropper/arena/DropperArenaSession.java
+++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaSession.java
@@ -1,9 +1,12 @@
-package net.knarcraft.dropper.arena;
+package net.knarcraft.minigames.arena.dropper;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.property.ArenaGameMode;
-import net.knarcraft.dropper.property.RecordResult;
-import net.knarcraft.dropper.util.PlayerTeleporter;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
+import net.knarcraft.minigames.arena.ArenaSession;
+import net.knarcraft.minigames.arena.PlayerEntryState;
+import net.knarcraft.minigames.config.DropperConfiguration;
+import net.knarcraft.minigames.property.RecordResult;
+import net.knarcraft.minigames.util.PlayerTeleporter;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -13,11 +16,11 @@ import java.util.logging.Level;
/**
* A representation of a player's current session in a dropper arena
*/
-public class DropperArenaSession {
+public class DropperArenaSession implements ArenaSession {
private final @NotNull DropperArena arena;
private final @NotNull Player player;
- private final @NotNull ArenaGameMode gameMode;
+ private final @NotNull DropperArenaGameMode gameMode;
private int deaths;
private final long startTime;
private final PlayerEntryState entryState;
@@ -30,16 +33,20 @@ public class DropperArenaSession {
* @param gameMode The game-mode
*/
public DropperArenaSession(@NotNull DropperArena dropperArena, @NotNull Player player,
- @NotNull ArenaGameMode gameMode) {
+ @NotNull DropperArenaGameMode gameMode) {
this.arena = dropperArena;
this.player = player;
this.gameMode = gameMode;
this.deaths = 0;
this.startTime = System.currentTimeMillis();
- this.entryState = new PlayerEntryState(player, gameMode);
+ DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration();
+ boolean makeInvisible = configuration.makePlayersInvisible();
+ boolean disableCollision = configuration.disableHitCollision();
+ this.entryState = new DropperPlayerEntryState(player, gameMode, makeInvisible, disableCollision,
+ dropperArena.getPlayerHorizontalVelocity());
// Make the player fly to improve mobility in the air
- this.entryState.setArenaState(this.arena.getPlayerHorizontalVelocity());
+ this.entryState.setArenaState();
}
/**
@@ -47,7 +54,7 @@ public class DropperArenaSession {
*
* @return The game-mode for this session
*/
- public @NotNull ArenaGameMode getGameMode() {
+ public @NotNull DropperArenaGameMode getGameMode() {
return this.gameMode;
}
@@ -68,10 +75,15 @@ public class DropperArenaSession {
stopSession();
// Check for, and display, records
- registerRecord();
+ MiniGames miniGames = MiniGames.getInstance();
+ boolean ignore = miniGames.getDropperConfiguration().ignoreRecordsUntilGroupBeatenOnce();
+ DropperArenaGroup group = miniGames.getDropperArenaHandler().getGroup(this.arena.getArenaId());
+ if (!ignore || group == null || group.hasBeatenAll(this.gameMode, this.player)) {
+ registerRecord();
+ }
// Mark the arena as cleared
- if (this.arena.getData().addCompleted(this.gameMode, this.player)) {
+ if (this.arena.getData().setCompleted(this.gameMode, this.player)) {
this.player.sendMessage("You cleared the arena!");
}
this.player.sendMessage("You won!");
@@ -82,6 +94,8 @@ public class DropperArenaSession {
/**
* Teleports the playing player out of the arena
+ *
+ * @param immediately Whether to to the teleportation immediately, not using any timers
*/
private void teleportToExit(boolean immediately) {
// Teleport the player out of the arena
@@ -99,10 +113,10 @@ public class DropperArenaSession {
*/
private void removeSession() {
// Remove this session for game sessions to stop listeners from fiddling more with the player
- boolean removedSession = Dropper.getInstance().getPlayerRegistry().removePlayer(player.getUniqueId());
+ boolean removedSession = MiniGames.getInstance().getDropperArenaPlayerRegistry().removePlayer(player.getUniqueId());
if (!removedSession) {
- Dropper.getInstance().getLogger().log(Level.SEVERE, "Unable to remove dropper arena session for " +
- player.getName() + ". This will have unintended consequences.");
+ MiniGames.log(Level.SEVERE, "Unable to remove dropper arena session for " + player.getName() + ". " +
+ "This will have unintended consequences.");
}
}
@@ -110,7 +124,7 @@ public class DropperArenaSession {
* Registers the player's record if necessary, and prints record information to the player
*/
private void registerRecord() {
- DropperArenaRecordsRegistry recordsRegistry = this.arena.getData().recordRegistries().get(this.gameMode);
+ ArenaRecordsRegistry recordsRegistry = this.arena.getData().getRecordRegistries().get(this.gameMode);
long timeElapsed = System.currentTimeMillis() - this.startTime;
announceRecord(recordsRegistry.registerTimeRecord(this.player.getUniqueId(), timeElapsed), "time");
announceRecord(recordsRegistry.registerDeathRecord(this.player.getUniqueId(), this.deaths), "least deaths");
@@ -150,11 +164,13 @@ public class DropperArenaSession {
this.deaths++;
//Teleport the player back to the top
PlayerTeleporter.teleportPlayer(this.player, this.arena.getSpawnLocation(), true, false);
- this.entryState.setArenaState(this.arena.getPlayerHorizontalVelocity());
+ this.entryState.setArenaState();
}
/**
* Triggers a quit for the player playing in this session
+ *
+ * @param immediately Whether to to the teleportation immediately, not using any timers
*/
public void triggerQuit(boolean immediately) {
// Stop this session
diff --git a/src/main/java/net/knarcraft/dropper/property/ArenaStorageKey.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaStorageKey.java
similarity index 91%
rename from src/main/java/net/knarcraft/dropper/property/ArenaStorageKey.java
rename to src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaStorageKey.java
index a97de1e..c8299a8 100644
--- a/src/main/java/net/knarcraft/dropper/property/ArenaStorageKey.java
+++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaStorageKey.java
@@ -1,11 +1,11 @@
-package net.knarcraft.dropper.property;
+package net.knarcraft.minigames.arena.dropper;
import org.jetbrains.annotations.NotNull;
/**
* A representation of each key used for storing arena data
*/
-public enum ArenaStorageKey {
+public enum DropperArenaStorageKey {
/**
* The key for an arena's id
@@ -55,7 +55,7 @@ public enum ArenaStorageKey {
*
* @param key The string path of the configuration key this value represents.
*/
- ArenaStorageKey(@NotNull String key) {
+ DropperArenaStorageKey(@NotNull String key) {
this.key = key;
}
diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperPlayerEntryState.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperPlayerEntryState.java
new file mode 100644
index 0000000..c72fedb
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperPlayerEntryState.java
@@ -0,0 +1,57 @@
+package net.knarcraft.minigames.arena.dropper;
+
+import net.knarcraft.minigames.arena.AbstractPlayerEntryState;
+import org.bukkit.GameMode;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * The state of a player before entering a dropper arena
+ */
+public class DropperPlayerEntryState extends AbstractPlayerEntryState {
+
+ private final float originalFlySpeed;
+ private final boolean disableHitCollision;
+ private final float horizontalVelocity;
+ private final DropperArenaGameMode arenaGameMode;
+
+ /**
+ * Instantiates a new player state
+ *
+ * @param player The player whose state should be stored
+ */
+ public DropperPlayerEntryState(@NotNull Player player, @NotNull DropperArenaGameMode arenaGameMode,
+ boolean makePlayerInvisible, boolean disableHitCollision, float horizontalVelocity) {
+ super(player, makePlayerInvisible);
+ this.originalFlySpeed = player.getFlySpeed();
+ this.arenaGameMode = arenaGameMode;
+ this.disableHitCollision = disableHitCollision;
+ this.horizontalVelocity = horizontalVelocity;
+ }
+
+ @Override
+ public void setArenaState() {
+ super.setArenaState();
+ this.player.setAllowFlight(true);
+ this.player.setFlying(true);
+ this.player.setGameMode(GameMode.ADVENTURE);
+ this.player.setSwimming(false);
+ if (this.disableHitCollision) {
+ this.player.setCollidable(false);
+ }
+
+ // If playing on the inverted game-mode, negate the horizontal velocity to swap the controls
+ if (this.arenaGameMode == DropperArenaGameMode.INVERTED) {
+ this.player.setFlySpeed(-this.horizontalVelocity);
+ } else {
+ this.player.setFlySpeed(this.horizontalVelocity);
+ }
+ }
+
+ @Override
+ public void restore() {
+ super.restore();
+ this.player.setFlySpeed(this.originalFlySpeed);
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java
new file mode 100644
index 0000000..2494e02
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java
@@ -0,0 +1,413 @@
+package net.knarcraft.minigames.arena.parkour;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.Arena;
+import net.knarcraft.minigames.arena.ArenaGameMode;
+import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
+import net.knarcraft.minigames.util.MaterialHelper;
+import net.knarcraft.minigames.util.ParkourArenaStorageHelper;
+import net.knarcraft.minigames.util.StringSanitizer;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import static net.knarcraft.minigames.util.InputValidationHelper.isInvalid;
+
+/**
+ * A representation of one parkour arena
+ */
+public class ParkourArena implements Arena {
+
+ /**
+ * An unique and persistent identifier for this arena
+ */
+ private final @NotNull UUID arenaId;
+
+ /**
+ * A name used when listing and storing this arena.
+ */
+ private @NotNull String arenaName;
+
+ /**
+ * The location players are teleported to when joining this arena.
+ */
+ private @NotNull Location spawnLocation;
+
+ /**
+ * The location players will be sent to when they win or lose the arena. If not set, their entry location should be
+ * used instead.
+ */
+ private @Nullable Location exitLocation;
+
+ /**
+ * The material of the block players have to hit to win this parkour arena
+ */
+ private @NotNull Material winBlockType;
+
+ /**
+ * The location the player has to reach to win. If not set, winBlockType is used instead
+ */
+ private @Nullable Location winLocation;
+
+ /**
+ * The names of the block types constituting this arena's kill plane
+ */
+ private @Nullable Set killPlaneBlockNames;
+
+ /**
+ * The block types constituting this arena's kill plane
+ */
+ private @Nullable Set killPlaneBlocks;
+
+ /**
+ * The checkpoints for this arena. Entering a checkpoint overrides the player's spawn location.
+ */
+ private final @NotNull List checkpoints;
+
+ /**
+ * The arena data for this arena
+ */
+ private final @NotNull ParkourArenaData parkourArenaData;
+
+ private final @NotNull ParkourArenaHandler parkourArenaHandler;
+
+ /**
+ * Instantiates a new parkour arena
+ *
+ * @param arenaId The id of the arena
+ * @param arenaName The name of the arena
+ * @param spawnLocation The location players spawn in when entering the arena
+ * @param exitLocation The location the players are teleported to when exiting the arena, or null
+ * @param winBlockType The material of the block players have to hit to win this parkour arena
+ * @param winLocation The location a player has to reach to win this arena
+ * @param parkourArenaData The arena data keeping track of which players have done what in this arena
+ * @param arenaHandler The arena handler used for saving any changes
+ */
+ public ParkourArena(@NotNull UUID arenaId, @NotNull String arenaName, @NotNull Location spawnLocation,
+ @Nullable Location exitLocation, @NotNull Material winBlockType, @Nullable Location winLocation,
+ @Nullable Set killPlaneBlockNames, @NotNull List checkpoints,
+ @NotNull ParkourArenaData parkourArenaData, @NotNull ParkourArenaHandler arenaHandler) {
+ this.arenaId = arenaId;
+ this.arenaName = arenaName;
+ this.spawnLocation = spawnLocation;
+ this.exitLocation = exitLocation;
+ this.winBlockType = winBlockType;
+ this.winLocation = winLocation;
+ this.killPlaneBlockNames = killPlaneBlockNames;
+ this.killPlaneBlocks = this.killPlaneBlockNames == null ? null : MaterialHelper.loadMaterialList(
+ new ArrayList<>(killPlaneBlockNames));
+ this.checkpoints = checkpoints;
+ this.parkourArenaData = parkourArenaData;
+ this.parkourArenaHandler = arenaHandler;
+ }
+
+ /**
+ * Instantiates a new parkour arena
+ *
+ * Note that this minimal constructor can be used to quickly create a new parkour arena at the player's given
+ * location, simply by them giving an arena name.
+ *
+ * @param arenaName The name of the arena
+ * @param spawnLocation The location players spawn in when entering the arena
+ * @param arenaHandler The arena handler used for saving any changes
+ */
+ public ParkourArena(@NotNull String arenaName, @NotNull Location spawnLocation,
+ @NotNull ParkourArenaHandler arenaHandler) {
+ this.arenaId = UUID.randomUUID();
+ this.arenaName = arenaName;
+ this.spawnLocation = spawnLocation;
+ this.exitLocation = null;
+ this.winLocation = null;
+
+ Map recordRegistries = new HashMap<>();
+ for (ParkourArenaGameMode arenaGameMode : ParkourArenaGameMode.values()) {
+ recordRegistries.put(arenaGameMode, new ParkourArenaRecordsRegistry(this.arenaId));
+ }
+
+ this.parkourArenaData = new ParkourArenaData(this.arenaId, recordRegistries, new HashMap<>());
+ this.winBlockType = Material.EMERALD_BLOCK;
+ this.killPlaneBlocks = null;
+ this.checkpoints = new ArrayList<>();
+ this.parkourArenaHandler = arenaHandler;
+ }
+
+ @Override
+ public @NotNull ParkourArenaData getData() {
+ return this.parkourArenaData;
+ }
+
+ @Override
+ public @NotNull UUID getArenaId() {
+ return this.arenaId;
+ }
+
+ @Override
+ public @NotNull String getArenaName() {
+ return this.arenaName;
+ }
+
+ @Override
+ public @NotNull Location getSpawnLocation() {
+ return this.spawnLocation;
+ }
+
+ /**
+ * Gets this arena's exit location
+ *
+ * @return This arena's exit location, or null if no such location is set.
+ */
+ public @Nullable Location getExitLocation() {
+ return this.exitLocation;
+ }
+
+ /**
+ * Gets the type of block a player has to hit to win this arena
+ *
+ * @return The kind of block players must hit
+ */
+ public @NotNull Material getWinBlockType() {
+ return this.winBlockType;
+ }
+
+ /**
+ * The location a player has to reach to win this arena
+ *
+ *
+ *
+ * @return The win trigger's location
+ */
+ public @Nullable Location getWinLocation() {
+ return this.winLocation != null ? this.winLocation.clone() : null;
+ }
+
+ /**
+ * Gets the block types used for this parkour arena's kill plane
+ *
+ * @return The types of blocks that cause a loss
+ */
+ public @NotNull Set getKillPlaneBlocks() {
+ if (this.killPlaneBlocks != null) {
+ return new HashSet<>(this.killPlaneBlocks);
+ } else {
+ return MiniGames.getInstance().getParkourConfiguration().getKillPlaneBlocks();
+ }
+ }
+
+ /**
+ * Gets the names of the block types used for this parkour arena's kill plane
+ *
+ * @return The names of the types of blocks that cause a loss
+ */
+ public @Nullable Set getKillPlaneBlockNames() {
+ return this.killPlaneBlockNames;
+ }
+
+ /**
+ * Gets all checkpoint locations for this arena
+ *
+ * @return All checkpoint locations for this arena
+ */
+ public List getCheckpoints() {
+ List copy = new ArrayList<>(this.checkpoints.size());
+ for (Location location : this.checkpoints) {
+ copy.add(location.clone());
+ }
+ return copy;
+ }
+
+ /**
+ * Gets this arena's sanitized name
+ *
+ * @return This arena's sanitized name
+ */
+ public @NotNull String getArenaNameSanitized() {
+ return StringSanitizer.sanitizeArenaName(this.getArenaName());
+ }
+
+ @Override
+ public boolean removeData() {
+ return ParkourArenaStorageHelper.removeParkourArenaData(getArenaId());
+ }
+
+ @Override
+ public boolean saveData() {
+ try {
+ ParkourArenaStorageHelper.saveParkourArenaData(getData());
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean willCauseWin(Block block) {
+ return (this.winLocation != null && this.winLocation.getBlock().equals(block)) ||
+ (this.winLocation == null && this.winBlockType == block.getType());
+ }
+
+ @Override
+ public boolean willCauseLoss(Block block) {
+ return this.getKillPlaneBlocks().contains(block.getType());
+ }
+
+ @Override
+ public boolean winLocationIsSolid() {
+ return (this.winLocation != null && this.winLocation.getBlock().getType().isSolid()) ||
+ this.winBlockType.isSolid();
+ }
+
+ /**
+ * Sets the spawn location for this arena
+ *
+ * @param newLocation The new spawn location
+ * @return True if successfully updated
+ */
+ public boolean setSpawnLocation(@NotNull Location newLocation) {
+ if (isInvalid(newLocation)) {
+ return false;
+ } else {
+ this.spawnLocation = newLocation;
+ this.parkourArenaHandler.saveArenas();
+ return true;
+ }
+ }
+
+ /**
+ * Sets the exit location for this arena
+ *
+ * @param newLocation The new exit location
+ * @return True if successfully updated
+ */
+ public boolean setExitLocation(@NotNull Location newLocation) {
+ if (isInvalid(newLocation)) {
+ return false;
+ } else {
+ this.exitLocation = newLocation;
+ this.parkourArenaHandler.saveArenas();
+ return true;
+ }
+ }
+
+ /**
+ * Sets the name of this arena
+ *
+ * @param arenaName The new name
+ * @return True if successfully updated
+ */
+ public boolean setName(@NotNull String arenaName) {
+ if (!arenaName.isBlank()) {
+ String oldName = this.getArenaNameSanitized();
+ this.arenaName = arenaName;
+ // Update the arena lookup map to make sure the new name can be used immediately
+ this.parkourArenaHandler.updateLookupName(oldName, this.getArenaNameSanitized());
+ this.parkourArenaHandler.saveArenas();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Sets the material of the win block type
+ *
+ * The win block type is the type of block a player must hit to win in this arena
+ *
+ * @param material The material to set for the win block type
+ * @return True if successfully updated
+ */
+ public boolean setWinBlockType(@NotNull Material material) {
+ if (material.isAir() || !material.isBlock()) {
+ return false;
+ } else {
+ this.winBlockType = material;
+ this.parkourArenaHandler.saveArenas();
+ return true;
+ }
+ }
+
+ /**
+ * Sets the location players need to reach to win this arena
+ *
+ * @param newLocation The location players have to reach
+ * @return True if successfully changed
+ */
+ public boolean setWinLocation(@NotNull Location newLocation) {
+ if (isInvalid(newLocation)) {
+ return false;
+ } else {
+ this.winLocation = newLocation.clone();
+ this.parkourArenaHandler.saveArenas();
+ return true;
+ }
+ }
+
+ /**
+ * Sets the type of blocks constituting this arena's kill plane
+ *
+ * @param killPlaneBlockNames The names of the blocks that will cause players to lose
+ */
+ public boolean setKillPlaneBlocks(@NotNull Set killPlaneBlockNames) {
+ if (killPlaneBlockNames.isEmpty()) {
+ this.killPlaneBlocks = null;
+ } else {
+ Set parsed = MaterialHelper.loadMaterialList(new ArrayList<>(killPlaneBlockNames));
+ if (parsed.isEmpty()) {
+ return false;
+ }
+ this.killPlaneBlocks = parsed;
+ }
+ this.parkourArenaHandler.saveArenas();
+ return true;
+ }
+
+ /**
+ * Adds a checkpoint to this arena
+ *
+ * @param checkpoint The checkpoint to add
+ * @return True if successfully added
+ */
+ public boolean addCheckpoint(@NotNull Location checkpoint) {
+ if (isInvalid(checkpoint)) {
+ return false;
+ }
+
+ this.checkpoints.add(checkpoint.clone());
+ this.parkourArenaHandler.saveArenas();
+ return true;
+ }
+
+ /**
+ * Clears all checkpoints from this arena
+ *
+ * @return True if successfully cleared
+ */
+ public boolean clearCheckpoints() {
+ if (checkpoints.isEmpty()) {
+ return false;
+ }
+
+ this.checkpoints.clear();
+ this.parkourArenaHandler.saveArenas();
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof ParkourArena otherArena)) {
+ return false;
+ }
+ return this.getArenaNameSanitized().equals(otherArena.getArenaNameSanitized());
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaData.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaData.java
new file mode 100644
index 0000000..3b5db09
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaData.java
@@ -0,0 +1,72 @@
+package net.knarcraft.minigames.arena.parkour;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaData;
+import net.knarcraft.minigames.arena.ArenaGameMode;
+import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
+import net.knarcraft.minigames.container.SerializableContainer;
+import net.knarcraft.minigames.container.SerializableUUID;
+import net.knarcraft.minigames.util.SerializableConverter;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Data stored for an arena
+ */
+public class ParkourArenaData extends ArenaData {
+
+ /**
+ * Instantiates a new parkour arena data object
+ *
+ * @param arenaId The id of the arena this data belongs to
+ * @param recordRegistries The registry of this arena's records
+ * @param playersCompleted The set of the players that have cleared this arena
+ */
+ public ParkourArenaData(@NotNull UUID arenaId,
+ @NotNull Map recordRegistries,
+ @NotNull Map> playersCompleted) {
+ super(arenaId, recordRegistries, playersCompleted);
+ }
+
+ @Override
+ public void saveData() {
+ MiniGames.getInstance().getParkourArenaHandler().saveData(this.arenaId);
+ }
+
+ /**
+ * Deserializes a parkour arena data from the given data
+ *
+ * @param data The data to deserialize
+ * @return The deserialized parkour arena data
+ */
+ @SuppressWarnings({"unused", "unchecked"})
+ public static @NotNull ParkourArenaData deserialize(@NotNull Map data) {
+ SerializableUUID serializableUUID = (SerializableUUID) data.get("arenaId");
+ Map recordsRegistry =
+ (Map) data.get("recordsRegistry");
+ Map>> playersCompletedData =
+ (Map>>) data.get("playersCompleted");
+
+ if (recordsRegistry == null) {
+ recordsRegistry = new HashMap<>();
+ } else if (playersCompletedData == null) {
+ playersCompletedData = new HashMap<>();
+ }
+
+ // Convert the serializable UUIDs to normal UUIDs
+ Map> allPlayersCompleted = new HashMap<>();
+ SerializableConverter.getRawValue(playersCompletedData, allPlayersCompleted);
+
+ for (ArenaGameMode arenaGameMode : playersCompletedData.keySet()) {
+ if (!recordsRegistry.containsKey(arenaGameMode) || recordsRegistry.get(arenaGameMode) == null) {
+ recordsRegistry.put(arenaGameMode, new ParkourArenaRecordsRegistry(serializableUUID.getRawValue()));
+ }
+ }
+ return new ParkourArenaData(serializableUUID.getRawValue(), recordsRegistry, allPlayersCompleted);
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaEditableProperty.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaEditableProperty.java
new file mode 100644
index 0000000..06672c1
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaEditableProperty.java
@@ -0,0 +1,107 @@
+package net.knarcraft.minigames.arena.parkour;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.function.Function;
+
+/**
+ * All editable properties of a parkour arena
+ */
+public enum ParkourArenaEditableProperty {
+
+ /**
+ * The name of the arena
+ */
+ NAME("name", ParkourArena::getArenaName),
+
+ /**
+ * The arena's spawn location
+ */
+ SPAWN_LOCATION("spawnLocation", (arena) -> String.valueOf(arena.getSpawnLocation())),
+
+ /**
+ * The arena's exit location
+ */
+ EXIT_LOCATION("exitLocation", (arena) -> String.valueOf(arena.getExitLocation())),
+
+ /**
+ * The arena's win block type
+ */
+ WIN_BLOCK_TYPE("winBlockType", (arena) -> arena.getWinBlockType().toString()),
+
+ /**
+ * The arena's win location (overrides the win block type)
+ */
+ WIN_LOCATION("winLocation", (arena) -> {
+ if (arena.getWinLocation() != null) {
+ return arena.getWinLocation().toString();
+ } else {
+ return "null";
+ }
+ }),
+
+ /**
+ * The arena's check points. Specifically used for adding.
+ */
+ CHECKPOINT_ADD("checkpointAdd", (arena) -> String.valueOf(arena.getCheckpoints())),
+
+ /**
+ * The arena's check points. Specifically used for clearing.
+ */
+ CHECKPOINT_CLEAR("checkpointClear", (arena) -> String.valueOf(arena.getCheckpoints())),
+
+ /**
+ * The blocks constituting the arena's lethal blocks
+ */
+ KILL_PLANE_BLOCKS("killPlaneBlocks", (arena) -> String.valueOf(arena.getKillPlaneBlockNames())),
+ ;
+
+ private final @NotNull String argumentString;
+ private final Function currentValueProvider;
+
+ /**
+ * Instantiates a new arena editable property
+ *
+ * @param argumentString The argument string used to specify this property
+ */
+ ParkourArenaEditableProperty(@NotNull String argumentString, Function currentValueProvider) {
+ this.argumentString = argumentString;
+ this.currentValueProvider = currentValueProvider;
+ }
+
+ /**
+ * Gets the string representation of this property's current value
+ *
+ * @param arena The arena to check the value for
+ * @return The current value as a string
+ */
+ public String getCurrentValueAsString(ParkourArena arena) {
+ return this.currentValueProvider.apply(arena);
+ }
+
+ /**
+ * Gets the argument string used to specify this property
+ *
+ * @return The argument string
+ */
+ public @NotNull String getArgumentString() {
+ return this.argumentString;
+ }
+
+ /**
+ * Gets the editable property corresponding to the given argument string
+ *
+ * @param argumentString The argument string used to specify an editable property
+ * @return The corresponding editable property, or null if not found
+ */
+ public static @Nullable ParkourArenaEditableProperty getFromArgumentString(String argumentString) {
+ for (ParkourArenaEditableProperty property : ParkourArenaEditableProperty.values()) {
+ if (property.argumentString.equalsIgnoreCase(argumentString)) {
+ return property;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaGameMode.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaGameMode.java
new file mode 100644
index 0000000..a423749
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaGameMode.java
@@ -0,0 +1,59 @@
+package net.knarcraft.minigames.arena.parkour;
+
+import net.knarcraft.minigames.arena.ArenaGameMode;
+import org.bukkit.configuration.serialization.ConfigurationSerializable;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A representation of possible arena game-modes
+ */
+public enum ParkourArenaGameMode implements ConfigurationSerializable, ArenaGameMode {
+
+ /**
+ * The default game-mode. Failing once throws the player out.
+ */
+ DEFAULT,
+ ;
+
+ /**
+ * Tries to match the correct game-mode according to the given string
+ *
+ * @param gameMode The game-mode string to match
+ * @return The specified arena game-mode
+ */
+ public static @NotNull ParkourArenaGameMode matchGamemode(@NotNull String gameMode) {
+ try {
+ return ParkourArenaGameMode.valueOf(gameMode.toUpperCase());
+ } catch (IllegalArgumentException exception) {
+ return ParkourArenaGameMode.DEFAULT;
+ }
+ }
+
+ @NotNull
+ @Override
+ public Map serialize() {
+ Map data = new HashMap<>();
+ data.put("name", this.name());
+ return data;
+ }
+
+ /**
+ * Deserializes the arena game-mode specified by the given data
+ *
+ * @param data The data to deserialize
+ * @return The deserialized arena game-mode
+ */
+ @SuppressWarnings("unused")
+ public static ParkourArenaGameMode deserialize(Map data) {
+ return ParkourArenaGameMode.valueOf((String) data.get("name"));
+ }
+
+ @Override
+ public @NotNull ParkourArenaGameMode[] getValues() {
+ return ParkourArenaGameMode.values();
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaGroup.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaGroup.java
new file mode 100644
index 0000000..e21babd
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaGroup.java
@@ -0,0 +1,56 @@
+package net.knarcraft.minigames.arena.parkour;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaGroup;
+import net.knarcraft.minigames.container.SerializableUUID;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * A sorted group of arenas that must be completed in sequence
+ */
+public class ParkourArenaGroup extends ArenaGroup {
+
+ /**
+ * Instantiates a new parkour arena group
+ *
+ * @param groupName The name of this group
+ */
+ public ParkourArenaGroup(@NotNull String groupName) {
+ super(groupName, MiniGames.getInstance().getParkourArenaHandler());
+ }
+
+ /**
+ * Instantiates a new parkour arena group
+ *
+ * @param groupId The unique id of this group
+ * @param groupName The name of this group
+ * @param arenas The arenas in this group
+ */
+ private ParkourArenaGroup(@NotNull UUID groupId, @NotNull String groupName, @NotNull List arenas) {
+ super(groupId, groupName, arenas, MiniGames.getInstance().getParkourArenaHandler());
+ }
+
+ /**
+ * Deserializes the given data
+ *
+ * @param data The data to deserialize
+ * @return The deserialized arena group
+ */
+ @SuppressWarnings({"unused", "unchecked"})
+ public static @NotNull ParkourArenaGroup deserialize(@NotNull Map data) {
+ UUID id = ((SerializableUUID) data.get("groupId")).getRawValue();
+ String name = (String) data.get("groupName");
+ List serializableArenas = (List) data.get("arenas");
+ List arenas = new ArrayList<>();
+ for (SerializableUUID arenaId : serializableArenas) {
+ arenas.add(arenaId.getRawValue());
+ }
+ return new ParkourArenaGroup(id, name, arenas);
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaHandler.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaHandler.java
new file mode 100644
index 0000000..d177879
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaHandler.java
@@ -0,0 +1,75 @@
+package net.knarcraft.minigames.arena.parkour;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaHandler;
+import net.knarcraft.minigames.util.ParkourArenaStorageHelper;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.logging.Level;
+
+/**
+ * A handler that keeps track of all parkour arenas
+ */
+public class ParkourArenaHandler extends ArenaHandler {
+
+ /**
+ * Instantiates a new arena handler
+ *
+ * @param playerRegistry The registry keeping track of player sessions
+ */
+ public ParkourArenaHandler(ParkourArenaPlayerRegistry playerRegistry) {
+ super(playerRegistry);
+ }
+
+ @Override
+ public void saveGroups() {
+ try {
+ ParkourArenaStorageHelper.saveParkourArenaGroups(new HashSet<>(this.arenaGroups.values()));
+ } catch (IOException e) {
+ MiniGames.log(Level.SEVERE, "Unable to save current arena groups! " +
+ "Data loss can occur!");
+ MiniGames.log(Level.SEVERE, e.getMessage());
+ }
+ }
+
+ @Override
+ protected void loadGroups() {
+ Set arenaGroups = ParkourArenaStorageHelper.loadParkourArenaGroups();
+ Map arenaGroupMap = new HashMap<>();
+ for (ParkourArenaGroup arenaGroup : arenaGroups) {
+ for (UUID arenaId : arenaGroup.getArenas()) {
+ arenaGroupMap.put(arenaId, arenaGroup);
+ }
+ }
+ this.arenaGroups = arenaGroupMap;
+ }
+
+ @Override
+ public void saveArenas() {
+ try {
+ ParkourArenaStorageHelper.saveParkourArenas(this.arenas);
+ } catch (IOException e) {
+ MiniGames.log(Level.SEVERE, "Unable to save current arenas! " +
+ "Data loss can occur!");
+ MiniGames.log(Level.SEVERE, e.getMessage());
+ }
+ }
+
+ @Override
+ protected void loadArenas() {
+ this.arenas = ParkourArenaStorageHelper.loadParkourArenas();
+
+ // Save a map from arena name to arena id for improved performance
+ this.arenaNameLookup = new HashMap<>();
+ for (Map.Entry arena : this.arenas.entrySet()) {
+ String sanitizedName = arena.getValue().getArenaNameSanitized();
+ this.arenaNameLookup.put(sanitizedName, arena.getKey());
+ }
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaPlayerRegistry.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaPlayerRegistry.java
new file mode 100644
index 0000000..0a20c07
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaPlayerRegistry.java
@@ -0,0 +1,62 @@
+package net.knarcraft.minigames.arena.parkour;
+
+import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * A registry to keep track of which players are playing in which arenas
+ */
+public class ParkourArenaPlayerRegistry implements ArenaPlayerRegistry {
+
+ private final Map arenaPlayers = new HashMap<>();
+
+ /**
+ * Registers that the given player has started playing the given parkour arena session
+ *
+ * @param playerId The id of the player that started playing
+ * @param arena The arena session to register
+ */
+ public void registerPlayer(@NotNull UUID playerId, @NotNull ParkourArenaSession arena) {
+ this.arenaPlayers.put(playerId, arena);
+ }
+
+ /**
+ * Removes this player from players currently playing
+ *
+ * @param playerId The id of the player to remove
+ */
+ public boolean removePlayer(@NotNull UUID playerId) {
+ return this.arenaPlayers.remove(playerId) != null;
+ }
+
+ /**
+ * Gets the player's active parkour arena session
+ *
+ * @param playerId The id of the player to get arena for
+ * @return The player's active arena session, or null if not currently playing
+ */
+ public @Nullable ParkourArenaSession getArenaSession(@NotNull UUID playerId) {
+ return this.arenaPlayers.getOrDefault(playerId, null);
+ }
+
+ /**
+ * Removes all active sessions for the given arena
+ *
+ * @param arena The arena to remove sessions for
+ */
+ public void removeForArena(ParkourArena arena) {
+ for (Map.Entry entry : this.arenaPlayers.entrySet()) {
+ if (entry.getValue().getArena() == arena) {
+ // Kick the player gracefully
+ entry.getValue().triggerQuit(false);
+ this.arenaPlayers.remove(entry.getKey());
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaRecordsRegistry.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaRecordsRegistry.java
new file mode 100644
index 0000000..c6027c5
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaRecordsRegistry.java
@@ -0,0 +1,66 @@
+package net.knarcraft.minigames.arena.parkour;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
+import net.knarcraft.minigames.arena.record.IntegerRecord;
+import net.knarcraft.minigames.arena.record.LongRecord;
+import net.knarcraft.minigames.container.SerializableUUID;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * A registry keeping track of all records
+ */
+public class ParkourArenaRecordsRegistry extends ArenaRecordsRegistry {
+
+ /**
+ * Instantiates a new empty records registry
+ */
+ public ParkourArenaRecordsRegistry(@NotNull UUID arenaId) {
+ super(arenaId);
+ }
+
+ /**
+ * Instantiates a new records registry
+ *
+ * @param leastDeaths The existing least death records to use
+ * @param shortestTimeMilliSeconds The existing leash time records to use
+ */
+ private ParkourArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Set leastDeaths,
+ @NotNull Set shortestTimeMilliSeconds) {
+ super(arenaId, leastDeaths, shortestTimeMilliSeconds);
+ }
+
+ /**
+ * Saves changed records
+ */
+ @Override
+ protected void save() {
+ MiniGames.getInstance().getParkourArenaHandler().saveData(this.arenaId);
+ }
+
+ /**
+ * Deserializes the given data
+ *
+ * @param data The data to deserialize
+ * @return The deserialized records registry
+ */
+ @SuppressWarnings({"unused", "unchecked"})
+ public static ParkourArenaRecordsRegistry deserialize(Map data) {
+ UUID arenaId = ((SerializableUUID) data.get("arenaId")).getRawValue();
+ Set leastDeaths =
+ (Set) data.getOrDefault("leastDeaths", new HashMap<>());
+ Set shortestTimeMilliseconds =
+ (Set) data.getOrDefault("shortestTime", new HashMap<>());
+
+ leastDeaths.removeIf(Objects::isNull);
+ shortestTimeMilliseconds.removeIf(Objects::isNull);
+ return new ParkourArenaRecordsRegistry(arenaId, leastDeaths, shortestTimeMilliseconds);
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaSession.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaSession.java
new file mode 100644
index 0000000..c21fb49
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaSession.java
@@ -0,0 +1,223 @@
+package net.knarcraft.minigames.arena.parkour;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaGameMode;
+import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
+import net.knarcraft.minigames.arena.ArenaSession;
+import net.knarcraft.minigames.arena.PlayerEntryState;
+import net.knarcraft.minigames.config.ParkourConfiguration;
+import net.knarcraft.minigames.property.RecordResult;
+import net.knarcraft.minigames.util.PlayerTeleporter;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.logging.Level;
+
+/**
+ * A representation of a player's current session in a parkour arena
+ */
+public class ParkourArenaSession implements ArenaSession {
+
+ private final @NotNull ParkourArena arena;
+ private final @NotNull Player player;
+ private final @NotNull ParkourArenaGameMode gameMode;
+ private int deaths;
+ private final long startTime;
+ private final PlayerEntryState entryState;
+ private Location reachedCheckpoint = null;
+
+ /**
+ * Instantiates a new parkour arena session
+ *
+ * @param parkourArena The arena that's being played in
+ * @param player The player playing the arena
+ * @param gameMode The game-mode
+ */
+ public ParkourArenaSession(@NotNull ParkourArena parkourArena, @NotNull Player player,
+ @NotNull ParkourArenaGameMode gameMode) {
+ this.arena = parkourArena;
+ this.player = player;
+ this.gameMode = gameMode;
+ this.deaths = 0;
+ this.startTime = System.currentTimeMillis();
+
+ ParkourConfiguration configuration = MiniGames.getInstance().getParkourConfiguration();
+ boolean makeInvisible = configuration.makePlayersInvisible();
+ this.entryState = new ParkourPlayerEntryState(player, makeInvisible);
+ // Make the player fly to improve mobility in the air
+ this.entryState.setArenaState();
+ }
+
+ @Override
+ public @NotNull ArenaGameMode getGameMode() {
+ return this.gameMode;
+ }
+
+ /**
+ * Gets the state of the player when they joined the session
+ *
+ * @return The player's entry state
+ */
+ public @NotNull PlayerEntryState getEntryState() {
+ return this.entryState;
+ }
+
+ /**
+ * Registers the checkpoint this session's player has reached
+ *
+ * @param location The location of the checkpoint
+ */
+ public void registerCheckpoint(@NotNull Location location) {
+ this.reachedCheckpoint = location;
+ }
+
+ /**
+ * Gets the checkpoint currently registered as the player's spawn location
+ *
+ * @return The registered checkpoint, or null if not set
+ */
+ public @Nullable Location getRegisteredCheckpoint() {
+ return this.reachedCheckpoint;
+ }
+
+ /**
+ * Triggers a win for the player playing in this session
+ */
+ public void triggerWin() {
+ // Stop this session
+ stopSession();
+
+ // Check for, and display, records
+ MiniGames miniGames = MiniGames.getInstance();
+ boolean ignore = miniGames.getParkourConfiguration().ignoreRecordsUntilGroupBeatenOnce();
+ ParkourArenaGroup group = miniGames.getParkourArenaHandler().getGroup(this.arena.getArenaId());
+ if (!ignore || group == null || group.hasBeatenAll(this.gameMode, this.player)) {
+ registerRecord();
+ }
+
+ // Mark the arena as cleared
+ if (this.arena.getData().setCompleted(this.gameMode, this.player)) {
+ this.player.sendMessage("You cleared the arena!");
+ }
+ this.player.sendMessage("You won!");
+
+ // Teleport the player out of the arena
+ teleportToExit(false);
+ }
+
+ /**
+ * Teleports the playing player out of the arena
+ */
+ private void teleportToExit(boolean immediately) {
+ // Teleport the player out of the arena
+ Location exitLocation;
+ if (this.arena.getExitLocation() != null) {
+ exitLocation = this.arena.getExitLocation();
+ } else {
+ exitLocation = this.entryState.getEntryLocation();
+ }
+ PlayerTeleporter.teleportPlayer(this.player, exitLocation, true, immediately);
+ }
+
+ /**
+ * Removes this session from current sessions
+ */
+ private void removeSession() {
+ // Remove this session for game sessions to stop listeners from fiddling more with the player
+ boolean removedSession = MiniGames.getInstance().getParkourArenaPlayerRegistry().removePlayer(player.getUniqueId());
+ if (!removedSession) {
+ MiniGames.log(Level.SEVERE, "Unable to remove parkour arena session for " + player.getName() + ". " +
+ "This will have unintended consequences.");
+ }
+ }
+
+ /**
+ * Registers the player's record if necessary, and prints record information to the player
+ */
+ private void registerRecord() {
+ ArenaRecordsRegistry recordsRegistry = this.arena.getData().getRecordRegistries().get(this.gameMode);
+ long timeElapsed = System.currentTimeMillis() - this.startTime;
+ announceRecord(recordsRegistry.registerTimeRecord(this.player.getUniqueId(), timeElapsed), "time");
+ announceRecord(recordsRegistry.registerDeathRecord(this.player.getUniqueId(), this.deaths), "least deaths");
+ }
+
+ /**
+ * Announces a record set by this player
+ *
+ * @param recordResult The result of the record
+ * @param type The type of record set (time or deaths)
+ */
+ private void announceRecord(@NotNull RecordResult recordResult, @NotNull String type) {
+ if (recordResult == RecordResult.NONE) {
+ return;
+ }
+
+ // Gets a string representation of the played game-mode
+ String gameModeString = switch (this.gameMode) {
+ case DEFAULT -> "default";
+ };
+
+ String recordString = "You just set a %s on the %s game-mode!";
+ recordString = switch (recordResult) {
+ case WORLD_RECORD -> String.format(recordString, "new %s record", gameModeString);
+ case PERSONAL_BEST -> String.format(recordString, "personal %s record", gameModeString);
+ default -> throw new IllegalStateException("Unexpected value: " + recordResult);
+ };
+ player.sendMessage(String.format(recordString, type));
+ }
+
+ /**
+ * Triggers a loss for the player playing in this session
+ */
+ public void triggerLoss() {
+ this.deaths++;
+ //Teleport the player back to the top
+ Location spawnLocation = this.reachedCheckpoint != null ? this.reachedCheckpoint : this.arena.getSpawnLocation();
+ PlayerTeleporter.teleportPlayer(this.player, spawnLocation, true, false);
+ this.entryState.setArenaState();
+ }
+
+ /**
+ * Triggers a quit for the player playing in this session
+ */
+ public void triggerQuit(boolean immediately) {
+ // Stop this session
+ stopSession();
+ // Teleport the player out of the arena
+ teleportToExit(immediately);
+
+ player.sendMessage("You quit the arena!");
+ }
+
+ /**
+ * Stops this session, and disables flight mode
+ */
+ private void stopSession() {
+ // Remove this session from game sessions to stop listeners from fiddling more with the player
+ removeSession();
+
+ // Remove flight mode
+ entryState.restore();
+ }
+
+ /**
+ * Gets the arena this session is being played in
+ *
+ * @return The session's arena
+ */
+ public @NotNull ParkourArena getArena() {
+ return this.arena;
+ }
+
+ /**
+ * Gets the player playing in this session
+ *
+ * @return This session's player
+ */
+ public @NotNull Player getPlayer() {
+ return this.player;
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaStorageKey.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaStorageKey.java
new file mode 100644
index 0000000..45a8dce
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaStorageKey.java
@@ -0,0 +1,76 @@
+package net.knarcraft.minigames.arena.parkour;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * A representation of each key used for storing arena data
+ */
+public enum ParkourArenaStorageKey {
+
+ /**
+ * The key for an arena's id
+ */
+ ID("arenaId"),
+
+ /**
+ * The key for an arena's name
+ */
+ NAME("arenaName"),
+
+ /**
+ * The key for an arena's spawn location
+ */
+ SPAWN_LOCATION("arenaSpawnLocation"),
+
+ /**
+ * The key for an arena's exit location
+ */
+ EXIT_LOCATION("arenaExitLocation"),
+
+ /**
+ * The key for the type of this arena's win block
+ */
+ WIN_BLOCK_TYPE("winBlockType"),
+
+ /**
+ * The key for this arena's win location (overrides win block type)
+ */
+ WIN_LOCATION("winLocation"),
+
+ /**
+ * The key for this arena's kill plane blocks (overrides the config)
+ */
+ KILL_PLANE_BLOCKS("killPlaneBlocks"),
+
+ /**
+ * The key for this arena's checkpoint locations
+ */
+ CHECKPOINTS("checkpoints"),
+
+ /**
+ * The hey for this arena's data
+ */
+ DATA("arenaData"),
+ ;
+
+ private final @NotNull String key;
+
+ /**
+ * Instantiates a new arena storage key
+ *
+ * @param key The string path of the configuration key this value represents.
+ */
+ ParkourArenaStorageKey(@NotNull String key) {
+ this.key = key;
+ }
+
+ /**
+ * Gets the configuration key this enum represents
+ *
+ * @return The string key representation.
+ */
+ public @NotNull String getKey() {
+ return this.key;
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourPlayerEntryState.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourPlayerEntryState.java
new file mode 100644
index 0000000..74d95f2
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourPlayerEntryState.java
@@ -0,0 +1,32 @@
+package net.knarcraft.minigames.arena.parkour;
+
+import net.knarcraft.minigames.arena.AbstractPlayerEntryState;
+import org.bukkit.GameMode;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * The state of a player before entering a parkour arena
+ */
+public class ParkourPlayerEntryState extends AbstractPlayerEntryState {
+
+ /**
+ * Instantiates a new player state
+ *
+ * @param player The player whose state should be stored
+ */
+ public ParkourPlayerEntryState(@NotNull Player player, boolean makePlayerInvisible) {
+ super(player, makePlayerInvisible);
+ }
+
+ @Override
+ public void setArenaState() {
+ super.setArenaState();
+ this.player.setAllowFlight(false);
+ this.player.setFlying(false);
+ this.player.setGameMode(GameMode.ADVENTURE);
+ this.player.setSwimming(false);
+ this.player.setCollidable(false);
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/dropper/arena/record/ArenaRecord.java b/src/main/java/net/knarcraft/minigames/arena/record/ArenaRecord.java
similarity index 94%
rename from src/main/java/net/knarcraft/dropper/arena/record/ArenaRecord.java
rename to src/main/java/net/knarcraft/minigames/arena/record/ArenaRecord.java
index 2b01024..dabced6 100644
--- a/src/main/java/net/knarcraft/dropper/arena/record/ArenaRecord.java
+++ b/src/main/java/net/knarcraft/minigames/arena/record/ArenaRecord.java
@@ -1,6 +1,6 @@
-package net.knarcraft.dropper.arena.record;
+package net.knarcraft.minigames.arena.record;
-import net.knarcraft.dropper.container.SerializableUUID;
+import net.knarcraft.minigames.container.SerializableUUID;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.jetbrains.annotations.NotNull;
diff --git a/src/main/java/net/knarcraft/dropper/arena/record/IntegerRecord.java b/src/main/java/net/knarcraft/minigames/arena/record/IntegerRecord.java
similarity index 87%
rename from src/main/java/net/knarcraft/dropper/arena/record/IntegerRecord.java
rename to src/main/java/net/knarcraft/minigames/arena/record/IntegerRecord.java
index d836eec..315594a 100644
--- a/src/main/java/net/knarcraft/dropper/arena/record/IntegerRecord.java
+++ b/src/main/java/net/knarcraft/minigames/arena/record/IntegerRecord.java
@@ -1,6 +1,6 @@
-package net.knarcraft.dropper.arena.record;
+package net.knarcraft.minigames.arena.record;
-import net.knarcraft.dropper.container.SerializableUUID;
+import net.knarcraft.minigames.container.SerializableUUID;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
@@ -37,7 +37,7 @@ public class IntegerRecord extends SummableArenaRecord {
*/
@SuppressWarnings("unused")
public static IntegerRecord deserialize(@NotNull Map data) {
- return new IntegerRecord(((SerializableUUID) data.get("userId")).uuid(), (Integer) data.get("record"));
+ return new IntegerRecord(((SerializableUUID) data.get("userId")).getRawValue(), (Integer) data.get("record"));
}
}
diff --git a/src/main/java/net/knarcraft/dropper/arena/record/LongRecord.java b/src/main/java/net/knarcraft/minigames/arena/record/LongRecord.java
similarity index 86%
rename from src/main/java/net/knarcraft/dropper/arena/record/LongRecord.java
rename to src/main/java/net/knarcraft/minigames/arena/record/LongRecord.java
index 5ea0c8a..a18a5f3 100644
--- a/src/main/java/net/knarcraft/dropper/arena/record/LongRecord.java
+++ b/src/main/java/net/knarcraft/minigames/arena/record/LongRecord.java
@@ -1,6 +1,6 @@
-package net.knarcraft.dropper.arena.record;
+package net.knarcraft.minigames.arena.record;
-import net.knarcraft.dropper.container.SerializableUUID;
+import net.knarcraft.minigames.container.SerializableUUID;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
@@ -37,7 +37,7 @@ public class LongRecord extends SummableArenaRecord {
*/
@SuppressWarnings("unused")
public static LongRecord deserialize(@NotNull Map data) {
- return new LongRecord(((SerializableUUID) data.get("userId")).uuid(), ((Number) data.get("record")).longValue());
+ return new LongRecord(((SerializableUUID) data.get("userId")).getRawValue(), ((Number) data.get("record")).longValue());
}
}
diff --git a/src/main/java/net/knarcraft/dropper/arena/record/SummableArenaRecord.java b/src/main/java/net/knarcraft/minigames/arena/record/SummableArenaRecord.java
similarity index 94%
rename from src/main/java/net/knarcraft/dropper/arena/record/SummableArenaRecord.java
rename to src/main/java/net/knarcraft/minigames/arena/record/SummableArenaRecord.java
index 52d2975..9c0ead6 100644
--- a/src/main/java/net/knarcraft/dropper/arena/record/SummableArenaRecord.java
+++ b/src/main/java/net/knarcraft/minigames/arena/record/SummableArenaRecord.java
@@ -1,4 +1,4 @@
-package net.knarcraft.dropper.arena.record;
+package net.knarcraft.minigames.arena.record;
import java.util.UUID;
diff --git a/src/main/java/net/knarcraft/minigames/command/JoinArenaTabCompleter.java b/src/main/java/net/knarcraft/minigames/command/JoinArenaTabCompleter.java
new file mode 100644
index 0000000..8e0c74d
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/command/JoinArenaTabCompleter.java
@@ -0,0 +1,49 @@
+package net.knarcraft.minigames.command;
+
+import net.knarcraft.minigames.arena.ArenaGameMode;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * An abstract class for an arena joining tab-completer
+ */
+public abstract class JoinArenaTabCompleter implements TabCompleter {
+
+ private final ArenaGameMode gameMode;
+ private final Supplier> arenaNameSupplier;
+
+ /**
+ * Implements a new join arena tab completer
+ *
+ * @param arenaNameSupplier The supplier to ask for arena names
+ * @param gameMode An instance of one of the available game-modes
+ */
+ public JoinArenaTabCompleter(Supplier> arenaNameSupplier, ArenaGameMode gameMode) {
+ this.arenaNameSupplier = arenaNameSupplier;
+ this.gameMode = gameMode;
+ }
+
+ @Override
+ public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
+ @NotNull String label, @NotNull String[] arguments) {
+ if (arguments.length == 1) {
+ return arenaNameSupplier.get();
+ } else if (arguments.length == 2) {
+ List gameModes = new ArrayList<>();
+ for (ArenaGameMode gameMode : gameMode.getValues()) {
+ gameModes.add(gameMode.name().toLowerCase());
+ }
+ return gameModes;
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/dropper/command/LeaveArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/LeaveArenaCommand.java
similarity index 76%
rename from src/main/java/net/knarcraft/dropper/command/LeaveArenaCommand.java
rename to src/main/java/net/knarcraft/minigames/command/LeaveArenaCommand.java
index c9a9025..3e70afd 100644
--- a/src/main/java/net/knarcraft/dropper/command/LeaveArenaCommand.java
+++ b/src/main/java/net/knarcraft/minigames/command/LeaveArenaCommand.java
@@ -1,7 +1,7 @@
-package net.knarcraft.dropper.command;
+package net.knarcraft.minigames.command;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.DropperArenaSession;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaSession;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@@ -25,10 +25,9 @@ public class LeaveArenaCommand implements TabExecutor {
return false;
}
- DropperArenaSession existingSession = Dropper.getInstance().getPlayerRegistry().getArenaSession(
- player.getUniqueId());
+ ArenaSession existingSession = MiniGames.getInstance().getSession(player.getUniqueId());
if (existingSession == null) {
- commandSender.sendMessage("You are not in a dropper arena!");
+ commandSender.sendMessage("You are not in a mini-games arena!");
return false;
}
diff --git a/src/main/java/net/knarcraft/dropper/command/ReloadCommand.java b/src/main/java/net/knarcraft/minigames/command/ReloadCommand.java
similarity index 87%
rename from src/main/java/net/knarcraft/dropper/command/ReloadCommand.java
rename to src/main/java/net/knarcraft/minigames/command/ReloadCommand.java
index 6be7a2f..cec650a 100644
--- a/src/main/java/net/knarcraft/dropper/command/ReloadCommand.java
+++ b/src/main/java/net/knarcraft/minigames/command/ReloadCommand.java
@@ -1,6 +1,6 @@
-package net.knarcraft.dropper.command;
+package net.knarcraft.minigames.command;
-import net.knarcraft.dropper.Dropper;
+import net.knarcraft.minigames.MiniGames;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@@ -18,7 +18,7 @@ public class ReloadCommand implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) {
- Dropper.getInstance().reload();
+ MiniGames.getInstance().reload();
commandSender.sendMessage("Plugin reloaded!");
return true;
}
diff --git a/src/main/java/net/knarcraft/dropper/command/CreateArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/CreateDropperArenaCommand.java
similarity index 77%
rename from src/main/java/net/knarcraft/dropper/command/CreateArenaCommand.java
rename to src/main/java/net/knarcraft/minigames/command/dropper/CreateDropperArenaCommand.java
index 89f4fa0..622b0b4 100644
--- a/src/main/java/net/knarcraft/dropper/command/CreateArenaCommand.java
+++ b/src/main/java/net/knarcraft/minigames/command/dropper/CreateDropperArenaCommand.java
@@ -1,9 +1,9 @@
-package net.knarcraft.dropper.command;
+package net.knarcraft.minigames.command.dropper;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.DropperArena;
-import net.knarcraft.dropper.arena.DropperArenaHandler;
-import net.knarcraft.dropper.util.StringSanitizer;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.dropper.DropperArena;
+import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
+import net.knarcraft.minigames.util.StringSanitizer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@@ -13,7 +13,7 @@ import org.jetbrains.annotations.NotNull;
/**
* The command for creating a new dropper arena
*/
-public class CreateArenaCommand implements CommandExecutor {
+public class CreateDropperArenaCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@@ -36,7 +36,7 @@ public class CreateArenaCommand implements CommandExecutor {
return false;
}
- DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
+ DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
DropperArena existingArena = arenaHandler.getArena(arenaName);
if (existingArena != null) {
diff --git a/src/main/java/net/knarcraft/dropper/command/GroupListCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupListCommand.java
similarity index 85%
rename from src/main/java/net/knarcraft/dropper/command/GroupListCommand.java
rename to src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupListCommand.java
index 73f0375..fe7525d 100644
--- a/src/main/java/net/knarcraft/dropper/command/GroupListCommand.java
+++ b/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupListCommand.java
@@ -1,9 +1,9 @@
-package net.knarcraft.dropper.command;
+package net.knarcraft.minigames.command.dropper;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.DropperArena;
-import net.knarcraft.dropper.arena.DropperArenaGroup;
-import net.knarcraft.dropper.arena.DropperArenaHandler;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.dropper.DropperArena;
+import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
+import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@@ -17,12 +17,12 @@ import java.util.UUID;
/**
* The command for listing groups and the stages within
*/
-public class GroupListCommand implements TabExecutor {
+public class DropperGroupListCommand implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) {
- DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
+ DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
if (arguments.length == 0) {
displayExistingGroups(arenaHandler, commandSender);
return true;
@@ -81,7 +81,7 @@ public class GroupListCommand implements TabExecutor {
@NotNull String[] arguments) {
if (arguments.length == 1) {
List groupNames = new ArrayList<>();
- for (DropperArenaGroup group : Dropper.getInstance().getArenaHandler().getAllGroups()) {
+ for (DropperArenaGroup group : MiniGames.getInstance().getDropperArenaHandler().getAllGroups()) {
groupNames.add(group.getGroupName());
}
return groupNames;
diff --git a/src/main/java/net/knarcraft/dropper/command/GroupSetCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSetCommand.java
similarity index 74%
rename from src/main/java/net/knarcraft/dropper/command/GroupSetCommand.java
rename to src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSetCommand.java
index 6ede1d5..68c14b1 100644
--- a/src/main/java/net/knarcraft/dropper/command/GroupSetCommand.java
+++ b/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSetCommand.java
@@ -1,11 +1,11 @@
-package net.knarcraft.dropper.command;
+package net.knarcraft.minigames.command.dropper;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.DropperArena;
-import net.knarcraft.dropper.arena.DropperArenaGroup;
-import net.knarcraft.dropper.arena.DropperArenaHandler;
-import net.knarcraft.dropper.util.StringSanitizer;
-import net.knarcraft.dropper.util.TabCompleteHelper;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.dropper.DropperArena;
+import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
+import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
+import net.knarcraft.minigames.util.StringSanitizer;
+import net.knarcraft.minigames.util.TabCompleteHelper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@@ -18,7 +18,7 @@ import java.util.List;
/**
* The command for setting the group of an arena
*/
-public class GroupSetCommand implements TabExecutor {
+public class DropperGroupSetCommand implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@@ -27,7 +27,7 @@ public class GroupSetCommand implements TabExecutor {
return false;
}
- DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
+ DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
DropperArena specifiedArena = arenaHandler.getArena(arguments[0]);
if (specifiedArena == null) {
@@ -62,12 +62,12 @@ public class GroupSetCommand implements TabExecutor {
public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) {
if (arguments.length == 1) {
- return TabCompleteHelper.getArenas();
+ return TabCompleteHelper.getDropperArenas();
} else if (arguments.length == 2) {
List possibleValues = new ArrayList<>();
possibleValues.add("none");
possibleValues.add("GroupName");
- for (DropperArenaGroup group : Dropper.getInstance().getArenaHandler().getAllGroups()) {
+ for (DropperArenaGroup group : MiniGames.getInstance().getDropperArenaHandler().getAllGroups()) {
possibleValues.add(group.getGroupName());
}
return possibleValues;
diff --git a/src/main/java/net/knarcraft/dropper/command/GroupSwapCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSwapCommand.java
similarity index 84%
rename from src/main/java/net/knarcraft/dropper/command/GroupSwapCommand.java
rename to src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSwapCommand.java
index 9f5456a..4de5f2b 100644
--- a/src/main/java/net/knarcraft/dropper/command/GroupSwapCommand.java
+++ b/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSwapCommand.java
@@ -1,9 +1,9 @@
-package net.knarcraft.dropper.command;
+package net.knarcraft.minigames.command.dropper;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.DropperArena;
-import net.knarcraft.dropper.arena.DropperArenaGroup;
-import net.knarcraft.dropper.arena.DropperArenaHandler;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.dropper.DropperArena;
+import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
+import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@@ -17,7 +17,7 @@ import java.util.UUID;
/**
* The command for swapping the order of two arenas in a group
*/
-public class GroupSwapCommand implements TabExecutor {
+public class DropperGroupSwapCommand implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@@ -26,7 +26,7 @@ public class GroupSwapCommand implements TabExecutor {
return false;
}
- DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
+ DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
DropperArena arena1 = arenaHandler.getArena(arguments[0]);
if (arena1 == null) {
@@ -57,7 +57,7 @@ public class GroupSwapCommand implements TabExecutor {
@Override
public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) {
- DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
+ DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
if (arguments.length == 1) {
List arenaNames = new ArrayList<>();
for (DropperArena dropperArena : arenaHandler.getArenasInAGroup()) {
@@ -78,7 +78,7 @@ public class GroupSwapCommand implements TabExecutor {
* @return The names of the arenas in the same group
*/
private List getArenaNamesInSameGroup(String arenaName) {
- DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
+ DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
DropperArena arena1 = arenaHandler.getArena(arenaName);
if (arena1 == null) {
return new ArrayList<>();
diff --git a/src/main/java/net/knarcraft/dropper/command/EditArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaCommand.java
similarity index 84%
rename from src/main/java/net/knarcraft/dropper/command/EditArenaCommand.java
rename to src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaCommand.java
index b8477a9..2e6eb30 100644
--- a/src/main/java/net/knarcraft/dropper/command/EditArenaCommand.java
+++ b/src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaCommand.java
@@ -1,8 +1,9 @@
-package net.knarcraft.dropper.command;
+package net.knarcraft.minigames.command.dropper;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.DropperArena;
-import net.knarcraft.dropper.property.ArenaEditableProperty;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.dropper.DropperArena;
+import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty;
+import net.knarcraft.minigames.config.DropperConfiguration;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.command.Command;
@@ -14,7 +15,18 @@ import org.jetbrains.annotations.NotNull;
/**
* The command for editing an existing dropper arena
*/
-public class EditArenaCommand implements CommandExecutor {
+public class EditDropperArenaCommand implements CommandExecutor {
+
+ private final DropperConfiguration configuration;
+
+ /**
+ * Instantiates a new edit arena command
+ *
+ * @param configuration The configuration to use
+ */
+ public EditDropperArenaCommand(DropperConfiguration configuration) {
+ this.configuration = configuration;
+ }
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@@ -28,13 +40,13 @@ public class EditArenaCommand implements CommandExecutor {
return false;
}
- DropperArena specifiedArena = Dropper.getInstance().getArenaHandler().getArena(arguments[0]);
+ DropperArena specifiedArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]);
if (specifiedArena == null) {
commandSender.sendMessage("Unable to find the specified dropper arena.");
return false;
}
- ArenaEditableProperty editableProperty = ArenaEditableProperty.getFromArgumentString(arguments[1]);
+ DropperArenaEditableProperty editableProperty = DropperArenaEditableProperty.getFromArgumentString(arguments[1]);
if (editableProperty == null) {
commandSender.sendMessage("Unknown property specified.");
return false;
@@ -67,7 +79,7 @@ public class EditArenaCommand implements CommandExecutor {
* @param player The player trying to change the value
* @return True if the value was successfully changed
*/
- private boolean changeValue(@NotNull DropperArena arena, @NotNull ArenaEditableProperty property,
+ private boolean changeValue(@NotNull DropperArena arena, @NotNull DropperArenaEditableProperty property,
@NotNull String value, @NotNull Player player) {
return switch (property) {
case WIN_BLOCK_TYPE -> arena.setWinBlockType(parseMaterial(value));
@@ -92,7 +104,7 @@ public class EditArenaCommand implements CommandExecutor {
try {
velocity = Double.parseDouble(velocityString);
} catch (NumberFormatException exception) {
- velocity = 3.92;
+ velocity = configuration.getVerticalVelocity();
}
// Require at least speed of 0.001, and at most 75 blocks/s
@@ -111,12 +123,7 @@ public class EditArenaCommand implements CommandExecutor {
try {
velocity = Float.parseFloat(velocityString);
} catch (NumberFormatException exception) {
- velocity = 1;
- }
-
- // Make sure the velocity isn't exactly 0
- if (velocity == 0) {
- velocity = 0.5f;
+ velocity = configuration.getHorizontalVelocity();
}
// If outside bonds, choose the most extreme value
diff --git a/src/main/java/net/knarcraft/dropper/command/EditArenaTabCompleter.java b/src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaTabCompleter.java
similarity index 73%
rename from src/main/java/net/knarcraft/dropper/command/EditArenaTabCompleter.java
rename to src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaTabCompleter.java
index 43213af..0f85127 100644
--- a/src/main/java/net/knarcraft/dropper/command/EditArenaTabCompleter.java
+++ b/src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaTabCompleter.java
@@ -1,6 +1,6 @@
-package net.knarcraft.dropper.command;
+package net.knarcraft.minigames.command.dropper;
-import net.knarcraft.dropper.util.TabCompleteHelper;
+import net.knarcraft.minigames.util.TabCompleteHelper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
@@ -13,15 +13,15 @@ import java.util.List;
/**
* The tab-completer for the edit arena command
*/
-public class EditArenaTabCompleter implements TabCompleter {
+public class EditDropperArenaTabCompleter implements TabCompleter {
@Override
public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String label, @NotNull String[] args) {
if (args.length == 1) {
- return TabCompleteHelper.getArenas();
+ return TabCompleteHelper.getDropperArenas();
} else if (args.length == 2) {
- return TabCompleteHelper.getArenaProperties();
+ return TabCompleteHelper.getDropperArenaProperties();
} else if (args.length == 3) {
//TODO: Tab-complete possible values for the given property
return null;
diff --git a/src/main/java/net/knarcraft/dropper/command/JoinArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/JoinDropperArenaCommand.java
similarity index 63%
rename from src/main/java/net/knarcraft/dropper/command/JoinArenaCommand.java
rename to src/main/java/net/knarcraft/minigames/command/dropper/JoinDropperArenaCommand.java
index 5c0ca1a..155e99e 100644
--- a/src/main/java/net/knarcraft/dropper/command/JoinArenaCommand.java
+++ b/src/main/java/net/knarcraft/minigames/command/dropper/JoinDropperArenaCommand.java
@@ -1,12 +1,13 @@
-package net.knarcraft.dropper.command;
+package net.knarcraft.minigames.command.dropper;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.DropperArena;
-import net.knarcraft.dropper.arena.DropperArenaGroup;
-import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry;
-import net.knarcraft.dropper.arena.DropperArenaSession;
-import net.knarcraft.dropper.property.ArenaGameMode;
-import net.knarcraft.dropper.util.PlayerTeleporter;
+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.DropperArenaPlayerRegistry;
+import net.knarcraft.minigames.arena.dropper.DropperArenaSession;
+import net.knarcraft.minigames.config.DropperConfiguration;
+import net.knarcraft.minigames.util.PlayerTeleporter;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@@ -16,7 +17,7 @@ import org.jetbrains.annotations.NotNull;
/**
* The command used to join a dropper arena
*/
-public class JoinArenaCommand implements CommandExecutor {
+public class JoinDropperArenaCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@@ -30,15 +31,14 @@ public class JoinArenaCommand implements CommandExecutor {
return false;
}
- // Disallow joining if the player is already in a dropper arena
- DropperArenaSession existingSession = Dropper.getInstance().getPlayerRegistry().getArenaSession(player.getUniqueId());
- if (existingSession != null) {
- commandSender.sendMessage("You are already in a dropper arena!");
+ // Disallow joining if the player is already in a mini-game arena
+ if (MiniGames.getInstance().getSession(player.getUniqueId()) != null) {
+ commandSender.sendMessage("You are already playing a mini-game!");
return false;
}
// Make sure the arena exists
- DropperArena specifiedArena = Dropper.getInstance().getArenaHandler().getArena(arguments[0]);
+ DropperArena specifiedArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]);
if (specifiedArena == null) {
commandSender.sendMessage("Unable to find the specified dropper arena.");
return false;
@@ -63,29 +63,30 @@ public class JoinArenaCommand implements CommandExecutor {
*/
private boolean joinArena(DropperArena specifiedArena, Player player, String[] arguments) {
// Find the specified game-mode
- ArenaGameMode gameMode;
+ DropperArenaGameMode gameMode;
if (arguments.length > 1) {
- gameMode = ArenaGameMode.matchGamemode(arguments[1]);
+ gameMode = DropperArenaGameMode.matchGamemode(arguments[1]);
} else {
- gameMode = ArenaGameMode.DEFAULT;
+ gameMode = DropperArenaGameMode.DEFAULT;
}
// Make sure the player has beaten the necessary levels
- DropperArenaGroup arenaGroup = Dropper.getInstance().getArenaHandler().getGroup(specifiedArena.getArenaId());
+ DropperArenaGroup arenaGroup = MiniGames.getInstance().getDropperArenaHandler().getGroup(specifiedArena.getArenaId());
if (arenaGroup != null && !doGroupChecks(specifiedArena, arenaGroup, gameMode, player)) {
return false;
}
// Make sure the player has beaten the arena once in normal mode before playing another mode
- if (gameMode != ArenaGameMode.DEFAULT &&
- specifiedArena.getData().hasNotCompleted(ArenaGameMode.DEFAULT, player)) {
+ if (MiniGames.getInstance().getDropperConfiguration().mustDoNormalModeFirst() &&
+ gameMode != DropperArenaGameMode.DEFAULT &&
+ specifiedArena.getData().hasNotCompleted(DropperArenaGameMode.DEFAULT, player)) {
player.sendMessage("You must complete this arena in normal mode first!");
return false;
}
// Register the player's session
DropperArenaSession newSession = new DropperArenaSession(specifiedArena, player, gameMode);
- DropperArenaPlayerRegistry playerRegistry = Dropper.getInstance().getPlayerRegistry();
+ DropperArenaPlayerRegistry playerRegistry = MiniGames.getInstance().getDropperArenaPlayerRegistry();
playerRegistry.registerPlayer(player.getUniqueId(), newSession);
// Try to teleport the player to the arena
@@ -97,7 +98,7 @@ public class JoinArenaCommand implements CommandExecutor {
return false;
} else {
// Make sure to update the state again in the air to remove a potential swimming state
- newSession.getEntryState().setArenaState(specifiedArena.getPlayerHorizontalVelocity());
+ newSession.getEntryState().setArenaState();
return true;
}
}
@@ -112,17 +113,18 @@ public class JoinArenaCommand implements CommandExecutor {
* @return False if any checks failed
*/
private boolean doGroupChecks(@NotNull DropperArena dropperArena, @NotNull DropperArenaGroup arenaGroup,
- @NotNull ArenaGameMode arenaGameMode, @NotNull Player player) {
+ @NotNull DropperArenaGameMode arenaGameMode, @NotNull Player player) {
+ DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration();
// Require that players beat all arenas in the group in the normal game-mode before trying challenge modes
- if (arenaGameMode != ArenaGameMode.DEFAULT) {
- if (!arenaGroup.hasBeatenAll(ArenaGameMode.DEFAULT, player)) {
- player.sendMessage("You have not yet beaten all arenas in this group!");
- return false;
- }
+ if (configuration.mustDoNormalModeFirst() && arenaGameMode != DropperArenaGameMode.DEFAULT &&
+ !arenaGroup.hasBeatenAll(DropperArenaGameMode.DEFAULT, player)) {
+ player.sendMessage("You have not yet beaten all arenas in this group!");
+ return false;
}
// Require that the player has beaten the previous arena on the same game-mode before trying this one
- if (!arenaGroup.canPlay(arenaGameMode, player, dropperArena.getArenaId())) {
+ if (configuration.mustDoGroupedInSequence() &&
+ arenaGroup.cannotPlay(arenaGameMode, player, dropperArena.getArenaId())) {
player.sendMessage("You have not yet beaten the previous arena!");
return false;
}
diff --git a/src/main/java/net/knarcraft/minigames/command/dropper/JoinDropperArenaTabCompleter.java b/src/main/java/net/knarcraft/minigames/command/dropper/JoinDropperArenaTabCompleter.java
new file mode 100644
index 0000000..10566c9
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/command/dropper/JoinDropperArenaTabCompleter.java
@@ -0,0 +1,19 @@
+package net.knarcraft.minigames.command.dropper;
+
+import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
+import net.knarcraft.minigames.command.JoinArenaTabCompleter;
+import net.knarcraft.minigames.util.TabCompleteHelper;
+
+/**
+ * The tab-completer for the join command
+ */
+public class JoinDropperArenaTabCompleter extends JoinArenaTabCompleter {
+
+ /**
+ * Implements a new join arena tab completer
+ */
+ public JoinDropperArenaTabCompleter() {
+ super(TabCompleteHelper::getDropperArenas, DropperArenaGameMode.DEFAULT);
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/dropper/command/ListArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/ListDropperArenaCommand.java
similarity index 78%
rename from src/main/java/net/knarcraft/dropper/command/ListArenaCommand.java
rename to src/main/java/net/knarcraft/minigames/command/dropper/ListDropperArenaCommand.java
index 48c8a38..16e6487 100644
--- a/src/main/java/net/knarcraft/dropper/command/ListArenaCommand.java
+++ b/src/main/java/net/knarcraft/minigames/command/dropper/ListDropperArenaCommand.java
@@ -1,6 +1,6 @@
-package net.knarcraft.dropper.command;
+package net.knarcraft.minigames.command.dropper;
-import net.knarcraft.dropper.util.TabCompleteHelper;
+import net.knarcraft.minigames.util.TabCompleteHelper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@@ -13,13 +13,13 @@ import java.util.List;
/**
* A command for listing existing dropper arenas
*/
-public class ListArenaCommand implements TabExecutor {
+public class ListDropperArenaCommand implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) {
sender.sendMessage("Dropper arenas:");
- for (String arenaName : TabCompleteHelper.getArenas()) {
+ for (String arenaName : TabCompleteHelper.getDropperArenas()) {
sender.sendMessage(arenaName);
}
return true;
diff --git a/src/main/java/net/knarcraft/dropper/command/RemoveArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaCommand.java
similarity index 67%
rename from src/main/java/net/knarcraft/dropper/command/RemoveArenaCommand.java
rename to src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaCommand.java
index 07d7934..861a537 100644
--- a/src/main/java/net/knarcraft/dropper/command/RemoveArenaCommand.java
+++ b/src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaCommand.java
@@ -1,7 +1,7 @@
-package net.knarcraft.dropper.command;
+package net.knarcraft.minigames.command.dropper;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.DropperArena;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.dropper.DropperArena;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@@ -10,7 +10,7 @@ import org.jetbrains.annotations.NotNull;
/**
* The method used for removing an existing arena
*/
-public class RemoveArenaCommand implements CommandExecutor {
+public class RemoveDropperArenaCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@@ -21,14 +21,14 @@ public class RemoveArenaCommand implements CommandExecutor {
}
// Get the specified arena
- DropperArena targetArena = Dropper.getInstance().getArenaHandler().getArena(arguments[0]);
+ DropperArena targetArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]);
if (targetArena == null) {
commandSender.sendMessage("Unable to find the specified arena");
return false;
}
// Remove the arena
- Dropper.getInstance().getArenaHandler().removeArena(targetArena);
+ MiniGames.getInstance().getDropperArenaHandler().removeArena(targetArena);
commandSender.sendMessage("The specified arena has been successfully removed");
return true;
}
diff --git a/src/main/java/net/knarcraft/dropper/command/RemoveArenaTabCompleter.java b/src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaTabCompleter.java
similarity index 73%
rename from src/main/java/net/knarcraft/dropper/command/RemoveArenaTabCompleter.java
rename to src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaTabCompleter.java
index 2dd8566..b12234f 100644
--- a/src/main/java/net/knarcraft/dropper/command/RemoveArenaTabCompleter.java
+++ b/src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaTabCompleter.java
@@ -1,6 +1,6 @@
-package net.knarcraft.dropper.command;
+package net.knarcraft.minigames.command.dropper;
-import net.knarcraft.dropper.util.TabCompleteHelper;
+import net.knarcraft.minigames.util.TabCompleteHelper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
@@ -13,14 +13,14 @@ import java.util.List;
/**
* The tab-completer for the remove arena command
*/
-public class RemoveArenaTabCompleter implements TabCompleter {
+public class RemoveDropperArenaTabCompleter implements TabCompleter {
@Nullable
@Override
public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) {
if (arguments.length == 1) {
- return TabCompleteHelper.getArenas();
+ return TabCompleteHelper.getDropperArenas();
} else {
return new ArrayList<>();
}
diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/CreateParkourArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/CreateParkourArenaCommand.java
new file mode 100644
index 0000000..0cb281c
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/command/parkour/CreateParkourArenaCommand.java
@@ -0,0 +1,53 @@
+package net.knarcraft.minigames.command.parkour;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.parkour.ParkourArena;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
+import net.knarcraft.minigames.util.StringSanitizer;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * The command for creating a new parkour arena
+ */
+public class CreateParkourArenaCommand implements CommandExecutor {
+
+ @Override
+ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
+ @NotNull String[] arguments) {
+ if (!(commandSender instanceof Player player)) {
+ commandSender.sendMessage("This command must be used by a player");
+ return false;
+ }
+
+ // Abort if no name was specified
+ if (arguments.length < 1) {
+ return false;
+ }
+
+ // Remove known characters that are likely to cause trouble if used in an arena name
+ String arenaName = StringSanitizer.removeUnwantedCharacters(arguments[0]);
+
+ // An arena name is required
+ if (arenaName.isBlank()) {
+ return false;
+ }
+
+ ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
+
+ ParkourArena existingArena = arenaHandler.getArena(arenaName);
+ if (existingArena != null) {
+ commandSender.sendMessage("There already exists a parkour arena with that name!");
+ return false;
+ }
+
+ ParkourArena arena = new ParkourArena(arenaName, player.getLocation(), arenaHandler);
+ arenaHandler.addArena(arena);
+ commandSender.sendMessage("The arena was successfully created!");
+ return true;
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaCommand.java
new file mode 100644
index 0000000..8c563cb
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaCommand.java
@@ -0,0 +1,127 @@
+package net.knarcraft.minigames.command.parkour;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.parkour.ParkourArena;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * The command for editing an existing dropper arena
+ */
+public class EditParkourArenaCommand implements CommandExecutor {
+
+ /**
+ * Instantiates a new edit arena command
+ */
+ public EditParkourArenaCommand() {
+ }
+
+ @Override
+ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
+ @NotNull String[] arguments) {
+ if (!(commandSender instanceof Player player)) {
+ commandSender.sendMessage("This command must be used by a player");
+ return false;
+ }
+
+ if (arguments.length < 2) {
+ return false;
+ }
+
+ ParkourArena specifiedArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]);
+ if (specifiedArena == null) {
+ commandSender.sendMessage("Unable to find the specified dropper arena.");
+ return false;
+ }
+
+ ParkourArenaEditableProperty editableProperty = ParkourArenaEditableProperty.getFromArgumentString(arguments[1]);
+ if (editableProperty == null) {
+ commandSender.sendMessage("Unknown property specified.");
+ return false;
+ }
+
+ String currentValueFormat = "Current value of %s is: %s";
+
+ if (arguments.length < 3) {
+ // Print the current value of the property
+ String value = editableProperty.getCurrentValueAsString(specifiedArena);
+ commandSender.sendMessage(String.format(currentValueFormat, editableProperty.getArgumentString(), value));
+ return true;
+ } else {
+ boolean successful = changeValue(specifiedArena, editableProperty, arguments[2], player);
+ if (successful) {
+ player.sendMessage(String.format("Property %s changed to: %s", editableProperty, arguments[2]));
+ } else {
+ player.sendMessage("Unable to change the property. Make sure your input is valid!");
+ }
+ return successful;
+ }
+ }
+
+ /**
+ * Changes the given property to the given value
+ *
+ * @param arena The arena to change the property for
+ * @param property The property to change
+ * @param value The new value of the property
+ * @param player The player trying to change the value
+ * @return True if the value was successfully changed
+ */
+ private boolean changeValue(@NotNull ParkourArena arena, @NotNull ParkourArenaEditableProperty property,
+ @NotNull String value, @NotNull Player player) {
+ return switch (property) {
+ case WIN_BLOCK_TYPE -> arena.setWinBlockType(parseMaterial(value));
+ case SPAWN_LOCATION -> arena.setSpawnLocation(parseLocation(player, value));
+ case NAME -> arena.setName(value);
+ case EXIT_LOCATION -> arena.setExitLocation(parseLocation(player, value));
+ case WIN_LOCATION -> arena.setWinLocation(parseLocation(player, value));
+ case CHECKPOINT_ADD -> arena.addCheckpoint(parseLocation(player, value));
+ case CHECKPOINT_CLEAR -> arena.clearCheckpoints();
+ case KILL_PLANE_BLOCKS -> arena.setKillPlaneBlocks(new HashSet<>(List.of(value.split(","))));
+ };
+ }
+
+ /**
+ * Parses the given location string
+ *
+ * @param player The player changing a location
+ * @param locationString The location string to parse
+ * @return The parsed location, or the player's location if not parse-able
+ */
+ private @NotNull Location parseLocation(Player player, String locationString) {
+ if ((locationString.trim() + ",").matches("([0-9]+.?[0-9]*,){3}")) {
+ String[] parts = locationString.split(",");
+ Location newLocation = player.getLocation().clone();
+ newLocation.setX(Double.parseDouble(parts[0].trim()));
+ newLocation.setY(Double.parseDouble(parts[1].trim()));
+ newLocation.setZ(Double.parseDouble(parts[2].trim()));
+ return newLocation;
+ } else {
+ return player.getLocation().clone();
+ }
+ }
+
+ /**
+ * Parses the given material name
+ *
+ * @param materialName The material name to parse
+ * @return The parsed material, or AIR if not valid
+ */
+ private @NotNull Material parseMaterial(String materialName) {
+ Material material = Material.matchMaterial(materialName);
+ if (material == null) {
+ material = Material.AIR;
+ }
+ return material;
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaTabCompleter.java b/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaTabCompleter.java
new file mode 100644
index 0000000..a9270e7
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaTabCompleter.java
@@ -0,0 +1,33 @@
+package net.knarcraft.minigames.command.parkour;
+
+import net.knarcraft.minigames.util.TabCompleteHelper;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The tab-completer for the edit arena command
+ */
+public class EditParkourArenaTabCompleter implements TabCompleter {
+
+ @Override
+ public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
+ @NotNull String label, @NotNull String[] args) {
+ if (args.length == 1) {
+ return TabCompleteHelper.getParkourArenas();
+ } else if (args.length == 2) {
+ return TabCompleteHelper.getParkourArenaProperties();
+ } else if (args.length == 3) {
+ //TODO: Tab-complete possible values for the given property
+ return null;
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/JoinParkourArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/JoinParkourArenaCommand.java
new file mode 100644
index 0000000..2174511
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/command/parkour/JoinParkourArenaCommand.java
@@ -0,0 +1,121 @@
+package net.knarcraft.minigames.command.parkour;
+
+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.ParkourArenaGroup;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaPlayerRegistry;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaSession;
+import net.knarcraft.minigames.config.ParkourConfiguration;
+import net.knarcraft.minigames.util.PlayerTeleporter;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * The command used to join a parkour arena
+ */
+public class JoinParkourArenaCommand implements CommandExecutor {
+
+ @Override
+ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
+ @NotNull String[] arguments) {
+ if (!(commandSender instanceof Player player)) {
+ commandSender.sendMessage("This command must be used by a player");
+ return false;
+ }
+
+ if (arguments.length < 1) {
+ return false;
+ }
+
+ // Disallow joining if the player is already in a mini-game arena
+ if (MiniGames.getInstance().getSession(player.getUniqueId()) != null) {
+ commandSender.sendMessage("You are already playing a mini-game!");
+ return false;
+ }
+
+ // Make sure the arena exists
+ ParkourArena specifiedArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]);
+ if (specifiedArena == null) {
+ commandSender.sendMessage("Unable to find the specified parkour arena.");
+ return false;
+ }
+
+ // Deny vehicles as allowing this is tricky, and will cause problems in some cases
+ if (player.isInsideVehicle() || !player.getPassengers().isEmpty()) {
+ commandSender.sendMessage("You cannot join an arena while inside a vehicle or carrying a passenger.");
+ return false;
+ }
+
+ return joinArena(specifiedArena, player, arguments);
+ }
+
+ /**
+ * Performs the actual arena joining
+ *
+ * @param specifiedArena The arena the player wants to join
+ * @param player The player joining the arena
+ * @param arguments The arguments given
+ * @return Whether the arena was joined successfully
+ */
+ private boolean joinArena(ParkourArena specifiedArena, Player player, String[] arguments) {
+ // Find the specified game-mode
+ ParkourArenaGameMode gameMode;
+ if (arguments.length > 1) {
+ gameMode = ParkourArenaGameMode.matchGamemode(arguments[1]);
+ } else {
+ gameMode = ParkourArenaGameMode.DEFAULT;
+ }
+
+ // Make sure the player has beaten the necessary levels
+ ParkourArenaGroup arenaGroup = MiniGames.getInstance().getParkourArenaHandler().getGroup(specifiedArena.getArenaId());
+ if (arenaGroup != null && !doGroupChecks(specifiedArena, arenaGroup, gameMode, player)) {
+ return false;
+ }
+
+ // Register the player's session
+ ParkourArenaSession newSession = new ParkourArenaSession(specifiedArena, player, gameMode);
+ ParkourArenaPlayerRegistry playerRegistry = MiniGames.getInstance().getParkourArenaPlayerRegistry();
+ playerRegistry.registerPlayer(player.getUniqueId(), newSession);
+
+ // Try to teleport the player to the arena
+ boolean teleported = PlayerTeleporter.teleportPlayer(player, specifiedArena.getSpawnLocation(), false, false);
+ if (!teleported) {
+ player.sendMessage("Unable to teleport you to the parkour arena. Make sure you're not in a vehicle," +
+ "and not carrying a passenger!");
+ newSession.triggerQuit(false);
+ return false;
+ } else {
+ // Make sure to update the state again in the air to remove a potential swimming state
+ newSession.getEntryState().setArenaState();
+ return true;
+ }
+ }
+
+ /**
+ * Performs necessary check for the given arena's group
+ *
+ * @param parkourArena The arena the player is trying to join
+ * @param arenaGroup The arena group the arena belongs to
+ * @param arenaGameMode The game-mode the player selected
+ * @param player The the player trying to join the arena
+ * @return False if any checks failed
+ */
+ private boolean doGroupChecks(@NotNull ParkourArena parkourArena, @NotNull ParkourArenaGroup arenaGroup,
+ @NotNull ParkourArenaGameMode arenaGameMode, @NotNull Player player) {
+ ParkourConfiguration configuration = MiniGames.getInstance().getParkourConfiguration();
+
+ // Require that the player has beaten the previous arena on the same game-mode before trying this one
+ if (configuration.mustDoGroupedInSequence() &&
+ arenaGroup.cannotPlay(arenaGameMode, player, parkourArena.getArenaId())) {
+ player.sendMessage("You have not yet beaten the previous arena!");
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/JoinParkourArenaTabCompleter.java b/src/main/java/net/knarcraft/minigames/command/parkour/JoinParkourArenaTabCompleter.java
new file mode 100644
index 0000000..96b0ac0
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/command/parkour/JoinParkourArenaTabCompleter.java
@@ -0,0 +1,19 @@
+package net.knarcraft.minigames.command.parkour;
+
+import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode;
+import net.knarcraft.minigames.command.JoinArenaTabCompleter;
+import net.knarcraft.minigames.util.TabCompleteHelper;
+
+/**
+ * The tab-completer for the join command
+ */
+public class JoinParkourArenaTabCompleter extends JoinArenaTabCompleter {
+
+ /**
+ * Implements a new join arena tab completer
+ */
+ public JoinParkourArenaTabCompleter() {
+ super(TabCompleteHelper::getParkourArenas, ParkourArenaGameMode.DEFAULT);
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/ListParkourArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/ListParkourArenaCommand.java
new file mode 100644
index 0000000..212f5db
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/command/parkour/ListParkourArenaCommand.java
@@ -0,0 +1,35 @@
+package net.knarcraft.minigames.command.parkour;
+
+import net.knarcraft.minigames.util.TabCompleteHelper;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabExecutor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A command for listing existing parkour arenas
+ */
+public class ListParkourArenaCommand implements TabExecutor {
+
+ @Override
+ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
+ @NotNull String[] arguments) {
+ sender.sendMessage("Parkour arenas:");
+ for (String arenaName : TabCompleteHelper.getParkourArenas()) {
+ sender.sendMessage(arenaName);
+ }
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
+ @NotNull String[] arguments) {
+ return new ArrayList<>();
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupListCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupListCommand.java
new file mode 100644
index 0000000..7dd31dd
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupListCommand.java
@@ -0,0 +1,95 @@
+package net.knarcraft.minigames.command.parkour;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.parkour.ParkourArena;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabExecutor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * The command for listing groups and the stages within
+ */
+public class ParkourGroupListCommand implements TabExecutor {
+
+ @Override
+ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
+ @NotNull String[] arguments) {
+ ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
+ if (arguments.length == 0) {
+ displayExistingGroups(arenaHandler, commandSender);
+ return true;
+ } else if (arguments.length == 1) {
+ return displayOrderedArenaNames(arenaHandler, commandSender, arguments[0]);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Displays all currently existing parkour arena groups
+ *
+ * @param arenaHandler The arena handler to get groups from
+ * @param sender The command sender to display the groups to
+ */
+ private void displayExistingGroups(@NotNull ParkourArenaHandler arenaHandler, @NotNull CommandSender sender) {
+ StringBuilder builder = new StringBuilder("Parkour arena groups:").append("\n");
+ arenaHandler.getAllGroups().stream().sorted().forEachOrdered((group) ->
+ builder.append(group.getGroupName()).append("\n"));
+ sender.sendMessage(builder.toString());
+ }
+
+ /**
+ * Displays the ordered stages in a specified group to the specified command sender
+ *
+ * @param arenaHandler The arena handler to get groups from
+ * @param sender The command sender to display the stages to
+ * @param groupName The name of the group to display stages for
+ * @return True if the stages were successfully displayed
+ */
+ private boolean displayOrderedArenaNames(@NotNull ParkourArenaHandler arenaHandler, @NotNull CommandSender sender,
+ @NotNull String groupName) {
+ ParkourArenaGroup arenaGroup = arenaHandler.getGroup(groupName);
+ if (arenaGroup == null) {
+ sender.sendMessage("Unable to find the specified group!");
+ return false;
+ }
+
+ // Send a list of all stages (arenas in the group)
+ StringBuilder builder = new StringBuilder(groupName).append("'s stages:").append("\n");
+ int counter = 1;
+ for (UUID arenaId : arenaGroup.getArenas()) {
+ ParkourArena arena = arenaHandler.getArena(arenaId);
+ if (arena != null) {
+ builder.append(counter++).append(". ").append(arena.getArenaName()).append("\n");
+ }
+ }
+ sender.sendMessage(builder.toString());
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
+ @NotNull String[] arguments) {
+ if (arguments.length == 1) {
+ List groupNames = new ArrayList<>();
+ Set arenaGroups = MiniGames.getInstance().getParkourArenaHandler().getAllGroups();
+ for (ParkourArenaGroup group : arenaGroups) {
+ groupNames.add(group.getGroupName());
+ }
+ return groupNames;
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSetCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSetCommand.java
new file mode 100644
index 0000000..14a66e2
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSetCommand.java
@@ -0,0 +1,79 @@
+package net.knarcraft.minigames.command.parkour;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.parkour.ParkourArena;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
+import net.knarcraft.minigames.util.StringSanitizer;
+import net.knarcraft.minigames.util.TabCompleteHelper;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabExecutor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The command for setting the group of an arena
+ */
+public class ParkourGroupSetCommand implements TabExecutor {
+
+ @Override
+ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
+ @NotNull String[] arguments) {
+ if (arguments.length < 2) {
+ return false;
+ }
+
+ ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
+
+ ParkourArena specifiedArena = arenaHandler.getArena(arguments[0]);
+ if (specifiedArena == null) {
+ commandSender.sendMessage("Unable to find the specified parkour arena.");
+ return false;
+ }
+
+ String groupName = StringSanitizer.removeUnwantedCharacters(arguments[1]);
+
+ if (groupName.isBlank()) {
+ return false;
+ }
+
+ ParkourArenaGroup arenaGroup;
+ if (groupName.equalsIgnoreCase("null") || groupName.equalsIgnoreCase("none")) {
+ arenaGroup = null;
+ } else {
+ arenaGroup = arenaHandler.getGroup(groupName);
+ if (arenaGroup == null) {
+ arenaGroup = new ParkourArenaGroup(groupName);
+ }
+ }
+
+ arenaHandler.setGroup(specifiedArena.getArenaId(), arenaGroup);
+
+ commandSender.sendMessage("The arena's group has been updated");
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
+ @NotNull String[] arguments) {
+ if (arguments.length == 1) {
+ return TabCompleteHelper.getParkourArenas();
+ } else if (arguments.length == 2) {
+ List possibleValues = new ArrayList<>();
+ possibleValues.add("none");
+ possibleValues.add("GroupName");
+ for (ParkourArenaGroup group : MiniGames.getInstance().getParkourArenaHandler().getAllGroups()) {
+ possibleValues.add(group.getGroupName());
+ }
+ return possibleValues;
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSwapCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSwapCommand.java
new file mode 100644
index 0000000..66a8174
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSwapCommand.java
@@ -0,0 +1,102 @@
+package net.knarcraft.minigames.command.parkour;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.parkour.ParkourArena;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabExecutor;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * The command for swapping the order of two arenas in a group
+ */
+public class ParkourGroupSwapCommand implements TabExecutor {
+
+ @Override
+ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
+ @NotNull String[] arguments) {
+ if (arguments.length < 2) {
+ return false;
+ }
+
+ ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
+
+ ParkourArena arena1 = arenaHandler.getArena(arguments[0]);
+ if (arena1 == null) {
+ commandSender.sendMessage("Unable to find the first specified parkour arena.");
+ return false;
+ }
+
+ ParkourArena arena2 = arenaHandler.getArena(arguments[1]);
+ if (arena2 == null) {
+ commandSender.sendMessage("Unable to find the second specified parkour arena.");
+ return false;
+ }
+
+ ParkourArenaGroup arena1Group = arenaHandler.getGroup(arena1.getArenaId());
+ ParkourArenaGroup arena2Group = arenaHandler.getGroup(arena2.getArenaId());
+ if (arena1Group == null || !arena1Group.equals(arena2Group)) {
+ commandSender.sendMessage("You cannot swap arenas in different groups!");
+ return false;
+ }
+
+ arena1Group.swapArenas(arena1Group.getArenas().indexOf(arena1.getArenaId()),
+ arena1Group.getArenas().indexOf(arena2.getArenaId()));
+ commandSender.sendMessage("The arenas have been swapped!");
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
+ @NotNull String[] arguments) {
+ ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
+ if (arguments.length == 1) {
+ List arenaNames = new ArrayList<>();
+ for (ParkourArena parkourArena : arenaHandler.getArenasInAGroup()) {
+ arenaNames.add(parkourArena.getArenaName());
+ }
+ return arenaNames;
+ } else if (arguments.length == 2) {
+ return getArenaNamesInSameGroup(arguments[0]);
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+ /**
+ * Gets the names of all arenas in the same group as the specified arena
+ *
+ * @param arenaName The name of the specified arena
+ * @return The names of the arenas in the same group
+ */
+ private List getArenaNamesInSameGroup(String arenaName) {
+ ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
+ ParkourArena arena1 = arenaHandler.getArena(arenaName);
+ if (arena1 == null) {
+ return new ArrayList<>();
+ }
+
+ // Only display other arenas in the selected group
+ List arenaNames = new ArrayList<>();
+ ParkourArenaGroup group = arenaHandler.getGroup(arena1.getArenaId());
+ if (group == null) {
+ return new ArrayList<>();
+ }
+ for (UUID arenaId : group.getArenas()) {
+ ParkourArena arena = arenaHandler.getArena(arenaId);
+ if (arena != null && arena.getArenaId() != arena1.getArenaId()) {
+ arenaNames.add(arena.getArenaName());
+ }
+ }
+ return arenaNames;
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaCommand.java
new file mode 100644
index 0000000..7aaa7c8
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaCommand.java
@@ -0,0 +1,36 @@
+package net.knarcraft.minigames.command.parkour;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.parkour.ParkourArena;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * The method used for removing an existing arena
+ */
+public class RemoveParkourArenaCommand implements CommandExecutor {
+
+ @Override
+ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
+ @NotNull String[] arguments) {
+ // Abort if no name was specified
+ if (arguments.length < 1) {
+ return false;
+ }
+
+ // Get the specified arena
+ ParkourArena targetArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]);
+ if (targetArena == null) {
+ commandSender.sendMessage("Unable to find the specified arena");
+ return false;
+ }
+
+ // Remove the arena
+ MiniGames.getInstance().getParkourArenaHandler().removeArena(targetArena);
+ commandSender.sendMessage("The specified arena has been successfully removed");
+ return true;
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaTabCompleter.java b/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaTabCompleter.java
new file mode 100644
index 0000000..9605a81
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaTabCompleter.java
@@ -0,0 +1,29 @@
+package net.knarcraft.minigames.command.parkour;
+
+import net.knarcraft.minigames.util.TabCompleteHelper;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The tab-completer for the remove arena command
+ */
+public class RemoveParkourArenaTabCompleter implements TabCompleter {
+
+ @Nullable
+ @Override
+ public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
+ @NotNull String[] arguments) {
+ if (arguments.length == 1) {
+ return TabCompleteHelper.getParkourArenas();
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/config/DropperConfiguration.java b/src/main/java/net/knarcraft/minigames/config/DropperConfiguration.java
new file mode 100644
index 0000000..d1d36ec
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/config/DropperConfiguration.java
@@ -0,0 +1,188 @@
+package net.knarcraft.minigames.config;
+
+import org.bukkit.Material;
+import org.bukkit.configuration.file.FileConfiguration;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The configuration keeping track of dropper settings
+ */
+public class DropperConfiguration extends MiniGameConfiguration {
+
+ private final static String rootNode = "dropper.";
+
+ private double verticalVelocity;
+ private float horizontalVelocity;
+ private int randomlyInvertedTimer;
+ private boolean mustDoGroupedInSequence;
+ private boolean ignoreRecordsUntilGroupBeatenOnce;
+ private boolean mustDoNormalModeFirst;
+ private boolean makePlayersInvisible;
+ private boolean disableHitCollision;
+ private boolean blockSneaking;
+ private boolean blockSprinting;
+ private Set blockWhitelist;
+
+ /**
+ * Instantiates a new dropper configuration
+ *
+ * @param configuration The YAML configuration to use internally
+ */
+ public DropperConfiguration(FileConfiguration configuration) {
+ super(configuration);
+ }
+
+ /**
+ * Gets the default vertical velocity
+ *
+ * @return The default vertical velocity
+ */
+ public double getVerticalVelocity() {
+ return this.verticalVelocity;
+ }
+
+ /**
+ * Gets the default horizontal velocity
+ *
+ * @return The default horizontal velocity
+ */
+ public float getHorizontalVelocity() {
+ return this.horizontalVelocity;
+ }
+
+ /**
+ * Gets the number of seconds before the randomly inverted game-mode toggles
+ *
+ * @return Number of seconds before the inversion toggles
+ */
+ public int getRandomlyInvertedTimer() {
+ return this.randomlyInvertedTimer;
+ }
+
+ /**
+ * Gets whether grouped arenas must be done in the set sequence
+ *
+ * @return Whether grouped arenas must be done in sequence
+ */
+ public boolean mustDoGroupedInSequence() {
+ return this.mustDoGroupedInSequence;
+ }
+
+ /**
+ * Gets whether the normal/default mode must be beaten before playing another game-mode
+ *
+ * @return Whether the normal game-mode must be beaten first
+ */
+ public boolean mustDoNormalModeFirst() {
+ return this.mustDoNormalModeFirst;
+ }
+
+ /**
+ * Gets the types of block which should not trigger a loss
+ *
+ * @return The materials that should not trigger a loss
+ */
+ public Set getBlockWhitelist() {
+ return new HashSet<>(this.blockWhitelist);
+ }
+
+ /**
+ * Gets whether records should be discarded, unless the player has already beaten all arenas in the group
+ *
+ * @return Whether to ignore records on the first play-through
+ */
+ public boolean ignoreRecordsUntilGroupBeatenOnce() {
+ return this.ignoreRecordsUntilGroupBeatenOnce;
+ }
+
+ /**
+ * Gets whether players should be made invisible while in an arena
+ *
+ * @return Whether players should be made invisible
+ */
+ public boolean makePlayersInvisible() {
+ return this.makePlayersInvisible;
+ }
+
+ /**
+ * Gets whether entity hit-collision of players in an arena should be disabled
+ *
+ * @return Whether to disable hit collision
+ */
+ public boolean disableHitCollision() {
+ return this.disableHitCollision;
+ }
+
+ /**
+ * Gets whether players trying to sneak while in a dropper arena to increase their downwards speed should be blocked
+ *
+ * @return Whether to block sneak to speed up
+ */
+ public boolean blockSneaking() {
+ return blockSneaking;
+ }
+
+ /**
+ * Gets whether players trying to sprint to improve their horizontal speed while in a dropper arena should be blocked
+ *
+ * @return Whether to block sprint to speed up
+ */
+ public boolean blockSprinting() {
+ return this.blockSprinting;
+ }
+
+ @Override
+ protected void load() {
+ this.verticalVelocity = configuration.getDouble(rootNode + "verticalVelocity", 1.0);
+ this.horizontalVelocity = (float) configuration.getDouble(rootNode + "horizontalVelocity", 1.0);
+ this.randomlyInvertedTimer = configuration.getInt(rootNode + "randomlyInvertedTimer", 7);
+ this.mustDoGroupedInSequence = configuration.getBoolean(rootNode + "mustDoGroupedInSequence", true);
+ this.ignoreRecordsUntilGroupBeatenOnce = configuration.getBoolean(rootNode + "ignoreRecordsUntilGroupBeatenOnce", false);
+ this.mustDoNormalModeFirst = configuration.getBoolean(rootNode + "mustDoNormalModeFirst", true);
+ this.makePlayersInvisible = configuration.getBoolean(rootNode + "makePlayersInvisible", false);
+ this.disableHitCollision = configuration.getBoolean(rootNode + "disableHitCollision", true);
+ this.blockSprinting = configuration.getBoolean(rootNode + "blockSprinting", true);
+ this.blockSneaking = configuration.getBoolean(rootNode + "blockSneaking", true);
+ this.blockWhitelist = loadMaterialList(rootNode + "blockWhitelist");
+ sanitizeValues();
+ }
+
+ /**
+ * Sanitizes configuration values to ensure they are within expected bounds
+ */
+ private void sanitizeValues() {
+ if (this.horizontalVelocity > 1 || this.horizontalVelocity <= 0) {
+ this.horizontalVelocity = 1;
+ }
+
+ if (this.verticalVelocity <= 0 || this.verticalVelocity > 75) {
+ this.verticalVelocity = 1;
+ }
+
+ if (this.randomlyInvertedTimer <= 0 || this.randomlyInvertedTimer > 3600) {
+ this.randomlyInvertedTimer = 7;
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(
+ "Current configuration:" +
+ "\n" + "Vertical velocity: " + verticalVelocity +
+ "\n" + "Horizontal velocity: " + horizontalVelocity +
+ "\n" + "Randomly inverted timer: " + randomlyInvertedTimer +
+ "\n" + "Must do groups in sequence: " + mustDoGroupedInSequence +
+ "\n" + "Ignore records until group beaten once: " + ignoreRecordsUntilGroupBeatenOnce +
+ "\n" + "Must do normal mode first: " + mustDoNormalModeFirst +
+ "\n" + "Make players invisible: " + makePlayersInvisible +
+ "\n" + "Disable hit collision: " + disableHitCollision +
+ "\n" + "Block whitelist: ");
+ for (Material material : blockWhitelist) {
+ builder.append("\n - ").append(material.name());
+ }
+ return builder.toString();
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/config/MiniGameConfiguration.java b/src/main/java/net/knarcraft/minigames/config/MiniGameConfiguration.java
new file mode 100644
index 0000000..6786308
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/config/MiniGameConfiguration.java
@@ -0,0 +1,52 @@
+package net.knarcraft.minigames.config;
+
+import net.knarcraft.minigames.util.MaterialHelper;
+import org.bukkit.Material;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A configuration for a mini-game
+ */
+public abstract class MiniGameConfiguration {
+
+ protected FileConfiguration configuration;
+
+ /**
+ * Instantiates a new mini-game configuration
+ *
+ * @param configuration The YAML configuration to use internally
+ */
+ public MiniGameConfiguration(@NotNull FileConfiguration configuration) {
+ this.configuration = configuration;
+ this.load();
+ }
+
+ /**
+ * Loads all configuration values from disk
+ *
+ * @param configuration The configuration to load
+ */
+ public void load(FileConfiguration configuration) {
+ this.configuration = configuration;
+ this.load();
+ }
+
+ /**
+ * Loads all configuration values from disk, using the current file configuration
+ */
+ protected abstract void load();
+
+ /**
+ * Loads the materials specified in the block whitelist
+ */
+ public @NotNull Set loadMaterialList(@NotNull String path) {
+ List> blockWhitelist = configuration.getList(path, new ArrayList<>());
+ return MaterialHelper.loadMaterialList(blockWhitelist);
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/config/ParkourConfiguration.java b/src/main/java/net/knarcraft/minigames/config/ParkourConfiguration.java
new file mode 100644
index 0000000..eb8703d
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/config/ParkourConfiguration.java
@@ -0,0 +1,100 @@
+package net.knarcraft.minigames.config;
+
+import org.bukkit.Material;
+import org.bukkit.configuration.file.FileConfiguration;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The configuration keeping track of parkour settings
+ */
+public class ParkourConfiguration extends MiniGameConfiguration {
+
+ private final static String rootNode = "parkour.";
+
+ private boolean enforceCheckpointOrder;
+ private boolean mustDoGroupedInSequence;
+ private boolean ignoreRecordsUntilGroupBeatenOnce;
+ private boolean makePlayersInvisible;
+ private Set killPlaneBlocks;
+
+ /**
+ * Instantiates a new dropper configuration
+ *
+ * @param configuration The YAML configuration to use internally
+ */
+ public ParkourConfiguration(FileConfiguration configuration) {
+ super(configuration);
+ }
+
+ /**
+ * Gets whether all checkpoints must be triggered in the order they are set when configuring the parkour arena
+ *
+ * @return Whether checkpoints must be triggered in order
+ */
+ public boolean enforceCheckpointOrder() {
+ return this.enforceCheckpointOrder;
+ }
+
+ /**
+ * Gets whether grouped arenas must be done in the set sequence
+ *
+ * @return Whether grouped arenas must be done in sequence
+ */
+ public boolean mustDoGroupedInSequence() {
+ return this.mustDoGroupedInSequence;
+ }
+
+ /**
+ * Gets whether records should be discarded, unless the player has already beaten all arenas in the group
+ *
+ * @return Whether to ignore records on the first play-through
+ */
+ public boolean ignoreRecordsUntilGroupBeatenOnce() {
+ return this.ignoreRecordsUntilGroupBeatenOnce;
+ }
+
+ /**
+ * Gets whether players should be made invisible while in an arena
+ *
+ * @return Whether players should be made invisible
+ */
+ public boolean makePlayersInvisible() {
+ return this.makePlayersInvisible;
+ }
+
+ /**
+ * Gets all types of blocks constituting parkour arenas' kill planes
+ *
+ * @return The types of blocks causing a player to fail a parkour map
+ */
+ public Set getKillPlaneBlocks() {
+ return new HashSet<>(this.killPlaneBlocks);
+ }
+
+ @Override
+ protected void load() {
+ this.enforceCheckpointOrder = configuration.getBoolean(rootNode + "enforceCheckpointOrder", false);
+ this.mustDoGroupedInSequence = configuration.getBoolean(rootNode + "mustDoGroupedInSequence", true);
+ this.ignoreRecordsUntilGroupBeatenOnce = configuration.getBoolean(rootNode + "ignoreRecordsUntilGroupBeatenOnce", false);
+ this.makePlayersInvisible = configuration.getBoolean(rootNode + "makePlayersInvisible", false);
+ this.killPlaneBlocks = loadMaterialList(rootNode + "killPlaneBlocks");
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(
+ "Current configuration:" +
+ "Current configuration:" +
+ "\n" + "Must do groups in sequence: " + mustDoGroupedInSequence +
+ "\n" + "Ignore records until group beaten once: " + ignoreRecordsUntilGroupBeatenOnce +
+ "\n" + "Make players invisible: " + makePlayersInvisible +
+ "\n" + "Kill plane blocks: ");
+ for (Material material : killPlaneBlocks) {
+ builder.append("\n - ").append(material.name());
+ }
+ return builder.toString();
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/config/SharedConfiguration.java b/src/main/java/net/knarcraft/minigames/config/SharedConfiguration.java
new file mode 100644
index 0000000..5c40856
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/config/SharedConfiguration.java
@@ -0,0 +1,69 @@
+package net.knarcraft.minigames.config;
+
+import org.bukkit.configuration.file.FileConfiguration;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * The configuration keeping track of shared settings
+ */
+public class SharedConfiguration extends MiniGameConfiguration {
+
+ private final static String rootNode = "shared.";
+ private double liquidHitBoxDepth;
+ private double solidHitBoxDistance;
+
+ /**
+ * Instantiates a new shared configuration
+ *
+ * @param configuration The YAML configuration to use internally
+ */
+ public SharedConfiguration(@NotNull FileConfiguration configuration) {
+ super(configuration);
+ }
+
+ /**
+ * Gets the negative depth a player must reach in a liquid block for fail/win detection to trigger
+ *
+ * This decides how far inside a non-solid block the player must go before detection triggers. The closer to -1
+ * it is, the more accurate it will seem to the player, but the likelihood of not detecting the hit increases.
+ *
+ * @return The liquid hit box depth to use
+ */
+ public double getLiquidHitBoxDepth() {
+ return this.liquidHitBoxDepth;
+ }
+
+ /**
+ * Gets the positive distance a player must at most be from a block for fail/win detection to trigger
+ *
+ * This decides the distance the player must be from a block below them before a hit triggers. If too low, the
+ * likelihood of detecting the hit decreases, but it won't look like the player hit the block without being near.
+ *
+ * @return The solid hit box distance to use
+ */
+ public double getSolidHitBoxDistance() {
+ return this.solidHitBoxDistance;
+ }
+
+ @Override
+ protected void load() {
+ this.liquidHitBoxDepth = configuration.getDouble(rootNode + "liquidHitBoxDepth", -0.8);
+ this.solidHitBoxDistance = configuration.getDouble(rootNode + "solidHitBoxDistance", 0.2);
+
+ if (this.liquidHitBoxDepth <= -1 || this.liquidHitBoxDepth > 0) {
+ this.liquidHitBoxDepth = -0.8;
+ }
+
+ if (this.solidHitBoxDistance <= 0 || this.solidHitBoxDistance > 1) {
+ this.solidHitBoxDistance = 0.2;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Current configuration:" +
+ "\n" + "Liquid hit box depth: " + liquidHitBoxDepth +
+ "\n" + "Solid hit box distance: " + solidHitBoxDistance;
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/container/SerializableContainer.java b/src/main/java/net/knarcraft/minigames/container/SerializableContainer.java
new file mode 100644
index 0000000..6ede0af
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/container/SerializableContainer.java
@@ -0,0 +1,49 @@
+package net.knarcraft.minigames.container;
+
+import org.bukkit.configuration.serialization.ConfigurationSerializable;
+
+/**
+ * A container that is serializable
+ *
+ * @param The type of the contained object
+ */
+public abstract class SerializableContainer implements ConfigurationSerializable {
+
+ private final K value;
+
+ /**
+ * Instantiates a new serializable container
+ *
+ * @param value The value to contain
+ */
+ public SerializableContainer(K value) {
+ this.value = value;
+ }
+
+ /**
+ * Gets the raw, non-serializable object
+ *
+ * @return The raw stored value
+ */
+ public K getRawValue() {
+ return value;
+ }
+
+ /**
+ * Gets a serializable container containing the given value
+ *
+ * @param value The value to make serializable
+ * @return The serializable value
+ */
+ public abstract SerializableContainer getSerializable(K value);
+
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof SerializableContainer>) {
+ return this.getRawValue().equals(((SerializableContainer>) object).getRawValue());
+ } else {
+ return false;
+ }
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/minigames/container/SerializableMaterial.java b/src/main/java/net/knarcraft/minigames/container/SerializableMaterial.java
new file mode 100644
index 0000000..5faeabf
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/container/SerializableMaterial.java
@@ -0,0 +1,52 @@
+package net.knarcraft.minigames.container;
+
+import org.bukkit.Material;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A material container able to be serialized
+ */
+public class SerializableMaterial extends SerializableContainer {
+
+ /**
+ * Instantiates a new serializable material
+ *
+ * @param material The material to contain
+ */
+ public SerializableMaterial(Material material) {
+ super(material);
+ }
+
+ @Override
+ public SerializableContainer getSerializable(Material value) {
+ return new SerializableMaterial(value);
+ }
+
+ @NotNull
+ @Override
+ public Map serialize() {
+ Map data = new HashMap<>();
+ data.put("name", getRawValue().name());
+ return data;
+ }
+
+ /**
+ * Deserializes a serialized material
+ *
+ * @param data The serialized data
+ * @return The deserialized material
+ */
+ @SuppressWarnings("unused")
+ public static SerializableMaterial deserialize(Map data) {
+ Material material = Material.matchMaterial((String) data.get("name"));
+ if (material == null) {
+ return null;
+ } else {
+ return new SerializableMaterial(material);
+ }
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/dropper/container/SerializableUUID.java b/src/main/java/net/knarcraft/minigames/container/SerializableUUID.java
similarity index 61%
rename from src/main/java/net/knarcraft/dropper/container/SerializableUUID.java
rename to src/main/java/net/knarcraft/minigames/container/SerializableUUID.java
index be7a5c4..2eb4a2b 100644
--- a/src/main/java/net/knarcraft/dropper/container/SerializableUUID.java
+++ b/src/main/java/net/knarcraft/minigames/container/SerializableUUID.java
@@ -1,6 +1,5 @@
-package net.knarcraft.dropper.container;
+package net.knarcraft.minigames.container;
-import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
@@ -9,16 +8,28 @@ import java.util.UUID;
/**
* A UUID container able to be serialized
- *
- * @param uuid The UUID stored by this record
*/
-public record SerializableUUID(UUID uuid) implements ConfigurationSerializable {
+public class SerializableUUID extends SerializableContainer {
+
+ /**
+ * Instantiates a new serializable uuid
+ *
+ * @param value The uuid to contain
+ */
+ public SerializableUUID(UUID value) {
+ super(value);
+ }
+
+ @Override
+ public SerializableContainer getSerializable(UUID value) {
+ return new SerializableUUID(value);
+ }
@NotNull
@Override
public Map serialize() {
Map data = new HashMap<>();
- data.put("id", uuid.toString());
+ data.put("id", getRawValue().toString());
return data;
}
@@ -38,13 +49,4 @@ public record SerializableUUID(UUID uuid) implements ConfigurationSerializable {
}
}
- @Override
- public boolean equals(Object object) {
- if (object instanceof SerializableUUID) {
- return this.uuid.equals(((SerializableUUID) object).uuid);
- } else {
- return false;
- }
- }
-
}
diff --git a/src/main/java/net/knarcraft/dropper/listener/CommandListener.java b/src/main/java/net/knarcraft/minigames/listener/CommandListener.java
similarity index 67%
rename from src/main/java/net/knarcraft/dropper/listener/CommandListener.java
rename to src/main/java/net/knarcraft/minigames/listener/CommandListener.java
index cea53b1..05391d8 100644
--- a/src/main/java/net/knarcraft/dropper/listener/CommandListener.java
+++ b/src/main/java/net/knarcraft/minigames/listener/CommandListener.java
@@ -1,7 +1,7 @@
-package net.knarcraft.dropper.listener;
+package net.knarcraft.minigames.listener;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.DropperArenaSession;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaSession;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -18,15 +18,16 @@ public class CommandListener implements Listener {
@EventHandler
public void onCommand(PlayerCommandPreprocessEvent event) {
Player player = event.getPlayer();
- DropperArenaSession existingSession = Dropper.getInstance().getPlayerRegistry().getArenaSession(
- player.getUniqueId());
+ ArenaSession existingSession = MiniGames.getInstance().getSession(player.getUniqueId());
if (existingSession == null) {
return;
}
List allowedCommands = new ArrayList<>();
- allowedCommands.add("/dropperleave");
- allowedCommands.add("/dleave");
+ allowedCommands.add("/miniGamesLeave");
+ allowedCommands.add("/mLeave");
+ allowedCommands.add("/dLeave");
+ allowedCommands.add("/pLeave");
String message = event.getMessage();
if (!message.startsWith("/")) {
@@ -34,7 +35,7 @@ public class CommandListener implements Listener {
}
for (String command : allowedCommands) {
- if (message.equals(command)) {
+ if (message.equalsIgnoreCase(command)) {
return;
}
}
diff --git a/src/main/java/net/knarcraft/minigames/listener/DamageListener.java b/src/main/java/net/knarcraft/minigames/listener/DamageListener.java
new file mode 100644
index 0000000..2d6f502
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/listener/DamageListener.java
@@ -0,0 +1,54 @@
+package net.knarcraft.minigames.listener;
+
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaSession;
+import net.knarcraft.minigames.arena.dropper.DropperArenaSession;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityCombustEvent;
+import org.bukkit.event.entity.EntityDamageEvent;
+
+/**
+ * A listener for checking if a player takes damage within a dropper arena
+ */
+public class DamageListener implements Listener {
+
+ @EventHandler
+ public void onPlayerDamage(EntityDamageEvent event) {
+ // Only player damage matters
+ if (event.getEntityType() != EntityType.PLAYER) {
+ return;
+ }
+
+ Player player = (Player) event.getEntity();
+
+ // We don't care about damage outside arenas
+ ArenaSession arenaSession = MiniGames.getInstance().getSession(player.getUniqueId());
+ if (arenaSession == null) {
+ return;
+ }
+
+ event.setCancelled(true);
+
+ // Only trigger a loss when a player suffers fall damage in a dropper arena
+ if (arenaSession instanceof DropperArenaSession && event.getCause() == EntityDamageEvent.DamageCause.FALL) {
+ arenaSession.triggerLoss();
+ }
+ }
+
+ @EventHandler(ignoreCancelled = true)
+ public void onPlayerCombustion(EntityCombustEvent event) {
+ if (event.getEntityType() != EntityType.PLAYER) {
+ return;
+ }
+
+ ArenaSession arenaSession = MiniGames.getInstance().getSession(event.getEntity().getUniqueId());
+ if (arenaSession != null) {
+ // Cancel combustion for any player in an arena
+ event.setCancelled(true);
+ }
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/dropper/listener/MoveListener.java b/src/main/java/net/knarcraft/minigames/listener/MoveListener.java
similarity index 50%
rename from src/main/java/net/knarcraft/dropper/listener/MoveListener.java
rename to src/main/java/net/knarcraft/minigames/listener/MoveListener.java
index 26dad3f..c9c2120 100644
--- a/src/main/java/net/knarcraft/dropper/listener/MoveListener.java
+++ b/src/main/java/net/knarcraft/minigames/listener/MoveListener.java
@@ -1,12 +1,16 @@
-package net.knarcraft.dropper.listener;
+package net.knarcraft.minigames.listener;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry;
-import net.knarcraft.dropper.arena.DropperArenaSession;
-import net.knarcraft.dropper.property.ArenaGameMode;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.Arena;
+import net.knarcraft.minigames.arena.ArenaSession;
+import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
+import net.knarcraft.minigames.arena.dropper.DropperArenaSession;
+import net.knarcraft.minigames.arena.parkour.ParkourArena;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaSession;
+import net.knarcraft.minigames.config.DropperConfiguration;
+import net.knarcraft.minigames.config.ParkourConfiguration;
+import net.knarcraft.minigames.config.SharedConfiguration;
import org.bukkit.Location;
-import org.bukkit.Material;
-import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -17,6 +21,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Calendar;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
@@ -24,6 +29,20 @@ import java.util.Set;
*/
public class MoveListener implements Listener {
+ private final DropperConfiguration dropperConfiguration;
+ private final ParkourConfiguration parkourConfiguration;
+
+ /**
+ * Instantiates a new move listener
+ *
+ * @param dropperConfiguration The dropper configuration to use
+ * @param parkourConfiguration The parkour configuration to use
+ */
+ public MoveListener(DropperConfiguration dropperConfiguration, ParkourConfiguration parkourConfiguration) {
+ this.dropperConfiguration = dropperConfiguration;
+ this.parkourConfiguration = parkourConfiguration;
+ }
+
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
// Ignore if no actual movement is happening
@@ -31,15 +50,66 @@ public class MoveListener implements Listener {
return;
}
- Player player = event.getPlayer();
- DropperArenaPlayerRegistry playerRegistry = Dropper.getInstance().getPlayerRegistry();
- DropperArenaSession arenaSession = playerRegistry.getArenaSession(player.getUniqueId());
- if (arenaSession == null) {
+ ArenaSession session = MiniGames.getInstance().getSession(event.getPlayer().getUniqueId());
+ if (session instanceof DropperArenaSession dropperSession) {
+ doDropperArenaChecks(event, dropperSession);
+ } else if (session instanceof ParkourArenaSession parkourSession) {
+ doParkourArenaChecks(event, parkourSession);
+ }
+ }
+
+ /**
+ * Performs the necessary checks and tasks for the player's session
+ *
+ * @param event The move event triggered
+ * @param arenaSession The dropper session of the player triggering the event
+ */
+ private void doParkourArenaChecks(@NotNull PlayerMoveEvent event, ParkourArenaSession arenaSession) {
+ // Ignore movement which won't cause the player's block to change
+ if (event.getTo() == null || event.getFrom().getBlock() == event.getTo().getBlock()) {
return;
}
+ // Only do block type checking if the block beneath the player changes
+ if (checkForSpecialBlock(arenaSession, event.getTo())) {
+ return;
+ }
+
+ // Check if the player reached one of the checkpoints for the arena
+ ParkourArena arena = arenaSession.getArena();
+ List checkpoints = arena.getCheckpoints();
+ for (Location checkpoint : checkpoints) {
+ Location previousCheckpoint = arenaSession.getRegisteredCheckpoint();
+ if (checkpoint.getBlock().equals(event.getTo().getBlock()) && !checkpoint.equals(previousCheckpoint)) {
+ if (parkourConfiguration.enforceCheckpointOrder()) {
+ int checkpointIndex = checkpoints.indexOf(checkpoint);
+ int previousIndex = previousCheckpoint == null ? -1 : checkpoints.indexOf(previousCheckpoint);
+ if (checkpointIndex - previousIndex != 1) {
+ continue;
+ }
+ }
+
+ arenaSession.registerCheckpoint(checkpoint.clone());
+ event.getPlayer().sendMessage("Checkpoint reached!");
+ return;
+ }
+ }
+ }
+
+ /**
+ * Performs the necessary checks and tasks for the player's session
+ *
+ * @param event The move event triggered
+ * @param arenaSession The dropper session of the player triggering the event
+ */
+ private void doDropperArenaChecks(@NotNull PlayerMoveEvent event, @NotNull DropperArenaSession arenaSession) {
+ if (event.getTo() == null) {
+ return;
+ }
// Prevent the player from flying upwards while in flight mode
- if (event.getFrom().getY() < event.getTo().getY()) {
+ if (event.getFrom().getY() < event.getTo().getY() ||
+ (dropperConfiguration.blockSneaking() && event.getPlayer().isSneaking()) ||
+ (dropperConfiguration.blockSprinting() && event.getPlayer().isSprinting())) {
event.setCancelled(true);
return;
}
@@ -64,20 +134,17 @@ public class MoveListener implements Listener {
* @param toLocation The location the player's session is about to hit
* @return True if a special block has been hit
*/
- private boolean checkForSpecialBlock(DropperArenaSession arenaSession, Location toLocation) {
- /* This decides how far inside a non-solid block the player must go before detection triggers. The closer to -1
- it is, the more accurate it will seem to the player, but the likelihood of not detecting the hit decreases */
- double liquidDepth = -0.8;
- /* This decides the distance the player must be from the block below before a hit triggers. If too low, the
- likelihood of detecting the hit decreases, but the immersion increases. */
- double solidDepth = 0.2;
+ private boolean checkForSpecialBlock(ArenaSession arenaSession, Location toLocation) {
+ SharedConfiguration sharedConfiguration = MiniGames.getInstance().getSharedConfiguration();
+ double liquidDepth = sharedConfiguration.getLiquidHitBoxDepth();
+ double solidDepth = sharedConfiguration.getSolidHitBoxDistance();
+
+ Arena arena = arenaSession.getArena();
- // Check if the player enters water
- Material winBlockType = arenaSession.getArena().getWinBlockType();
// For water, only trigger when the player enters the water, but trigger earlier for everything else
- double depth = !winBlockType.isSolid() ? liquidDepth : solidDepth;
+ double depth = arena.winLocationIsSolid() ? solidDepth : liquidDepth;
for (Block block : getBlocksBeneathLocation(toLocation, depth)) {
- if (block.getType() == winBlockType) {
+ if (arena.willCauseWin(block)) {
arenaSession.triggerWin();
return true;
}
@@ -85,9 +152,7 @@ public class MoveListener implements Listener {
// Check if the player is about to hit a non-air and non-liquid block
for (Block block : getBlocksBeneathLocation(toLocation, solidDepth)) {
- Material blockType = block.getType();
- if (!blockType.isAir() && blockType != Material.STRUCTURE_VOID && blockType != Material.WATER &&
- blockType != Material.LAVA && !Tag.WALL_SIGNS.isTagged(blockType)) {
+ if (!block.getType().isAir() && arena.willCauseLoss(block)) {
arenaSession.triggerLoss();
return true;
}
@@ -118,10 +183,11 @@ public class MoveListener implements Listener {
* @param session The session to update the velocity for
*/
private void updatePlayerVelocity(@NotNull DropperArenaSession session) {
+ // Override the vertical velocity
Player player = session.getPlayer();
Vector playerVelocity = player.getVelocity();
double arenaVelocity = session.getArena().getPlayerVerticalVelocity();
- Vector newVelocity = new Vector(playerVelocity.getX(), -arenaVelocity, playerVelocity.getZ());
+ Vector newVelocity = new Vector(playerVelocity.getX() * 5, -arenaVelocity, playerVelocity.getZ() * 5);
player.setVelocity(newVelocity);
// Toggle the direction of the player's flying, as necessary
@@ -134,12 +200,12 @@ public class MoveListener implements Listener {
* @param session The session to possibly invert flying for
*/
private void toggleFlyInversion(@NotNull DropperArenaSession session) {
- if (session.getGameMode() != ArenaGameMode.RANDOM_INVERTED) {
+ if (session.getGameMode() != DropperArenaGameMode.RANDOM_INVERTED) {
return;
}
Player player = session.getPlayer();
float horizontalVelocity = session.getArena().getPlayerHorizontalVelocity();
- float secondsBetweenToggle = 7;
+ float secondsBetweenToggle = dropperConfiguration.getRandomlyInvertedTimer();
int seconds = Calendar.getInstance().get(Calendar.SECOND);
/*
diff --git a/src/main/java/net/knarcraft/dropper/listener/PlayerLeaveListener.java b/src/main/java/net/knarcraft/minigames/listener/PlayerLeaveListener.java
similarity index 52%
rename from src/main/java/net/knarcraft/dropper/listener/PlayerLeaveListener.java
rename to src/main/java/net/knarcraft/minigames/listener/PlayerLeaveListener.java
index a57bfa5..7ca6239 100644
--- a/src/main/java/net/knarcraft/dropper/listener/PlayerLeaveListener.java
+++ b/src/main/java/net/knarcraft/minigames/listener/PlayerLeaveListener.java
@@ -1,16 +1,16 @@
-package net.knarcraft.dropper.listener;
+package net.knarcraft.minigames.listener;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.DropperArenaSession;
+import net.knarcraft.minigames.MiniGames;
+import net.knarcraft.minigames.arena.ArenaSession;
+import net.knarcraft.minigames.arena.parkour.ParkourArenaSession;
import org.bukkit.Bukkit;
+import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
@@ -22,17 +22,18 @@ import java.util.logging.Level;
*/
public class PlayerLeaveListener implements Listener {
- private final Map leftSessions = new HashMap<>();
+ private final Map leftSessions = new HashMap<>();
@EventHandler
public void onPlayerLeave(PlayerQuitEvent event) {
Player player = event.getPlayer();
- DropperArenaSession arenaSession = getSession(player);
+
+ ArenaSession arenaSession = MiniGames.getInstance().getSession(event.getPlayer().getUniqueId());
if (arenaSession == null) {
return;
}
- Dropper.getInstance().getLogger().log(Level.WARNING, "Found player " + player.getUniqueId() +
+ MiniGames.log(Level.WARNING, "Found player " + player.getUniqueId() +
" leaving in the middle of a session!");
leftSessions.put(player.getUniqueId(), arenaSession);
}
@@ -42,10 +43,10 @@ public class PlayerLeaveListener implements Listener {
UUID playerId = event.getPlayer().getUniqueId();
// Force the player to quit from the session once they re-join
if (leftSessions.containsKey(playerId)) {
- Dropper.getInstance().getLogger().log(Level.WARNING, "Found un-exited dropper session!");
- Bukkit.getScheduler().runTaskLater(Dropper.getInstance(), () -> {
+ MiniGames.log(Level.WARNING, "Found un-exited dropper session!");
+ Bukkit.getScheduler().runTaskLater(MiniGames.getInstance(), () -> {
leftSessions.get(playerId).triggerQuit(false);
- Dropper.getInstance().getLogger().log(Level.WARNING, "Triggered a quit!");
+ MiniGames.log(Level.WARNING, "Triggered a quit!");
leftSessions.remove(playerId);
}, 80);
}
@@ -53,30 +54,26 @@ public class PlayerLeaveListener implements Listener {
@EventHandler
public void onPlayerTeleport(PlayerTeleportEvent event) {
- if (event.getTo() == null || event.isCancelled()) {
+ Location targetLocation = event.getTo();
+ if (targetLocation == null || event.isCancelled()) {
return;
}
- DropperArenaSession arenaSession = getSession(event.getPlayer());
+ ArenaSession arenaSession = MiniGames.getInstance().getSession(event.getPlayer().getUniqueId());
if (arenaSession == null) {
return;
}
- if (event.getTo().equals(arenaSession.getArena().getSpawnLocation())) {
+ if (targetLocation.equals(arenaSession.getArena().getSpawnLocation())) {
+ return;
+ }
+
+ if (arenaSession instanceof ParkourArenaSession parkourArenaSession &&
+ targetLocation.equals(parkourArenaSession.getRegisteredCheckpoint())) {
return;
}
arenaSession.triggerQuit(false);
}
- /**
- * Gets the arena session for the given player
- *
- * @param player The player to get the arena session for
- * @return The player's session, or null if not in a session
- */
- private @Nullable DropperArenaSession getSession(@NotNull Player player) {
- return Dropper.getInstance().getPlayerRegistry().getArenaSession(player.getUniqueId());
- }
-
}
diff --git a/src/main/java/net/knarcraft/minigames/placeholder/DropperRecordExpansion.java b/src/main/java/net/knarcraft/minigames/placeholder/DropperRecordExpansion.java
new file mode 100644
index 0000000..3357f68
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/placeholder/DropperRecordExpansion.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.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 A reference to the dropper plugin
+ */
+ 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);
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/dropper/placeholder/GroupRecordCache.java b/src/main/java/net/knarcraft/minigames/placeholder/GroupRecordCache.java
similarity index 66%
rename from src/main/java/net/knarcraft/dropper/placeholder/GroupRecordCache.java
rename to src/main/java/net/knarcraft/minigames/placeholder/GroupRecordCache.java
index ca916de..872ebe2 100644
--- a/src/main/java/net/knarcraft/dropper/placeholder/GroupRecordCache.java
+++ b/src/main/java/net/knarcraft/minigames/placeholder/GroupRecordCache.java
@@ -1,8 +1,8 @@
-package net.knarcraft.dropper.placeholder;
+package net.knarcraft.minigames.placeholder;
-import net.knarcraft.dropper.arena.record.ArenaRecord;
-import net.knarcraft.dropper.placeholder.parsing.RecordType;
-import net.knarcraft.dropper.property.ArenaGameMode;
+import net.knarcraft.minigames.arena.ArenaGameMode;
+import net.knarcraft.minigames.arena.record.ArenaRecord;
+import net.knarcraft.minigames.property.RecordType;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
@@ -15,7 +15,8 @@ import java.util.Set;
* @param records The stored records
* @param createdTime The time this cache was created
*/
-public record GroupRecordCache>(@NotNull ArenaGameMode gameMode, @NotNull RecordType recordType,
+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/dropper/placeholder/DropperRecordExpansion.java b/src/main/java/net/knarcraft/minigames/placeholder/RecordExpansion.java
similarity index 81%
rename from src/main/java/net/knarcraft/dropper/placeholder/DropperRecordExpansion.java
rename to src/main/java/net/knarcraft/minigames/placeholder/RecordExpansion.java
index 3d69cc7..9dca66c 100644
--- a/src/main/java/net/knarcraft/dropper/placeholder/DropperRecordExpansion.java
+++ b/src/main/java/net/knarcraft/minigames/placeholder/RecordExpansion.java
@@ -1,17 +1,16 @@
-package net.knarcraft.dropper.placeholder;
+package net.knarcraft.minigames.placeholder;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.DropperArena;
-import net.knarcraft.dropper.arena.DropperArenaGroup;
-import net.knarcraft.dropper.arena.DropperArenaHandler;
-import net.knarcraft.dropper.arena.DropperArenaRecordsRegistry;
-import net.knarcraft.dropper.arena.record.ArenaRecord;
-import net.knarcraft.dropper.placeholder.parsing.InfoType;
-import net.knarcraft.dropper.placeholder.parsing.RecordType;
-import net.knarcraft.dropper.placeholder.parsing.SelectionType;
-import net.knarcraft.dropper.property.ArenaGameMode;
-import net.knarcraft.dropper.util.DropperGroupRecordHelper;
+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;
@@ -29,28 +28,21 @@ import java.util.UUID;
import java.util.function.Supplier;
/**
- * A placeholder expansion for dropper record placeholders
+ * A placeholder expansion for parkour record placeholders
*/
-public class DropperRecordExpansion extends PlaceholderExpansion {
+public abstract class RecordExpansion extends PlaceholderExpansion {
- private final Dropper plugin;
+ private final ArenaHandler, ?> arenaHandler;
private final Map>> groupRecordDeathsCache;
private final Map>> groupRecordTimeCache;
/**
* Initializes a new record expansion
- *
- * @param plugin A reference to the dropper plugin
*/
- public DropperRecordExpansion(Dropper plugin) {
- this.plugin = plugin;
+ public RecordExpansion(ArenaHandler, ?> arenaHandler) {
this.groupRecordDeathsCache = new HashMap<>();
this.groupRecordTimeCache = new HashMap<>();
- }
-
- @Override
- public String getIdentifier() {
- return "dropper";
+ this.arenaHandler = arenaHandler;
}
@Override
@@ -76,7 +68,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
return parameters;
}
RecordType recordType = RecordType.getFromString(parts[1]);
- ArenaGameMode gameMode = ArenaGameMode.matchGamemode(parts[2]);
+ ArenaGameMode gameMode = parseGameMode(parts[2]);
SelectionType selectionType = SelectionType.getFromString(parts[3]);
String identifier = parts[4];
int recordNumber = Integer.parseInt(parts[5]) - 1;
@@ -87,7 +79,6 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
}
String info = null;
- DropperArenaHandler arenaHandler = plugin.getArenaHandler();
if (selectionType == SelectionType.GROUP) {
info = getGroupRecord(arenaHandler, identifier, gameMode, recordType, recordNumber, infoType);
} else if (selectionType == SelectionType.ARENA) {
@@ -98,7 +89,23 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
}
/**
- * Gets a piece of record information from a dropper arena group
+ * 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
@@ -108,11 +115,11 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
* @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,
+ 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
- DropperArenaGroup group;
+ ArenaGroup, ?> group;
try {
group = arenaHandler.getGroup(UUID.fromString(identifier));
} catch (IllegalArgumentException exception) {
@@ -124,9 +131,9 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
ArenaRecord> record;
if (recordType == RecordType.DEATHS) {
- record = getGroupDeathRecord(group, gameMode, recordNumber);
+ record = getGroupDeathRecord(group, gameMode, recordNumber, arenaHandler);
} else {
- record = getGroupTimeRecord(group, gameMode, recordNumber);
+ record = getGroupTimeRecord(group, gameMode, recordNumber, arenaHandler);
}
// If a record number is not found, leave it blank, so it looks neat
@@ -143,12 +150,14 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
* @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 DropperArenaGroup group,
- @NotNull ArenaGameMode gameMode, int recordNumber) {
+ private @Nullable ArenaRecord> getGroupTimeRecord(@NotNull ArenaGroup, ?> group,
+ @NotNull ArenaGameMode gameMode, int recordNumber,
+ @NotNull ArenaHandler, ?> arenaHandler) {
return getCachedGroupRecord(group, gameMode, RecordType.TIME, recordNumber, groupRecordTimeCache,
- () -> DropperGroupRecordHelper.getCombinedTime(group, gameMode));
+ () -> GroupRecordHelper.getCombinedTime(group, gameMode, arenaHandler));
}
/**
@@ -157,12 +166,14 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
* @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 DropperArenaGroup group,
- @NotNull ArenaGameMode gameMode, int recordNumber) {
+ private @Nullable ArenaRecord> getGroupDeathRecord(@NotNull ArenaGroup, ?> group,
+ @NotNull ArenaGameMode gameMode, int recordNumber,
+ @NotNull ArenaHandler, ?> arenaHandler) {
return getCachedGroupRecord(group, gameMode, RecordType.DEATHS, recordNumber, groupRecordDeathsCache,
- () -> DropperGroupRecordHelper.getCombinedDeaths(group, gameMode));
+ () -> GroupRecordHelper.getCombinedDeaths(group, gameMode, arenaHandler));
}
/**
@@ -177,7 +188,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
* @param The type of the provided records
* @return The specified record, or null if not found
*/
- private > @Nullable ArenaRecord> getCachedGroupRecord(@NotNull DropperArenaGroup group,
+ private > @Nullable ArenaRecord> getCachedGroupRecord(@NotNull ArenaGroup, ?> group,
@NotNull ArenaGameMode gameMode,
@NotNull RecordType recordType,
int recordNumber,
@@ -214,7 +225,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
}
/**
- * Gets a piece of record information from a dropper arena
+ * 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
@@ -224,11 +235,11 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
* @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,
+ 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
- DropperArena arena;
+ Arena arena;
try {
arena = arenaHandler.getArena(UUID.fromString(identifier));
} catch (IllegalArgumentException exception) {
@@ -237,8 +248,8 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
if (arena == null) {
return null;
}
- @NotNull Map registries = arena.getData().recordRegistries();
- DropperArenaRecordsRegistry recordsRegistry = registries.get(gameMode);
+ @NotNull Map registries = arena.getData().getRecordRegistries();
+ ArenaRecordsRegistry recordsRegistry = registries.get(gameMode);
ArenaRecord> record = getRecord(recordsRegistry, recordType, recordNumber);
@@ -258,7 +269,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
* @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,
+ private @Nullable ArenaRecord> getRecord(@NotNull ArenaRecordsRegistry recordsRegistry,
@NotNull RecordType recordType, int recordNumber) {
return switch (recordType) {
case TIME -> getRecord(new HashSet<>(recordsRegistry.getShortestTimeMilliSecondsRecords()), recordNumber);
diff --git a/src/main/java/net/knarcraft/dropper/placeholder/parsing/InfoType.java b/src/main/java/net/knarcraft/minigames/placeholder/parsing/InfoType.java
similarity index 94%
rename from src/main/java/net/knarcraft/dropper/placeholder/parsing/InfoType.java
rename to src/main/java/net/knarcraft/minigames/placeholder/parsing/InfoType.java
index fc663f6..0672c35 100644
--- a/src/main/java/net/knarcraft/dropper/placeholder/parsing/InfoType.java
+++ b/src/main/java/net/knarcraft/minigames/placeholder/parsing/InfoType.java
@@ -1,4 +1,4 @@
-package net.knarcraft.dropper.placeholder.parsing;
+package net.knarcraft.minigames.placeholder.parsing;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
diff --git a/src/main/java/net/knarcraft/dropper/placeholder/parsing/SelectionType.java b/src/main/java/net/knarcraft/minigames/placeholder/parsing/SelectionType.java
similarity index 94%
rename from src/main/java/net/knarcraft/dropper/placeholder/parsing/SelectionType.java
rename to src/main/java/net/knarcraft/minigames/placeholder/parsing/SelectionType.java
index bace341..9ceab68 100644
--- a/src/main/java/net/knarcraft/dropper/placeholder/parsing/SelectionType.java
+++ b/src/main/java/net/knarcraft/minigames/placeholder/parsing/SelectionType.java
@@ -1,4 +1,4 @@
-package net.knarcraft.dropper.placeholder.parsing;
+package net.knarcraft.minigames.placeholder.parsing;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
diff --git a/src/main/java/net/knarcraft/dropper/property/RecordResult.java b/src/main/java/net/knarcraft/minigames/property/RecordResult.java
similarity index 87%
rename from src/main/java/net/knarcraft/dropper/property/RecordResult.java
rename to src/main/java/net/knarcraft/minigames/property/RecordResult.java
index e18e159..2561d60 100644
--- a/src/main/java/net/knarcraft/dropper/property/RecordResult.java
+++ b/src/main/java/net/knarcraft/minigames/property/RecordResult.java
@@ -1,4 +1,4 @@
-package net.knarcraft.dropper.property;
+package net.knarcraft.minigames.property;
/**
* A representation of all possible record results
diff --git a/src/main/java/net/knarcraft/dropper/placeholder/parsing/RecordType.java b/src/main/java/net/knarcraft/minigames/property/RecordType.java
similarity index 93%
rename from src/main/java/net/knarcraft/dropper/placeholder/parsing/RecordType.java
rename to src/main/java/net/knarcraft/minigames/property/RecordType.java
index 90219f9..feccaac 100644
--- a/src/main/java/net/knarcraft/dropper/placeholder/parsing/RecordType.java
+++ b/src/main/java/net/knarcraft/minigames/property/RecordType.java
@@ -1,4 +1,4 @@
-package net.knarcraft.dropper.placeholder.parsing;
+package net.knarcraft.minigames.property;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
diff --git a/src/main/java/net/knarcraft/minigames/util/ArenaStorageHelper.java b/src/main/java/net/knarcraft/minigames/util/ArenaStorageHelper.java
new file mode 100644
index 0000000..c64ad1d
--- /dev/null
+++ b/src/main/java/net/knarcraft/minigames/util/ArenaStorageHelper.java
@@ -0,0 +1,31 @@
+package net.knarcraft.minigames.util;
+
+import net.knarcraft.minigames.MiniGames;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.UUID;
+import java.util.logging.Level;
+
+public final class ArenaStorageHelper {
+
+ private ArenaStorageHelper() {
+
+ }
+
+ /**
+ * Gets the file used to store the given arena id's data
+ *
+ * @param root The root directory for the file
+ * @param arenaId The id of the arena to get a data file for
+ * @return The file the arena's data is/should be stored in
+ */
+ static @NotNull File getArenaDataFile(File root, @NotNull UUID arenaId) {
+ File arenaDataFile = new File(root, arenaId + ".yml");
+ if (!root.exists() && !root.mkdirs()) {
+ MiniGames.log(Level.SEVERE, "Unable to create the arena data directories");
+ }
+ return arenaDataFile;
+ }
+
+}
diff --git a/src/main/java/net/knarcraft/dropper/util/ArenaStorageHelper.java b/src/main/java/net/knarcraft/minigames/util/DropperArenaStorageHelper.java
similarity index 52%
rename from src/main/java/net/knarcraft/dropper/util/ArenaStorageHelper.java
rename to src/main/java/net/knarcraft/minigames/util/DropperArenaStorageHelper.java
index 0af7a65..b158669 100644
--- a/src/main/java/net/knarcraft/dropper/util/ArenaStorageHelper.java
+++ b/src/main/java/net/knarcraft/minigames/util/DropperArenaStorageHelper.java
@@ -1,14 +1,16 @@
-package net.knarcraft.dropper.util;
+package net.knarcraft.minigames.util;
-import net.knarcraft.dropper.Dropper;
-import net.knarcraft.dropper.arena.DropperArena;
-import net.knarcraft.dropper.arena.DropperArenaData;
-import net.knarcraft.dropper.arena.DropperArenaGroup;
-import net.knarcraft.dropper.arena.DropperArenaRecordsRegistry;
-import net.knarcraft.dropper.container.SerializableMaterial;
-import net.knarcraft.dropper.container.SerializableUUID;
-import net.knarcraft.dropper.property.ArenaGameMode;
-import net.knarcraft.dropper.property.ArenaStorageKey;
+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.DropperArenaData;
+import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
+import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
+import net.knarcraft.minigames.arena.dropper.DropperArenaRecordsRegistry;
+import net.knarcraft.minigames.arena.dropper.DropperArenaStorageKey;
+import net.knarcraft.minigames.container.SerializableMaterial;
+import net.knarcraft.minigames.container.SerializableUUID;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
@@ -24,20 +26,22 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
-import java.util.logging.Logger;
+
+import static net.knarcraft.minigames.util.ArenaStorageHelper.getArenaDataFile;
/**
* A helper class for saving and loading arenas
*/
-public final class ArenaStorageHelper {
+public final class DropperArenaStorageHelper {
- private final static String arenasConfigurationSection = "arenas";
- private final static String groupsConfigurationSection = "groups";
- private static final File arenaFile = new File(Dropper.getInstance().getDataFolder(), "arenas.yml");
- private static final File groupFile = new File(Dropper.getInstance().getDataFolder(), "groups.yml");
- private static final File arenaDataFolder = new File(Dropper.getInstance().getDataFolder(), "arena_data");
+ private final static File dataFolder = MiniGames.getInstance().getDataFolder();
+ private final static String dropperArenasConfigurationSection = "dropperArenas";
+ private final static String dropperGroupsConfigurationSection = "dropperGroups";
+ private static final File dropperArenaFile = new File(dataFolder, "dropper_arenas.yml");
+ private static final File dropperGroupFile = new File(dataFolder, "dropper_groups.yml");
+ private static final File dropperArenaDataFolder = new File(dataFolder, "dropper_arena_data");
- private ArenaStorageHelper() {
+ private DropperArenaStorageHelper() {
}
@@ -49,13 +53,13 @@ public final class ArenaStorageHelper {
*/
public static void saveDropperArenaGroups(@NotNull Set arenaGroups) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
- ConfigurationSection groupSection = configuration.createSection(groupsConfigurationSection);
+ ConfigurationSection groupSection = configuration.createSection(dropperGroupsConfigurationSection);
for (DropperArenaGroup arenaGroup : arenaGroups) {
groupSection.set(arenaGroup.getGroupId().toString(), arenaGroup);
}
- configuration.save(groupFile);
+ configuration.save(dropperGroupFile);
}
/**
@@ -64,8 +68,8 @@ public final class ArenaStorageHelper {
* @return