Parkour implementation safety save

This is just a safety save in case the code gets too broken to fix.
This commit is contained in:
Kristian Knarvik 2023-04-13 13:24:43 +02:00
parent b1e86a928b
commit 9a3f9841ab
55 changed files with 2356 additions and 695 deletions

View File

@ -1,20 +1,21 @@
# Dropper # MiniGames
This is a plugin for a dropper mini-game (try to reach the bottom without hitting any obstacles). This plugin adds several mini-games.
To create an arena, simply use `/droppercreate <name>`, where \<name> is simply the name used to differentiate and To create a dropper arena, simply use `/droppercreate <name>`, where \<name> is simply the name used to differentiate
and
recognize the arena. Your location will be used as the spawn location for anyone joining the dropper arena. To start recognize the arena. Your location will be used as the spawn location for anyone joining the dropper arena. To start
playing, simply use `/dropperjoin <name>`, where \<name> is the same as you specified upon creation. playing, simply use `/dropperjoin <name>`, where \<name> is the same as you specified upon creation.
To modify To modify the arena, use `/dropperedit <name> <property> <value>`.
## Permissions ## Permissions
| Node | Description | | Node | Description |
|----------------|----------------------------------------------------| |------------------|------------------------------------------------------|
| dropper.admin | Gives all permissions. | | minigames.admin | Gives all permissions. |
| dropper.join | Allows a player to participate in dropper arenas. | | minigames.join | Allows a player to participate in mini-game arenas. |
| dropper.create | Allows a player to create a new dropper arena. | | minigames.create | Allows a player to create a new mini-game arena. |
| dropper.edit | Allows a player to edit an existing dropper arena. | | minigames.edit | Allows a player to edit an existing mini-game arena. |
| dropper.remove | Allows a player to remove a dropper arena. | | minigames.remove | Allows a player to remove a mini-game arena. |
## Commands ## Commands

View File

@ -5,13 +5,13 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>net.knarcraft</groupId> <groupId>net.knarcraft</groupId>
<artifactId>Dropper</artifactId> <artifactId>MiniGames</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Dropper</name> <name>MiniGames</name>
<description>A plugin for dropper mini-games</description> <description>A plugin which adds various mini-games</description>
<properties> <properties>
<java.version>16</java.version> <java.version>16</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

View File

@ -1,12 +1,14 @@
package net.knarcraft.dropper; package net.knarcraft.dropper;
import net.knarcraft.dropper.arena.ArenaGameMode; import net.knarcraft.dropper.arena.dropper.DropperArenaData;
import net.knarcraft.dropper.arena.DropperArenaData; import net.knarcraft.dropper.arena.dropper.DropperArenaGameMode;
import net.knarcraft.dropper.arena.DropperArenaGroup; import net.knarcraft.dropper.arena.dropper.DropperArenaGroup;
import net.knarcraft.dropper.arena.DropperArenaHandler; import net.knarcraft.dropper.arena.dropper.DropperArenaHandler;
import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry; import net.knarcraft.dropper.arena.dropper.DropperArenaPlayerRegistry;
import net.knarcraft.dropper.arena.DropperArenaRecordsRegistry; import net.knarcraft.dropper.arena.dropper.DropperArenaRecordsRegistry;
import net.knarcraft.dropper.arena.DropperArenaSession; import net.knarcraft.dropper.arena.dropper.DropperArenaSession;
import net.knarcraft.dropper.arena.parkour.ParkourArenaHandler;
import net.knarcraft.dropper.arena.parkour.ParkourArenaPlayerRegistry;
import net.knarcraft.dropper.arena.record.IntegerRecord; import net.knarcraft.dropper.arena.record.IntegerRecord;
import net.knarcraft.dropper.arena.record.LongRecord; import net.knarcraft.dropper.arena.record.LongRecord;
import net.knarcraft.dropper.command.CreateArenaCommand; import net.knarcraft.dropper.command.CreateArenaCommand;
@ -47,39 +49,59 @@ import java.util.logging.Level;
* The dropper plugin's main class * The dropper plugin's main class
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class Dropper extends JavaPlugin { public final class MiniGames extends JavaPlugin {
private static Dropper instance; private static MiniGames instance;
private DropperConfiguration configuration; private DropperConfiguration configuration;
private DropperArenaHandler arenaHandler; private DropperArenaHandler dropperArenaHandler;
private DropperArenaPlayerRegistry playerRegistry; private DropperArenaPlayerRegistry dropperArenaPlayerRegistry;
private DropperRecordExpansion dropperRecordExpansion; private DropperRecordExpansion dropperRecordExpansion;
private ParkourArenaHandler parkourArenaHandler;
private ParkourArenaPlayerRegistry parkourArenaPlayerRegistry;
/** /**
* Gets an instance of this plugin * Gets an instance of this plugin
* *
* @return <p>An instance of this plugin, or null if not initialized yet.</p> * @return <p>An instance of this plugin, or null if not initialized yet.</p>
*/ */
public static Dropper getInstance() { public static MiniGames getInstance() {
return instance; return instance;
} }
/** /**
* Gets the arena handler for this instance * Gets the dropper arena handler for this instance
* *
* @return <p>A dropper arena handler</p> * @return <p>A dropper arena handler</p>
*/ */
public DropperArenaHandler getArenaHandler() { public DropperArenaHandler getDropperArenaHandler() {
return this.arenaHandler; return this.dropperArenaHandler;
} }
/** /**
* Gets the arena player registry for this instance * Gets the parkour arena handler for this instance
*
* @return <p>A parkour arena handler</p>
*/
public ParkourArenaHandler getParkourArenaHandler() {
return this.parkourArenaHandler;
}
/**
* Gets the dropper arena player registry for this instance
* *
* @return <p>A dropper arena player registry</p> * @return <p>A dropper arena player registry</p>
*/ */
public DropperArenaPlayerRegistry getPlayerRegistry() { public DropperArenaPlayerRegistry getDropperArenaPlayerRegistry() {
return this.playerRegistry; return this.dropperArenaPlayerRegistry;
}
/**
* Gets the parkour arena player registry for this instance
*
* @return <p>A parkour arena player registry</p>
*/
public ParkourArenaPlayerRegistry getParkourArenaPlayerRegistry() {
return this.parkourArenaPlayerRegistry;
} }
/** /**
@ -98,7 +120,7 @@ public final class Dropper extends JavaPlugin {
* @param message <p>The message to log</p> * @param message <p>The message to log</p>
*/ */
public static void log(Level level, String message) { public static void log(Level level, String message) {
Dropper.getInstance().getLogger().log(level, message); MiniGames.getInstance().getLogger().log(level, message);
} }
/** /**
@ -106,8 +128,8 @@ public final class Dropper extends JavaPlugin {
*/ */
public void reload() { public void reload() {
// Load all arenas again // Load all arenas again
this.arenaHandler.loadArenas(); this.dropperArenaHandler.load();
this.arenaHandler.loadGroups(); this.parkourArenaHandler.load();
// Reload configuration // Reload configuration
this.reloadConfig(); this.reloadConfig();
@ -126,7 +148,7 @@ public final class Dropper extends JavaPlugin {
ConfigurationSerialization.registerClass(SerializableUUID.class); ConfigurationSerialization.registerClass(SerializableUUID.class);
ConfigurationSerialization.registerClass(DropperArenaData.class); ConfigurationSerialization.registerClass(DropperArenaData.class);
ConfigurationSerialization.registerClass(DropperArenaGroup.class); ConfigurationSerialization.registerClass(DropperArenaGroup.class);
ConfigurationSerialization.registerClass(ArenaGameMode.class); ConfigurationSerialization.registerClass(DropperArenaGameMode.class);
ConfigurationSerialization.registerClass(LongRecord.class); ConfigurationSerialization.registerClass(LongRecord.class);
ConfigurationSerialization.registerClass(IntegerRecord.class); ConfigurationSerialization.registerClass(IntegerRecord.class);
} }
@ -141,10 +163,12 @@ public final class Dropper extends JavaPlugin {
reloadConfig(); reloadConfig();
this.configuration = new DropperConfiguration(this.getConfig()); this.configuration = new DropperConfiguration(this.getConfig());
this.configuration.load(); this.configuration.load();
this.playerRegistry = new DropperArenaPlayerRegistry(); this.dropperArenaPlayerRegistry = new DropperArenaPlayerRegistry();
this.arenaHandler = new DropperArenaHandler(); this.dropperArenaHandler = new DropperArenaHandler();
this.arenaHandler.loadArenas(); this.dropperArenaHandler.load();
this.arenaHandler.loadGroups();
this.parkourArenaHandler = new ParkourArenaHandler();
this.parkourArenaHandler.load();
PluginManager pluginManager = getServer().getPluginManager(); PluginManager pluginManager = getServer().getPluginManager();
pluginManager.registerEvents(new DamageListener(), this); pluginManager.registerEvents(new DamageListener(), this);
@ -175,7 +199,7 @@ public final class Dropper extends JavaPlugin {
public void onDisable() { public void onDisable() {
// Throw out currently playing players before exiting // Throw out currently playing players before exiting
for (Player player : getServer().getOnlinePlayers()) { for (Player player : getServer().getOnlinePlayers()) {
DropperArenaSession session = playerRegistry.getArenaSession(player.getUniqueId()); DropperArenaSession session = dropperArenaPlayerRegistry.getArenaSession(player.getUniqueId());
if (session != null) { if (session != null) {
session.triggerQuit(true); session.triggerQuit(true);
} }

View File

@ -0,0 +1,33 @@
package net.knarcraft.dropper.arena;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* An interface describing all arenas
*/
public interface Arena {
/**
* Gets the id of this arena
*
* @return <p>This arena's identifier</p>
*/
@NotNull UUID getArenaId();
/**
* Gets the name of this arena
*
* @return <p>The name of this arena</p>
*/
@NotNull String getArenaName();
/**
* Gets this arena's sanitized name
*
* @return <p>This arena's sanitized name</p>
*/
@NotNull String getArenaNameSanitized();
}

View File

@ -0,0 +1,102 @@
package net.knarcraft.dropper.arena;
import net.knarcraft.dropper.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;
/**
* An interface describing generic arena data
*/
public abstract class ArenaData implements ConfigurationSerializable {
protected final @NotNull UUID arenaId;
private final @NotNull Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries;
private final @NotNull Map<ArenaGameMode, Set<UUID>> playersCompleted;
/**
* Instantiates arena data
*
* @param arenaId <p>The id of the arena this data belongs to</p>
* @param recordRegistries <p>The registry storing records for this arena</p>
* @param playersCompleted <p>The players that have completed this arena</p>
*/
public ArenaData(@NotNull UUID arenaId, @NotNull Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries,
@NotNull Map<ArenaGameMode, Set<UUID>> playersCompleted) {
this.arenaId = arenaId;
this.recordRegistries = recordRegistries;
this.playersCompleted = playersCompleted;
}
/**
* Gets the id of this arena
*
* @return <p>The id of this arena</p>
*/
public @NotNull UUID getArenaId() {
return this.arenaId;
}
/**
* Gets whether the given player has cleared this arena
*
* @param arenaGameMode <p>The game-mode to check for</p>
* @param player <p>The player to check</p>
* @return <p>True if the player has cleared the arena this data belongs to</p>
*/
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 <p>The game-mode the player completed</p>
* @param player <p>The player that completed this data's arena</p>
*/
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<String, Object> serialize() {
Map<String, Object> data = new HashMap<>();
data.put("arenaId", new SerializableUUID(this.arenaId));
data.put("recordsRegistry", this.recordRegistries);
// Convert normal UUIDs to serializable UUIDs
Map<ArenaGameMode, Set<SerializableUUID>> serializablePlayersCompleted = new HashMap<>();
for (ArenaGameMode arenaGameMode : this.playersCompleted.keySet()) {
Set<SerializableUUID> playersCompleted = new HashSet<>();
for (UUID playerCompleted : this.playersCompleted.get(arenaGameMode)) {
playersCompleted.add(new SerializableUUID(playerCompleted));
}
serializablePlayersCompleted.put(arenaGameMode, playersCompleted);
}
data.put("playersCompleted", serializablePlayersCompleted);
return data;
}
}

View File

@ -1,66 +1,4 @@
package net.knarcraft.dropper.arena; package net.knarcraft.dropper.arena;
import org.bukkit.configuration.serialization.ConfigurationSerializable; public interface ArenaGameMode {
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
/**
* A representation of possible arena game-modes
*/
public enum ArenaGameMode implements ConfigurationSerializable {
/**
* The default game-mode. Failing once throws the player out.
*/
DEFAULT,
/**
* A game-mode where the player's directional buttons are inverted
*/
INVERTED,
/**
* A game-mode which swaps between normal and inverted controls on a set schedule of a few seconds
*/
RANDOM_INVERTED,
;
/**
* Tries to match the correct game-mode according to the given string
*
* @param gameMode <p>The game-mode string to match</p>
* @return <p>The specified arena game-mode</p>
*/
public static @NotNull ArenaGameMode matchGamemode(@NotNull String gameMode) {
String sanitized = gameMode.trim().toLowerCase();
if (sanitized.matches("(invert(ed)?|inverse)")) {
return ArenaGameMode.INVERTED;
} else if (sanitized.matches("rand(om)?")) {
return ArenaGameMode.RANDOM_INVERTED;
} else {
return ArenaGameMode.DEFAULT;
}
}
@NotNull
@Override
public Map<String, Object> serialize() {
Map<String, Object> data = new HashMap<>();
data.put("name", this.name());
return data;
}
/**
* Deserializes the arena game-mode specified by the given data
*
* @param data <p>The data to deserialize</p>
* @return <p>The deserialized arena game-mode</p>
*/
@SuppressWarnings("unused")
public static ArenaGameMode deserialize(Map<String, Object> data) {
return ArenaGameMode.valueOf((String) data.get("name"));
}
} }

View File

@ -0,0 +1,165 @@
package net.knarcraft.dropper.arena;
import net.knarcraft.dropper.arena.dropper.DropperArenaGroup;
import net.knarcraft.dropper.container.SerializableUUID;
import net.knarcraft.dropper.util.StringSanitizer;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public abstract class ArenaGroup implements ConfigurationSerializable {
/**
* The unique id for this group of arenas
*/
private final UUID groupId;
/**
* The unique name for this group of arenas
*/
private final String groupName;
/**
* The arenas in this group, ordered from stage 1 to stage n
*/
protected final List<UUID> arenas;
/**
* Instantiates a new dropper arena group
*
* @param groupName <p>The name of this group</p>
*/
protected ArenaGroup(@NotNull String groupName) {
this.groupId = UUID.randomUUID();
this.groupName = groupName;
this.arenas = new ArrayList<>();
}
/**
* Instantiates a new arena group
*
* @param groupId <p>The unique id of this group</p>
* @param groupName <p>The name of this group</p>
* @param arenas <p>The arenas in this group</p>
*/
protected ArenaGroup(@NotNull UUID groupId, @NotNull String groupName, @NotNull List<UUID> arenas) {
this.groupId = groupId;
this.groupName = groupName;
this.arenas = new ArrayList<>(arenas);
}
/**
* Gets the id of this arena group
*
* @return <p>The id of this group</p>
*/
public @NotNull UUID getGroupId() {
return this.groupId;
}
/**
* Gets the name of this arena group
*
* @return <p>The name of this group</p>
*/
public @NotNull String getGroupName() {
return this.groupName;
}
/**
* Gets the arenas contained in this group in the correct order
*
* @return <p>The ids of the arenas in this group</p>
*/
public @NotNull List<UUID> getArenas() {
return new ArrayList<>(arenas);
}
/**
* Removes the given arena from this group
*
* @param arenaId <p>The id of the arena to remove</p>
*/
public void removeArena(UUID arenaId) {
this.arenas.remove(arenaId);
}
/**
* Adds an arena to the end of this group
*
* @param arenaId <p>The arena to add to this group</p>
*/
public void addArena(UUID arenaId) {
addArena(arenaId, this.arenas.size());
}
/**
* Adds an arena to the end of this group
*
* @param arenaId <p>The arena to add to this group</p>
* @param index <p>The index to put the arena in</p>
*/
public void addArena(UUID arenaId, int index) {
// Make sure we don't have duplicates
if (!this.arenas.contains(arenaId)) {
this.arenas.add(index, arenaId);
}
}
/**
* Gets this group's name, but sanitized
*
* @return <p>The sanitized group name</p>
*/
public @NotNull String getGroupNameSanitized() {
return StringSanitizer.sanitizeArenaName(this.getGroupName());
}
/**
* Swaps the arenas at the given indices
*
* @param index1 <p>The index of the first arena to swap</p>
* @param index2 <p>The index of the second arena to swap</p>
*/
public void swapArenas(int index1, int index2) {
// Change nothing if not a valid request
if (index1 == index2 || index1 < 0 || index2 < 0 || index1 >= this.arenas.size() ||
index2 >= this.arenas.size()) {
return;
}
// Swap the two arena ids
UUID temporaryValue = this.arenas.get(index2);
this.arenas.set(index2, this.arenas.get(index1));
this.arenas.set(index1, temporaryValue);
}
@NotNull
@Override
public Map<String, Object> serialize() {
Map<String, Object> data = new HashMap<>();
data.put("groupId", new SerializableUUID(this.groupId));
data.put("groupName", this.groupName);
List<SerializableUUID> serializableArenas = new ArrayList<>();
for (UUID arenaId : arenas) {
serializableArenas.add(new SerializableUUID(arenaId));
}
data.put("arenas", serializableArenas);
return data;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof DropperArenaGroup otherGroup)) {
return false;
}
return this.getGroupNameSanitized().equals(otherGroup.getGroupNameSanitized());
}
}

View File

@ -0,0 +1,4 @@
package net.knarcraft.dropper.arena;
public interface ArenaRecordsRegistry {
}

View File

@ -1,252 +0,0 @@
package net.knarcraft.dropper.arena;
import net.knarcraft.dropper.Dropper;
import net.knarcraft.dropper.container.SerializableUUID;
import net.knarcraft.dropper.util.StringSanitizer;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
/**
* A sorted group of arenas that must be completed in sequence
*/
public class DropperArenaGroup implements ConfigurationSerializable {
/**
* The unique id for this group of arenas
*/
private final UUID groupId;
/**
* The unique name for this group of arenas
*/
private final String groupName;
/**
* The arenas in this group, ordered from stage 1 to stage n
*/
private final List<UUID> arenas;
/**
* Instantiates a new dropper arena group
*
* @param groupName <p>The name of this group</p>
*/
public DropperArenaGroup(@NotNull String groupName) {
this.groupId = UUID.randomUUID();
this.groupName = groupName;
this.arenas = new ArrayList<>();
}
/**
* Instantiates a new dropper arena group
*
* @param groupId <p>The unique id of this group</p>
* @param groupName <p>The name of this group</p>
* @param arenas <p>The arenas in this group</p>
*/
private DropperArenaGroup(@NotNull UUID groupId, @NotNull String groupName, @NotNull List<UUID> arenas) {
this.groupId = groupId;
this.groupName = groupName;
this.arenas = new ArrayList<>(arenas);
}
/**
* Gets the id of this dropper arena group
*
* @return <p>The id of this group</p>
*/
public @NotNull UUID getGroupId() {
return this.groupId;
}
/**
* Gets the name of this dropper arena group
*
* @return <p>The name of this group</p>
*/
public @NotNull String getGroupName() {
return this.groupName;
}
/**
* Gets the arenas contained in this group in the correct order
*
* @return <p>The ids of the arenas in this group</p>
*/
public @NotNull List<UUID> getArenas() {
return new ArrayList<>(arenas);
}
/**
* Removes the given dropper arena from this group
*
* @param arenaId <p>The id of the dropper arena to remove</p>
*/
public void removeArena(UUID arenaId) {
this.arenas.remove(arenaId);
}
/**
* Adds an arena to the end of this group
*
* @param arenaId <p>The arena to add to this group</p>
*/
public void addArena(UUID arenaId) {
addArena(arenaId, this.arenas.size());
}
/**
* Adds an arena to the end of this group
*
* @param arenaId <p>The arena to add to this group</p>
* @param index <p>The index to put the arena in</p>
*/
public void addArena(UUID arenaId, int index) {
// Make sure we don't have duplicates
if (!this.arenas.contains(arenaId)) {
this.arenas.add(index, arenaId);
}
}
/**
* Checks whether the given player has beaten all arenas in this group on the given game-mode
*
* @param gameMode <p>The game-mode to check</p>
* @param player <p>The player to check</p>
* @return <p>True if the player has beaten all arenas, false otherwise</p>
*/
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) {
// The arena would only be null if the arena has been deleted, but not removed from this group
Dropper.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)) {
return false;
}
}
return true;
}
/**
* Gets whether the given player can play the given arena part of this group, on the given game-mode
*
* @param gameMode <p>The game-mode the player is trying to play</p>
* @param player <p>The player to check</p>
* @param arenaId <p>The id of the arena in this group to check</p>
* @return <p>True if the player is allowed to play the arena</p>
* @throws IllegalArgumentException <p>If checking an arena not in this group</p>
*/
public boolean canPlay(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;
}
DropperArena dropperArena = arenaHandler.getArena(anArenaId);
if (dropperArena == null) {
// The arena would only be null if the arena has been deleted, but not removed from this group
Dropper.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;
}
}
return false;
}
/**
* Gets this group's name, but sanitized
*
* @return <p>The sanitized group name</p>
*/
public @NotNull String getGroupNameSanitized() {
return StringSanitizer.sanitizeArenaName(this.getGroupName());
}
/**
* Swaps the arenas at the given indices
*
* @param index1 <p>The index of the first arena to swap</p>
* @param index2 <p>The index of the second arena to swap</p>
*/
public void swapArenas(int index1, int index2) {
// Change nothing if not a valid request
if (index1 == index2 || index1 < 0 || index2 < 0 || index1 >= this.arenas.size() ||
index2 >= this.arenas.size()) {
return;
}
// Swap the two arena ids
UUID temporaryValue = this.arenas.get(index2);
this.arenas.set(index2, this.arenas.get(index1));
this.arenas.set(index1, temporaryValue);
}
@NotNull
@Override
public Map<String, Object> serialize() {
Map<String, Object> data = new HashMap<>();
data.put("groupId", new SerializableUUID(this.groupId));
data.put("groupName", this.groupName);
List<SerializableUUID> serializableArenas = new ArrayList<>();
for (UUID arenaId : arenas) {
serializableArenas.add(new SerializableUUID(arenaId));
}
data.put("arenas", serializableArenas);
return data;
}
/**
* Deserializes the given data
*
* @param data <p>The data to deserialize</p>
* @return <p>The deserialized arena group</p>
*/
@SuppressWarnings({"unused", "unchecked"})
public static @NotNull DropperArenaGroup deserialize(@NotNull Map<String, Object> data) {
UUID id = ((SerializableUUID) data.get("groupId")).uuid();
String name = (String) data.get("groupName");
List<SerializableUUID> serializableArenas = (List<SerializableUUID>) data.get("arenas");
List<UUID> 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)) {
return false;
}
return this.getGroupNameSanitized().equals(otherGroup.getGroupNameSanitized());
}
}

View File

@ -1,11 +1,13 @@
package net.knarcraft.dropper.arena; package net.knarcraft.dropper.arena.dropper;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.Arena;
import net.knarcraft.dropper.arena.ArenaGameMode;
import net.knarcraft.dropper.arena.ArenaRecordsRegistry;
import net.knarcraft.dropper.config.DropperConfiguration; import net.knarcraft.dropper.config.DropperConfiguration;
import net.knarcraft.dropper.util.StringSanitizer; import net.knarcraft.dropper.util.StringSanitizer;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -13,10 +15,12 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import static net.knarcraft.dropper.util.InputValidationHelper.isInvalid;
/** /**
* A representation of one dropper arena * A representation of one dropper arena
*/ */
public class DropperArena { public class DropperArena implements Arena {
/** /**
* An unique and persistent identifier for this arena * An unique and persistent identifier for this arena
@ -103,7 +107,7 @@ public class DropperArena {
*/ */
public DropperArena(@NotNull String arenaName, @NotNull Location spawnLocation, public DropperArena(@NotNull String arenaName, @NotNull Location spawnLocation,
@NotNull DropperArenaHandler arenaHandler) { @NotNull DropperArenaHandler arenaHandler) {
DropperConfiguration configuration = Dropper.getInstance().getDropperConfiguration(); DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration();
this.arenaId = UUID.randomUUID(); this.arenaId = UUID.randomUUID();
this.arenaName = arenaName; this.arenaName = arenaName;
this.spawnLocation = spawnLocation; this.spawnLocation = spawnLocation;
@ -111,8 +115,8 @@ public class DropperArena {
this.playerVerticalVelocity = configuration.getVerticalVelocity(); this.playerVerticalVelocity = configuration.getVerticalVelocity();
this.playerHorizontalVelocity = configuration.getHorizontalVelocity(); this.playerHorizontalVelocity = configuration.getHorizontalVelocity();
Map<ArenaGameMode, DropperArenaRecordsRegistry> recordRegistries = new HashMap<>(); Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries = new HashMap<>();
for (ArenaGameMode arenaGameMode : ArenaGameMode.values()) { for (ArenaGameMode arenaGameMode : DropperArenaGameMode.values()) {
recordRegistries.put(arenaGameMode, new DropperArenaRecordsRegistry(this.arenaId)); recordRegistries.put(arenaGameMode, new DropperArenaRecordsRegistry(this.arenaId));
} }
@ -130,20 +134,12 @@ public class DropperArena {
return this.dropperArenaData; return this.dropperArenaData;
} }
/** @Override
* Gets the id of this arena
*
* @return <p>This arena's identifier</p>
*/
public @NotNull UUID getArenaId() { public @NotNull UUID getArenaId() {
return this.arenaId; return this.arenaId;
} }
/** @Override
* Gets the name of this arena
*
* @return <p>The name of this arena</p>
*/
public @NotNull String getArenaName() { public @NotNull String getArenaName() {
return this.arenaName; return this.arenaName;
} }
@ -156,7 +152,7 @@ public class DropperArena {
* @return <p>This arena's spawn location.</p> * @return <p>This arena's spawn location.</p>
*/ */
public @NotNull Location getSpawnLocation() { public @NotNull Location getSpawnLocation() {
return this.spawnLocation; return this.spawnLocation.clone();
} }
/** /**
@ -165,7 +161,7 @@ public class DropperArena {
* @return <p>This arena's exit location, or null if no such location is set.</p> * @return <p>This arena's exit location, or null if no such location is set.</p>
*/ */
public @Nullable Location getExitLocation() { public @Nullable Location getExitLocation() {
return this.exitLocation; return this.exitLocation != null ? this.exitLocation.clone() : null;
} }
/** /**
@ -200,11 +196,7 @@ public class DropperArena {
return this.winBlockType; return this.winBlockType;
} }
/** @Override
* Gets this arena's sanitized name
*
* @return <p>This arena's sanitized name</p>
*/
public @NotNull String getArenaNameSanitized() { public @NotNull String getArenaNameSanitized() {
return StringSanitizer.sanitizeArenaName(this.getArenaName()); return StringSanitizer.sanitizeArenaName(this.getArenaName());
} }
@ -320,15 +312,4 @@ public class DropperArena {
return this.getArenaNameSanitized().equals(otherArena.getArenaNameSanitized()); return this.getArenaNameSanitized().equals(otherArena.getArenaNameSanitized());
} }
/**
* Checks whether the given location is valid
*
* @param location <p>The location to validate</p>
* @return <p>False if the location is valid</p>
*/
private boolean isInvalid(Location location) {
World world = location.getWorld();
return world == null || !world.getWorldBorder().isInside(location);
}
} }

View File

@ -0,0 +1,75 @@
package net.knarcraft.dropper.arena.dropper;
import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.ArenaData;
import net.knarcraft.dropper.arena.ArenaGameMode;
import net.knarcraft.dropper.arena.ArenaRecordsRegistry;
import net.knarcraft.dropper.container.SerializableUUID;
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;
/**
* Data stored for an arena
*/
public class DropperArenaData extends ArenaData {
/**
* Instantiates a new dropper arena data object
*
* @param arenaId <p>The id of the arena this data belongs to</p>
* @param recordRegistries <p>The registries of this arena's records</p>
* @param playersCompleted <p>The set of the players that have cleared this arena for each game-mode</p>
*/
public DropperArenaData(@NotNull UUID arenaId,
@NotNull Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries,
@NotNull Map<ArenaGameMode, Set<UUID>> 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 <p>The data to deserialize</p>
* @return <p>The deserialized dropper arena data</p>
*/
@SuppressWarnings({"unused", "unchecked"})
public static @NotNull DropperArenaData deserialize(@NotNull Map<String, Object> data) {
SerializableUUID serializableUUID = (SerializableUUID) data.get("arenaId");
Map<ArenaGameMode, ArenaRecordsRegistry> recordsRegistry =
(Map<ArenaGameMode, ArenaRecordsRegistry>) data.get("recordsRegistry");
Map<ArenaGameMode, Set<SerializableUUID>> playersCompletedData =
(Map<ArenaGameMode, Set<SerializableUUID>>) data.get("playersCompleted");
if (recordsRegistry == null) {
recordsRegistry = new HashMap<>();
} else if (playersCompletedData == null) {
playersCompletedData = new HashMap<>();
}
// Convert the serializable UUIDs to normal UUIDs
Map<ArenaGameMode, Set<UUID>> allPlayersCompleted = new HashMap<>();
for (ArenaGameMode arenaGameMode : playersCompletedData.keySet()) {
Set<UUID> playersCompleted = new HashSet<>();
for (SerializableUUID completedId : playersCompletedData.get(arenaGameMode)) {
playersCompleted.add(completedId.uuid());
}
allPlayersCompleted.put(arenaGameMode, playersCompleted);
if (!recordsRegistry.containsKey(arenaGameMode) || recordsRegistry.get(arenaGameMode) == null) {
recordsRegistry.put(arenaGameMode, new DropperArenaRecordsRegistry(serializableUUID.uuid()));
}
}
return new DropperArenaData(serializableUUID.uuid(), recordsRegistry, allPlayersCompleted);
}
}

View File

@ -1,4 +1,4 @@
package net.knarcraft.dropper.arena; package net.knarcraft.dropper.arena.dropper;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -8,7 +8,7 @@ import java.util.function.Function;
/** /**
* All editable properties of a dropper arena * All editable properties of a dropper arena
*/ */
public enum ArenaEditableProperty { public enum DropperArenaEditableProperty {
/** /**
* The name of the arena * The name of the arena
@ -49,7 +49,7 @@ public enum ArenaEditableProperty {
* *
* @param argumentString <p>The argument string used to specify this property</p> * @param argumentString <p>The argument string used to specify this property</p>
*/ */
ArenaEditableProperty(@NotNull String argumentString, Function<DropperArena, String> currentValueProvider) { DropperArenaEditableProperty(@NotNull String argumentString, Function<DropperArena, String> currentValueProvider) {
this.argumentString = argumentString; this.argumentString = argumentString;
this.currentValueProvider = currentValueProvider; this.currentValueProvider = currentValueProvider;
} }
@ -79,8 +79,8 @@ public enum ArenaEditableProperty {
* @param argumentString <p>The argument string used to specify an editable property</p> * @param argumentString <p>The argument string used to specify an editable property</p>
* @return <p>The corresponding editable property, or null if not found</p> * @return <p>The corresponding editable property, or null if not found</p>
*/ */
public static @Nullable ArenaEditableProperty getFromArgumentString(String argumentString) { public static @Nullable DropperArenaEditableProperty getFromArgumentString(String argumentString) {
for (ArenaEditableProperty property : ArenaEditableProperty.values()) { for (DropperArenaEditableProperty property : DropperArenaEditableProperty.values()) {
if (property.argumentString.equalsIgnoreCase(argumentString)) { if (property.argumentString.equalsIgnoreCase(argumentString)) {
return property; return property;
} }

View File

@ -0,0 +1,67 @@
package net.knarcraft.dropper.arena.dropper;
import net.knarcraft.dropper.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 DropperArenaGameMode implements ConfigurationSerializable, ArenaGameMode {
/**
* The default game-mode. Failing once throws the player out.
*/
DEFAULT,
/**
* A game-mode where the player's directional buttons are inverted
*/
INVERTED,
/**
* A game-mode which swaps between normal and inverted controls on a set schedule of a few seconds
*/
RANDOM_INVERTED,
;
/**
* Tries to match the correct game-mode according to the given string
*
* @param gameMode <p>The game-mode string to match</p>
* @return <p>The specified arena game-mode</p>
*/
public static @NotNull DropperArenaGameMode matchGamemode(@NotNull String gameMode) {
String sanitized = gameMode.trim().toLowerCase();
if (sanitized.matches("(invert(ed)?|inverse)")) {
return DropperArenaGameMode.INVERTED;
} else if (sanitized.matches("rand(om)?")) {
return DropperArenaGameMode.RANDOM_INVERTED;
} else {
return DropperArenaGameMode.DEFAULT;
}
}
@NotNull
@Override
public Map<String, Object> serialize() {
Map<String, Object> data = new HashMap<>();
data.put("name", this.name());
return data;
}
/**
* Deserializes the arena game-mode specified by the given data
*
* @param data <p>The data to deserialize</p>
* @return <p>The deserialized arena game-mode</p>
*/
@SuppressWarnings("unused")
public static DropperArenaGameMode deserialize(Map<String, Object> data) {
return DropperArenaGameMode.valueOf((String) data.get("name"));
}
}

View File

@ -0,0 +1,122 @@
package net.knarcraft.dropper.arena.dropper;
import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.ArenaGroup;
import net.knarcraft.dropper.container.SerializableUUID;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
/**
* A sorted group of arenas that must be completed in sequence
*/
public class DropperArenaGroup extends ArenaGroup {
/**
* Instantiates a new dropper arena group
*
* @param groupName <p>The name of this group</p>
*/
public DropperArenaGroup(@NotNull String groupName) {
super(groupName);
}
/**
* Instantiates a new dropper arena group
*
* @param groupId <p>The unique id of this group</p>
* @param groupName <p>The name of this group</p>
* @param arenas <p>The arenas in this group</p>
*/
private DropperArenaGroup(@NotNull UUID groupId, @NotNull String groupName, @NotNull List<UUID> arenas) {
super(groupId, groupName, arenas);
}
/**
* Checks whether the given player has beaten all arenas in this group on the given game-mode
*
* @param gameMode <p>The game-mode to check</p>
* @param player <p>The player to check</p>
* @return <p>True if the player has beaten all arenas, false otherwise</p>
*/
public boolean hasBeatenAll(DropperArenaGameMode gameMode, Player player) {
DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
for (UUID anArenaId : this.getArenas()) {
DropperArena dropperArena = arenaHandler.getArena(anArenaId);
if (dropperArena == null) {
// The arena would only be null if the arena has been deleted, but not removed from this group
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)) {
return false;
}
}
return true;
}
/**
* Gets whether the given player can play the given arena part of this group, on the given game-mode
*
* @param gameMode <p>The game-mode the player is trying to play</p>
* @param player <p>The player to check</p>
* @param arenaId <p>The id of the arena in this group to check</p>
* @return <p>True if the player is allowed to play the arena</p>
* @throws IllegalArgumentException <p>If checking an arena not in this group</p>
*/
public boolean canPlay(DropperArenaGameMode 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 = MiniGames.getInstance().getDropperArenaHandler();
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;
}
DropperArena dropperArena = arenaHandler.getArena(anArenaId);
if (dropperArena == null) {
// The arena would only be null if the arena has been deleted, but not removed from this group
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;
}
}
return false;
}
/**
* Deserializes the given data
*
* @param data <p>The data to deserialize</p>
* @return <p>The deserialized arena group</p>
*/
@SuppressWarnings({"unused", "unchecked"})
public static @NotNull DropperArenaGroup deserialize(@NotNull Map<String, Object> data) {
UUID id = ((SerializableUUID) data.get("groupId")).uuid();
String name = (String) data.get("groupName");
List<SerializableUUID> serializableArenas = (List<SerializableUUID>) data.get("arenas");
List<UUID> arenas = new ArrayList<>();
for (SerializableUUID arenaId : serializableArenas) {
arenas.add(arenaId.uuid());
}
return new DropperArenaGroup(id, name, arenas);
}
}

View File

@ -1,6 +1,6 @@
package net.knarcraft.dropper.arena; package net.knarcraft.dropper.arena.dropper;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.util.ArenaStorageHelper; import net.knarcraft.dropper.util.ArenaStorageHelper;
import net.knarcraft.dropper.util.StringSanitizer; import net.knarcraft.dropper.util.StringSanitizer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -159,12 +159,12 @@ public class DropperArenaHandler {
*/ */
public void removeArena(@NotNull DropperArena arena) { public void removeArena(@NotNull DropperArena arena) {
UUID arenaId = arena.getArenaId(); UUID arenaId = arena.getArenaId();
Dropper.getInstance().getPlayerRegistry().removeForArena(arena); MiniGames.getInstance().getDropperArenaPlayerRegistry().removeForArena(arena);
this.arenas.remove(arenaId); this.arenas.remove(arenaId);
this.arenaNameLookup.remove(arena.getArenaNameSanitized()); this.arenaNameLookup.remove(arena.getArenaNameSanitized());
this.arenaGroups.remove(arenaId); this.arenaGroups.remove(arenaId);
if (!ArenaStorageHelper.removeArenaData(arenaId)) { if (!ArenaStorageHelper.removeDropperArenaData(arenaId)) {
Dropper.log(Level.WARNING, "Unable to remove dropper arena data file " + arenaId + ".yml. " + MiniGames.log(Level.WARNING, "Unable to remove dropper arena data file " + arenaId + ".yml. " +
"You must remove it manually!"); "You must remove it manually!");
} }
this.saveArenas(); this.saveArenas();
@ -177,10 +177,10 @@ public class DropperArenaHandler {
*/ */
public void saveData(UUID arenaId) { public void saveData(UUID arenaId) {
try { try {
ArenaStorageHelper.saveArenaData(this.arenas.get(arenaId).getData()); ArenaStorageHelper.saveDropperArenaData(this.arenas.get(arenaId).getData());
} catch (IOException e) { } catch (IOException e) {
Dropper.log(Level.SEVERE, "Unable to save arena data! Data loss can occur!"); MiniGames.log(Level.SEVERE, "Unable to save arena data! Data loss can occur!");
Dropper.log(Level.SEVERE, e.getMessage()); MiniGames.log(Level.SEVERE, e.getMessage());
} }
} }
@ -191,16 +191,24 @@ public class DropperArenaHandler {
try { try {
ArenaStorageHelper.saveDropperArenaGroups(new HashSet<>(this.arenaGroups.values())); ArenaStorageHelper.saveDropperArenaGroups(new HashSet<>(this.arenaGroups.values()));
} catch (IOException e) { } catch (IOException e) {
Dropper.log(Level.SEVERE, "Unable to save current arena groups! " + MiniGames.log(Level.SEVERE, "Unable to save current arena groups! " +
"Data loss can occur!"); "Data loss can occur!");
Dropper.log(Level.SEVERE, e.getMessage()); MiniGames.log(Level.SEVERE, e.getMessage());
} }
} }
/**
* Loads all arenas and groups from disk
*/
public void load() {
loadArenas();
loadGroups();
}
/** /**
* Loads all dropper groups from disk * Loads all dropper groups from disk
*/ */
public void loadGroups() { private void loadGroups() {
Set<DropperArenaGroup> arenaGroups = ArenaStorageHelper.loadDropperArenaGroups(); Set<DropperArenaGroup> arenaGroups = ArenaStorageHelper.loadDropperArenaGroups();
Map<UUID, DropperArenaGroup> arenaGroupMap = new HashMap<>(); Map<UUID, DropperArenaGroup> arenaGroupMap = new HashMap<>();
for (DropperArenaGroup arenaGroup : arenaGroups) { for (DropperArenaGroup arenaGroup : arenaGroups) {
@ -216,19 +224,19 @@ public class DropperArenaHandler {
*/ */
public void saveArenas() { public void saveArenas() {
try { try {
ArenaStorageHelper.saveArenas(this.arenas); ArenaStorageHelper.saveDropperArenas(this.arenas);
} catch (IOException e) { } catch (IOException e) {
Dropper.log(Level.SEVERE, "Unable to save current arenas! " + MiniGames.log(Level.SEVERE, "Unable to save current arenas! " +
"Data loss can occur!"); "Data loss can occur!");
Dropper.log(Level.SEVERE, e.getMessage()); MiniGames.log(Level.SEVERE, e.getMessage());
} }
} }
/** /**
* Loads all arenas from disk * Loads all arenas from disk
*/ */
public void loadArenas() { private void loadArenas() {
this.arenas = ArenaStorageHelper.loadArenas(); this.arenas = ArenaStorageHelper.loadDropperArenas();
// Save a map from arena name to arena id for improved performance // Save a map from arena name to arena id for improved performance
this.arenaNameLookup = new HashMap<>(); this.arenaNameLookup = new HashMap<>();

View File

@ -1,4 +1,4 @@
package net.knarcraft.dropper.arena; package net.knarcraft.dropper.arena.dropper;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;

View File

@ -1,6 +1,7 @@
package net.knarcraft.dropper.arena; package net.knarcraft.dropper.arena.dropper;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.ArenaRecordsRegistry;
import net.knarcraft.dropper.arena.record.ArenaRecord; import net.knarcraft.dropper.arena.record.ArenaRecord;
import net.knarcraft.dropper.arena.record.IntegerRecord; import net.knarcraft.dropper.arena.record.IntegerRecord;
import net.knarcraft.dropper.arena.record.LongRecord; import net.knarcraft.dropper.arena.record.LongRecord;
@ -23,7 +24,7 @@ import java.util.function.Consumer;
/** /**
* A registry keeping track of all records * A registry keeping track of all records
*/ */
public class DropperArenaRecordsRegistry implements ConfigurationSerializable { public class DropperArenaRecordsRegistry implements ConfigurationSerializable, ArenaRecordsRegistry {
private final UUID arenaId; private final UUID arenaId;
private final @NotNull Set<IntegerRecord> leastDeaths; private final @NotNull Set<IntegerRecord> leastDeaths;
@ -105,7 +106,7 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
* Saves changed records * Saves changed records
*/ */
private void save() { private void save() {
Dropper.getInstance().getArenaHandler().saveData(this.arenaId); MiniGames.getInstance().getDropperArenaHandler().saveData(this.arenaId);
} }
/** /**

View File

@ -1,6 +1,6 @@
package net.knarcraft.dropper.arena; package net.knarcraft.dropper.arena.dropper;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.config.DropperConfiguration; import net.knarcraft.dropper.config.DropperConfiguration;
import net.knarcraft.dropper.property.RecordResult; import net.knarcraft.dropper.property.RecordResult;
import net.knarcraft.dropper.util.PlayerTeleporter; import net.knarcraft.dropper.util.PlayerTeleporter;
@ -17,10 +17,10 @@ public class DropperArenaSession {
private final @NotNull DropperArena arena; private final @NotNull DropperArena arena;
private final @NotNull Player player; private final @NotNull Player player;
private final @NotNull ArenaGameMode gameMode; private final @NotNull DropperArenaGameMode gameMode;
private int deaths; private int deaths;
private final long startTime; private final long startTime;
private final PlayerEntryState entryState; private final DropperPlayerEntryState entryState;
/** /**
* Instantiates a new dropper arena session * Instantiates a new dropper arena session
@ -30,17 +30,17 @@ public class DropperArenaSession {
* @param gameMode <p>The game-mode</p> * @param gameMode <p>The game-mode</p>
*/ */
public DropperArenaSession(@NotNull DropperArena dropperArena, @NotNull Player player, public DropperArenaSession(@NotNull DropperArena dropperArena, @NotNull Player player,
@NotNull ArenaGameMode gameMode) { @NotNull DropperArenaGameMode gameMode) {
this.arena = dropperArena; this.arena = dropperArena;
this.player = player; this.player = player;
this.gameMode = gameMode; this.gameMode = gameMode;
this.deaths = 0; this.deaths = 0;
this.startTime = System.currentTimeMillis(); this.startTime = System.currentTimeMillis();
DropperConfiguration configuration = Dropper.getInstance().getDropperConfiguration(); DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration();
boolean makeInvisible = configuration.makePlayersInvisible(); boolean makeInvisible = configuration.makePlayersInvisible();
boolean disableCollision = configuration.disableHitCollision(); boolean disableCollision = configuration.disableHitCollision();
this.entryState = new PlayerEntryState(player, gameMode, makeInvisible, disableCollision); this.entryState = new DropperPlayerEntryState(player, gameMode, makeInvisible, disableCollision);
// Make the player fly to improve mobility in the air // Make the player fly to improve mobility in the air
this.entryState.setArenaState(this.arena.getPlayerHorizontalVelocity()); this.entryState.setArenaState(this.arena.getPlayerHorizontalVelocity());
} }
@ -50,7 +50,7 @@ public class DropperArenaSession {
* *
* @return <p>The game-mode for this session</p> * @return <p>The game-mode for this session</p>
*/ */
public @NotNull ArenaGameMode getGameMode() { public @NotNull DropperArenaGameMode getGameMode() {
return this.gameMode; return this.gameMode;
} }
@ -59,7 +59,7 @@ public class DropperArenaSession {
* *
* @return <p>The player's entry state</p> * @return <p>The player's entry state</p>
*/ */
public @NotNull PlayerEntryState getEntryState() { public @NotNull DropperPlayerEntryState getEntryState() {
return this.entryState; return this.entryState;
} }
@ -71,15 +71,15 @@ public class DropperArenaSession {
stopSession(); stopSession();
// Check for, and display, records // Check for, and display, records
Dropper dropper = Dropper.getInstance(); MiniGames miniGames = MiniGames.getInstance();
boolean ignore = dropper.getDropperConfiguration().ignoreRecordsUntilGroupBeatenOnce(); boolean ignore = miniGames.getDropperConfiguration().ignoreRecordsUntilGroupBeatenOnce();
DropperArenaGroup group = dropper.getArenaHandler().getGroup(this.arena.getArenaId()); DropperArenaGroup group = miniGames.getDropperArenaHandler().getGroup(this.arena.getArenaId());
if (!ignore || group == null || group.hasBeatenAll(this.gameMode, this.player)) { if (!ignore || group == null || group.hasBeatenAll(this.gameMode, this.player)) {
registerRecord(); registerRecord();
} }
// Mark the arena as cleared // 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 cleared the arena!");
} }
this.player.sendMessage("You won!"); this.player.sendMessage("You won!");
@ -107,9 +107,9 @@ public class DropperArenaSession {
*/ */
private void removeSession() { private void removeSession() {
// Remove this session for game sessions to stop listeners from fiddling more with the player // 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) { if (!removedSession) {
Dropper.log(Level.SEVERE, "Unable to remove dropper arena session for " + player.getName() + ". " + MiniGames.log(Level.SEVERE, "Unable to remove dropper arena session for " + player.getName() + ". " +
"This will have unintended consequences."); "This will have unintended consequences.");
} }
} }

View File

@ -1,11 +1,11 @@
package net.knarcraft.dropper.arena; package net.knarcraft.dropper.arena.dropper;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
* A representation of each key used for storing arena data * A representation of each key used for storing arena data
*/ */
public enum ArenaStorageKey { public enum DropperArenaStorageKey {
/** /**
* The key for an arena's id * The key for an arena's id
@ -55,7 +55,7 @@ public enum ArenaStorageKey {
* *
* @param key <p>The string path of the configuration key this value represents.</p> * @param key <p>The string path of the configuration key this value represents.</p>
*/ */
ArenaStorageKey(@NotNull String key) { DropperArenaStorageKey(@NotNull String key) {
this.key = key; this.key = key;
} }

View File

@ -1,4 +1,4 @@
package net.knarcraft.dropper.arena; package net.knarcraft.dropper.arena.dropper;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
@ -10,7 +10,7 @@ import org.jetbrains.annotations.NotNull;
/** /**
* The state of a player before entering a dropper arena * The state of a player before entering a dropper arena
*/ */
public class PlayerEntryState { public class DropperPlayerEntryState {
private final Player player; private final Player player;
private final Location entryLocation; private final Location entryLocation;
@ -23,14 +23,14 @@ public class PlayerEntryState {
private final boolean originalCollideAble; private final boolean originalCollideAble;
private final boolean makePlayerInvisible; private final boolean makePlayerInvisible;
private final boolean disableHitCollision; private final boolean disableHitCollision;
private final ArenaGameMode arenaGameMode; private final DropperArenaGameMode arenaGameMode;
/** /**
* Instantiates a new player state * Instantiates a new player state
* *
* @param player <p>The player whose state should be stored</p> * @param player <p>The player whose state should be stored</p>
*/ */
public PlayerEntryState(@NotNull Player player, @NotNull ArenaGameMode arenaGameMode, boolean makePlayerInvisible, public DropperPlayerEntryState(@NotNull Player player, @NotNull DropperArenaGameMode arenaGameMode, boolean makePlayerInvisible,
boolean disableHitCollision) { boolean disableHitCollision) {
this.player = player; this.player = player;
this.entryLocation = player.getLocation().clone(); this.entryLocation = player.getLocation().clone();
@ -65,7 +65,7 @@ public class PlayerEntryState {
} }
// If playing on the inverted game-mode, negate the horizontal velocity to swap the controls // If playing on the inverted game-mode, negate the horizontal velocity to swap the controls
if (arenaGameMode == ArenaGameMode.INVERTED) { if (arenaGameMode == DropperArenaGameMode.INVERTED) {
this.player.setFlySpeed(-horizontalVelocity); this.player.setFlySpeed(-horizontalVelocity);
} else { } else {
this.player.setFlySpeed(horizontalVelocity); this.player.setFlySpeed(horizontalVelocity);

View File

@ -0,0 +1,284 @@
package net.knarcraft.dropper.arena.parkour;
import net.knarcraft.dropper.util.StringSanitizer;
import org.bukkit.Location;
import org.bukkit.Material;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static net.knarcraft.dropper.util.InputValidationHelper.isInvalid;
/**
* A representation of one dropper arena
*/
public class ParkourArena {
/**
* An unique and persistent identifier for this arena
*/
private final 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 dropper 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 checkpoints for this arena. Entering a checkpoint overrides the player's spawn location.
*/
private @Nullable List<Location> checkpoints;
/**
* The arena data for this arena
*/
private final ParkourArenaData parkourArenaData;
private final ParkourArenaHandler parkourArenaHandler;
/**
* Instantiates a new dropper arena
*
* @param arenaId <p>The id of the arena</p>
* @param arenaName <p>The name of the arena</p>
* @param spawnLocation <p>The location players spawn in when entering the arena</p>
* @param exitLocation <p>The location the players are teleported to when exiting the arena, or null</p>
* @param winBlockType <p>The material of the block players have to hit to win this dropper arena</p>
* @param parkourArenaData <p>The arena data keeping track of which players have done what in this arena</p>
* @param arenaHandler <p>The arena handler used for saving any changes</p>
*/
public ParkourArena(@NotNull UUID arenaId, @NotNull String arenaName, @NotNull Location spawnLocation,
@Nullable Location exitLocation, @NotNull Material winBlockType,
@NotNull ParkourArenaData parkourArenaData, @NotNull ParkourArenaHandler arenaHandler) {
this.arenaId = arenaId;
this.arenaName = arenaName;
this.spawnLocation = spawnLocation;
this.exitLocation = exitLocation;
this.winBlockType = winBlockType;
this.parkourArenaData = parkourArenaData;
this.parkourArenaHandler = arenaHandler;
}
/**
* Instantiates a new dropper arena
*
* <p>Note that this minimal constructor can be used to quickly create a new dropper arena at the player's given
* location, simply by them giving an arena name.</p>
*
* @param arenaName <p>The name of the arena</p>
* @param spawnLocation <p>The location players spawn in when entering the arena</p>
* @param arenaHandler <p>The arena handler used for saving any changes</p>
*/
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<ParkourArenaGameMode, ParkourArenaRecordsRegistry> 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.parkourArenaHandler = arenaHandler;
}
/**
* Gets this arena's data
*
* @return <p>This arena's data</p>
*/
public @NotNull ParkourArenaData getData() {
return this.parkourArenaData;
}
/**
* Gets the id of this arena
*
* @return <p>This arena's identifier</p>
*/
public @NotNull UUID getArenaId() {
return this.arenaId;
}
/**
* Gets the name of this arena
*
* @return <p>The name of this arena</p>
*/
public @NotNull String getArenaName() {
return this.arenaName;
}
/**
* Gets this arena's spawn location
*
* <p>The spawn location is the location every player starts from when entering the dropper.</p>
*
* @return <p>This arena's spawn location.</p>
*/
public @NotNull Location getSpawnLocation() {
return this.spawnLocation;
}
/**
* Gets this arena's exit location
*
* @return <p>This arena's exit location, or null if no such location is set.</p>
*/
public @Nullable Location getExitLocation() {
return this.exitLocation;
}
/**
* Gets the type of block a player has to hit to win this arena
*
* @return <p>The kind of block players must hit</p>
*/
public @NotNull Material getWinBlockType() {
return this.winBlockType;
}
/**
* The location a player has to reach to win this arena
*
* <p></p>
*
* @return <p>The win trigger's location</p>
*/
public @Nullable Location getWinLocation() {
return this.winLocation != null ? this.winLocation.clone() : null;
}
/**
* Gets this arena's sanitized name
*
* @return <p>This arena's sanitized name</p>
*/
public @NotNull String getArenaNameSanitized() {
return StringSanitizer.sanitizeArenaName(this.getArenaName());
}
/**
* Sets the spawn location for this arena
*
* @param newLocation <p>The new spawn location</p>
* @return <p>True if successfully updated</p>
*/
public boolean setSpawnLocation(@NotNull Location newLocation) {
if (isInvalid(newLocation)) {
return false;
} else {
this.spawnLocation = newLocation;
parkourArenaHandler.saveArenas();
return true;
}
}
/**
* Sets the exit location for this arena
*
* @param newLocation <p>The new exit location</p>
* @return <p>True if successfully updated</p>
*/
public boolean setExitLocation(@NotNull Location newLocation) {
if (isInvalid(newLocation)) {
return false;
} else {
this.exitLocation = newLocation;
parkourArenaHandler.saveArenas();
return true;
}
}
/**
* Sets the name of this arena
*
* @param arenaName <p>The new name</p>
* @return <p>True if successfully updated</p>
*/
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
parkourArenaHandler.updateLookupName(oldName, this.getArenaNameSanitized());
parkourArenaHandler.saveArenas();
return true;
} else {
return false;
}
}
/**
* Sets the material of the win block type
*
* <p>The win block type is the type of block a player must hit to win in this arena</p>
*
* @param material <p>The material to set for the win block type</p>
* @return <p>True if successfully updated</p>
*/
public boolean setWinBlockType(@NotNull Material material) {
if (material.isAir() || !material.isBlock()) {
return false;
} else {
this.winBlockType = material;
parkourArenaHandler.saveArenas();
return true;
}
}
/**
* Sets the location players need to reach to win this arena
*
* @param newLocation <p>The location players have to reach</p>
* @return <p>True if successfully changed</p>
*/
public boolean setWinLocation(@NotNull Location newLocation) {
if (isInvalid(newLocation)) {
return false;
} else {
this.exitLocation = newLocation;
parkourArenaHandler.saveArenas();
return true;
}
}
@Override
public boolean equals(Object other) {
if (!(other instanceof ParkourArena otherArena)) {
return false;
}
return this.getArenaNameSanitized().equals(otherArena.getArenaNameSanitized());
}
}

View File

@ -1,6 +1,6 @@
package net.knarcraft.dropper.arena; package net.knarcraft.dropper.arena.parkour;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.container.SerializableUUID; import net.knarcraft.dropper.container.SerializableUUID;
import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -17,22 +17,22 @@ import java.util.UUID;
* *
* @param arenaId <p>The id of the arena this data belongs to</p> * @param arenaId <p>The id of the arena this data belongs to</p>
* @param recordRegistries <p>The records belonging to the arena</p> * @param recordRegistries <p>The records belonging to the arena</p>
* @param playersCompleted <p>A list of all player that have completed this arena</p> * @param playersCompleted <p>A set of all player that have completed this arena</p>
*/ */
public record DropperArenaData(@NotNull UUID arenaId, public record ParkourArenaData(@NotNull UUID arenaId,
@NotNull Map<ArenaGameMode, DropperArenaRecordsRegistry> recordRegistries, @NotNull Map<ParkourArenaGameMode, ParkourArenaRecordsRegistry> recordRegistries,
@NotNull Map<ArenaGameMode, Set<UUID>> playersCompleted) implements ConfigurationSerializable { @NotNull Map<ParkourArenaGameMode, Set<UUID>> playersCompleted) implements ConfigurationSerializable {
/** /**
* Instantiates a new dropper arena data object * Instantiates a new dropper arena data object
* *
* @param arenaId <p>The id of the arena this data belongs to</p> * @param arenaId <p>The id of the arena this data belongs to</p>
* @param recordRegistries <p>The registries of this arena's records</p> * @param recordRegistries <p>The registry of this arena's records</p>
* @param playersCompleted <p>The set of the players that have cleared this arena for each game-mode</p> * @param playersCompleted <p>The set of the players that have cleared this arena</p>
*/ */
public DropperArenaData(@NotNull UUID arenaId, public ParkourArenaData(@NotNull UUID arenaId,
@NotNull Map<ArenaGameMode, DropperArenaRecordsRegistry> recordRegistries, @NotNull Map<ParkourArenaGameMode, ParkourArenaRecordsRegistry> recordRegistries,
@NotNull Map<ArenaGameMode, Set<UUID>> playersCompleted) { @NotNull Map<ParkourArenaGameMode, Set<UUID>> playersCompleted) {
this.arenaId = arenaId; this.arenaId = arenaId;
this.recordRegistries = recordRegistries; this.recordRegistries = recordRegistries;
this.playersCompleted = new HashMap<>(playersCompleted); this.playersCompleted = new HashMap<>(playersCompleted);
@ -41,20 +41,20 @@ public record DropperArenaData(@NotNull UUID arenaId,
/** /**
* Gets whether the given player has cleared this arena * Gets whether the given player has cleared this arena
* *
* @param arenaGameMode <p>The game-mode to check for</p>
* @param player <p>The player to check</p> * @param player <p>The player to check</p>
* @return <p>True if the player has cleared the arena this data belongs to</p> * @return <p>True if the player has cleared the arena this data belongs to</p>
*/ */
public boolean hasNotCompleted(@NotNull ArenaGameMode arenaGameMode, @NotNull Player player) { public boolean hasNotCompleted(@NotNull ParkourArenaGameMode arenaGameMode, @NotNull Player player) {
return !this.playersCompleted.getOrDefault(arenaGameMode, new HashSet<>()).contains(player.getUniqueId()); return !this.playersCompleted.getOrDefault(arenaGameMode, new HashSet<>()).contains(player.getUniqueId());
} }
/** /**
* Registers the given player as having completed this arena * Registers the given player as having completed this arena
* *
* @param arenaGameMode <p>The game-mode the player completed</p>
* @param player <p>The player that completed this data's arena</p> * @param player <p>The player that completed this data's arena</p>
*/ */
public boolean addCompleted(@NotNull ArenaGameMode arenaGameMode, @NotNull Player player) { public boolean addCompleted(@NotNull ParkourArenaGameMode arenaGameMode, @NotNull Player player) {
// Make sure to add an empty set to prevent a NullPointerException // Make sure to add an empty set to prevent a NullPointerException
if (!this.playersCompleted.containsKey(arenaGameMode)) { if (!this.playersCompleted.containsKey(arenaGameMode)) {
this.playersCompleted.put(arenaGameMode, new HashSet<>()); this.playersCompleted.put(arenaGameMode, new HashSet<>());
@ -63,7 +63,7 @@ public record DropperArenaData(@NotNull UUID arenaId,
boolean added = this.playersCompleted.get(arenaGameMode).add(player.getUniqueId()); boolean added = this.playersCompleted.get(arenaGameMode).add(player.getUniqueId());
// Persistently save the completion // Persistently save the completion
if (added) { if (added) {
Dropper.getInstance().getArenaHandler().saveData(this.arenaId); MiniGames.getInstance().getDropperArenaHandler().saveData(this.arenaId);
} }
return added; return added;
} }
@ -76,8 +76,8 @@ public record DropperArenaData(@NotNull UUID arenaId,
data.put("recordsRegistry", this.recordRegistries); data.put("recordsRegistry", this.recordRegistries);
// Convert normal UUIDs to serializable UUIDs // Convert normal UUIDs to serializable UUIDs
Map<ArenaGameMode, Set<SerializableUUID>> serializablePlayersCompleted = new HashMap<>(); Map<ParkourArenaGameMode, Set<SerializableUUID>> serializablePlayersCompleted = new HashMap<>();
for (ArenaGameMode arenaGameMode : this.playersCompleted.keySet()) { for (ParkourArenaGameMode arenaGameMode : this.playersCompleted.keySet()) {
Set<SerializableUUID> playersCompleted = new HashSet<>(); Set<SerializableUUID> playersCompleted = new HashSet<>();
for (UUID playerCompleted : this.playersCompleted.get(arenaGameMode)) { for (UUID playerCompleted : this.playersCompleted.get(arenaGameMode)) {
playersCompleted.add(new SerializableUUID(playerCompleted)); playersCompleted.add(new SerializableUUID(playerCompleted));
@ -95,12 +95,12 @@ public record DropperArenaData(@NotNull UUID arenaId,
* @return <p>The deserialized dropper arena data</p> * @return <p>The deserialized dropper arena data</p>
*/ */
@SuppressWarnings({"unused", "unchecked"}) @SuppressWarnings({"unused", "unchecked"})
public static @NotNull DropperArenaData deserialize(@NotNull Map<String, Object> data) { public static @NotNull ParkourArenaData deserialize(@NotNull Map<String, Object> data) {
SerializableUUID serializableUUID = (SerializableUUID) data.get("arenaId"); SerializableUUID serializableUUID = (SerializableUUID) data.get("arenaId");
Map<ArenaGameMode, DropperArenaRecordsRegistry> recordsRegistry = Map<ParkourArenaGameMode, ParkourArenaRecordsRegistry> recordsRegistry =
(Map<ArenaGameMode, DropperArenaRecordsRegistry>) data.get("recordsRegistry"); (Map<ParkourArenaGameMode, ParkourArenaRecordsRegistry>) data.get("recordsRegistry");
Map<ArenaGameMode, Set<SerializableUUID>> playersCompletedData = Map<ParkourArenaGameMode, Set<SerializableUUID>> playersCompletedData =
(Map<ArenaGameMode, Set<SerializableUUID>>) data.get("playersCompleted"); (Map<ParkourArenaGameMode, Set<SerializableUUID>>) data.get("playersCompleted");
if (recordsRegistry == null) { if (recordsRegistry == null) {
recordsRegistry = new HashMap<>(); recordsRegistry = new HashMap<>();
@ -109,8 +109,8 @@ public record DropperArenaData(@NotNull UUID arenaId,
} }
// Convert the serializable UUIDs to normal UUIDs // Convert the serializable UUIDs to normal UUIDs
Map<ArenaGameMode, Set<UUID>> allPlayersCompleted = new HashMap<>(); Map<ParkourArenaGameMode, Set<UUID>> allPlayersCompleted = new HashMap<>();
for (ArenaGameMode arenaGameMode : playersCompletedData.keySet()) { for (ParkourArenaGameMode arenaGameMode : playersCompletedData.keySet()) {
Set<UUID> playersCompleted = new HashSet<>(); Set<UUID> playersCompleted = new HashSet<>();
for (SerializableUUID completedId : playersCompletedData.get(arenaGameMode)) { for (SerializableUUID completedId : playersCompletedData.get(arenaGameMode)) {
playersCompleted.add(completedId.uuid()); playersCompleted.add(completedId.uuid());
@ -118,10 +118,10 @@ public record DropperArenaData(@NotNull UUID arenaId,
allPlayersCompleted.put(arenaGameMode, playersCompleted); allPlayersCompleted.put(arenaGameMode, playersCompleted);
if (!recordsRegistry.containsKey(arenaGameMode) || recordsRegistry.get(arenaGameMode) == null) { if (!recordsRegistry.containsKey(arenaGameMode) || recordsRegistry.get(arenaGameMode) == null) {
recordsRegistry.put(arenaGameMode, new DropperArenaRecordsRegistry(serializableUUID.uuid())); recordsRegistry.put(arenaGameMode, new ParkourArenaRecordsRegistry(serializableUUID.uuid()));
} }
} }
return new DropperArenaData(serializableUUID.uuid(), recordsRegistry, allPlayersCompleted); return new ParkourArenaData(serializableUUID.uuid(), recordsRegistry, allPlayersCompleted);
} }
} }

View File

@ -0,0 +1,50 @@
package net.knarcraft.dropper.arena.parkour;
import net.knarcraft.dropper.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 <p>The game-mode string to match</p>
* @return <p>The specified arena game-mode</p>
*/
public static @NotNull ParkourArenaGameMode matchGamemode(@NotNull String gameMode) {
return ParkourArenaGameMode.DEFAULT;
}
@NotNull
@Override
public Map<String, Object> serialize() {
Map<String, Object> data = new HashMap<>();
data.put("name", this.name());
return data;
}
/**
* Deserializes the arena game-mode specified by the given data
*
* @param data <p>The data to deserialize</p>
* @return <p>The deserialized arena game-mode</p>
*/
@SuppressWarnings("unused")
public static ParkourArenaGameMode deserialize(Map<String, Object> data) {
return ParkourArenaGameMode.valueOf((String) data.get("name"));
}
}

View File

@ -0,0 +1,122 @@
package net.knarcraft.dropper.arena.parkour;
import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.ArenaGroup;
import net.knarcraft.dropper.container.SerializableUUID;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
/**
* A sorted group of arenas that must be completed in sequence
*/
public class ParkourArenaGroup extends ArenaGroup {
/**
* Instantiates a new dropper arena group
*
* @param groupName <p>The name of this group</p>
*/
public ParkourArenaGroup(@NotNull String groupName) {
super(groupName);
}
/**
* Instantiates a new dropper arena group
*
* @param groupId <p>The unique id of this group</p>
* @param groupName <p>The name of this group</p>
* @param arenas <p>The arenas in this group</p>
*/
private ParkourArenaGroup(@NotNull UUID groupId, @NotNull String groupName, @NotNull List<UUID> arenas) {
super(groupId, groupName, arenas);
}
/**
* Checks whether the given player has beaten all arenas in this group on the given game-mode
*
* @param gameMode <p>The game-mode to check</p>
* @param player <p>The player to check</p>
* @return <p>True if the player has beaten all arenas, false otherwise</p>
*/
public boolean hasBeatenAll(ParkourArenaGameMode gameMode, Player player) {
ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
for (UUID anArenaId : this.getArenas()) {
ParkourArena dropperArena = arenaHandler.getArena(anArenaId);
if (dropperArena == null) {
// The arena would only be null if the arena has been deleted, but not removed from this group
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)) {
return false;
}
}
return true;
}
/**
* Gets whether the given player can play the given arena part of this group, on the given game-mode
*
* @param gameMode <p>The game-mode the player is trying to play</p>
* @param player <p>The player to check</p>
* @param arenaId <p>The id of the arena in this group to check</p>
* @return <p>True if the player is allowed to play the arena</p>
* @throws IllegalArgumentException <p>If checking an arena not in this group</p>
*/
public boolean canPlay(ParkourArenaGameMode 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!");
}
ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
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;
}
ParkourArena parkourArena = arenaHandler.getArena(anArenaId);
if (parkourArena == null) {
// The arena would only be null if the arena has been deleted, but not removed from this group
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 (parkourArena.getData().hasNotCompleted(gameMode, player)) {
return false;
}
}
return false;
}
/**
* Deserializes the given data
*
* @param data <p>The data to deserialize</p>
* @return <p>The deserialized arena group</p>
*/
@SuppressWarnings({"unused", "unchecked"})
public static @NotNull ParkourArenaGroup deserialize(@NotNull Map<String, Object> data) {
UUID id = ((SerializableUUID) data.get("groupId")).uuid();
String name = (String) data.get("groupName");
List<SerializableUUID> serializableArenas = (List<SerializableUUID>) data.get("arenas");
List<UUID> arenas = new ArrayList<>();
for (SerializableUUID arenaId : serializableArenas) {
arenas.add(arenaId.uuid());
}
return new ParkourArenaGroup(id, name, arenas);
}
}

View File

@ -0,0 +1,249 @@
package net.knarcraft.dropper.arena.parkour;
import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.util.ArenaStorageHelper;
import net.knarcraft.dropper.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;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
/**
* A handler that keeps track of all dropper arenas
*/
public class ParkourArenaHandler {
private Map<UUID, ParkourArena> arenas = new HashMap<>();
private Map<UUID, ParkourArenaGroup> arenaGroups = new HashMap<>();
private Map<String, UUID> arenaNameLookup = new HashMap<>();
/**
* Gets all arenas that are within a group
*
* @return <p>All arenas in a group</p>
*/
public @NotNull Set<ParkourArena> getArenasInAGroup() {
Set<ParkourArena> arenas = new HashSet<>();
for (UUID arenaId : arenaGroups.keySet()) {
arenas.add(this.arenas.get(arenaId));
}
return arenas;
}
/**
* Gets a copy of all dropper groups
*
* @return <p>All dropper groups</p>
*/
public Set<ParkourArenaGroup> getAllGroups() {
return new HashSet<>(arenaGroups.values());
}
/**
* Gets the group the given arena belongs to
*
* @param arenaId <p>The id of the arena to get the group of</p>
* @return <p>The group the arena belongs to, or null if not in a group</p>
*/
public @Nullable ParkourArenaGroup getGroup(@NotNull UUID arenaId) {
return this.arenaGroups.get(arenaId);
}
/**
* Sets the group for the given arena
*
* @param arenaId <p>The id of the arena to change</p>
* @param arenaGroup <p>The group to add the arena to, or null to remove the current group</p>
*/
public void setGroup(@NotNull UUID arenaId, @Nullable ParkourArenaGroup arenaGroup) {
if (arenaGroup == null) {
// No need to remove something non-existing
if (!this.arenaGroups.containsKey(arenaId)) {
return;
}
// Remove the existing group
ParkourArenaGroup oldGroup = this.arenaGroups.remove(arenaId);
oldGroup.removeArena(arenaId);
} else {
// Make sure to remove the arena from the old group's internal tracking
if (this.arenaGroups.containsKey(arenaId)) {
this.arenaGroups.remove(arenaId).removeArena(arenaId);
}
this.arenaGroups.put(arenaId, arenaGroup);
arenaGroup.addArena(arenaId);
}
saveGroups();
}
/**
* Gets the dropper arena group with the given name
*
* @param groupName <p>The name of the group to get</p>
* @return <p>The group, or null if not found</p>
*/
public @Nullable ParkourArenaGroup getGroup(String groupName) {
String sanitized = StringSanitizer.sanitizeArenaName(groupName);
for (ParkourArenaGroup arenaGroup : this.arenaGroups.values()) {
if (arenaGroup.getGroupNameSanitized().equals(sanitized)) {
return arenaGroup;
}
}
return null;
}
/**
* Replaces an arena's lookup name
*
* @param oldName <p>The arena's old sanitized lookup name</p>
* @param newName <p>The arena's new sanitized lookup name</p>
*/
public void updateLookupName(@NotNull String oldName, @NotNull String newName) {
UUID arenaId = this.arenaNameLookup.remove(oldName);
if (arenaId != null) {
this.arenaNameLookup.put(newName, arenaId);
}
}
/**
* Adds a new arena
*
* @param arena <p>The arena to add</p>
*/
public void addArena(@NotNull ParkourArena arena) {
this.arenas.put(arena.getArenaId(), arena);
this.arenaNameLookup.put(arena.getArenaNameSanitized(), arena.getArenaId());
this.saveArenas();
}
/**
* Gets the arena with the given id
*
* @param arenaId <p>The id of the arena to get</p>
* @return <p>The arena, or null if no arena could be found</p>
*/
public @Nullable ParkourArena getArena(@NotNull UUID arenaId) {
return this.arenas.get(arenaId);
}
/**
* Gets the arena with the given name
*
* @param arenaName <p>The arena to get</p>
* @return <p>The arena with the given name, or null if not found</p>
*/
public @Nullable ParkourArena getArena(@NotNull String arenaName) {
return this.arenas.get(this.arenaNameLookup.get(StringSanitizer.sanitizeArenaName(arenaName)));
}
/**
* Gets all known arenas
*
* @return <p>All known arenas</p>
*/
public @NotNull Map<UUID, ParkourArena> getArenas() {
return new HashMap<>(this.arenas);
}
/**
* Removes the given arena
*
* @param arena <p>The arena to remove</p>
*/
public void removeArena(@NotNull ParkourArena arena) {
UUID arenaId = arena.getArenaId();
MiniGames.getInstance().getParkourArenaPlayerRegistry().removeForArena(arena);
this.arenas.remove(arenaId);
this.arenaNameLookup.remove(arena.getArenaNameSanitized());
this.arenaGroups.remove(arenaId);
if (!ArenaStorageHelper.removeDropperArenaData(arenaId)) {
MiniGames.log(Level.WARNING, "Unable to remove dropper arena data file " + arenaId + ".yml. " +
"You must remove it manually!");
}
this.saveArenas();
}
/**
* Stores the data for the given arena
*
* @param arenaId <p>The id of the arena whose data should be saved</p>
*/
public void saveData(UUID arenaId) {
try {
ArenaStorageHelper.saveParkourArenaData(this.arenas.get(arenaId).getData());
} catch (IOException e) {
MiniGames.log(Level.SEVERE, "Unable to save arena data! Data loss can occur!");
MiniGames.log(Level.SEVERE, e.getMessage());
}
}
/**
* Saves all current dropper groups to disk
*/
public void saveGroups() {
try {
ArenaStorageHelper.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());
}
}
/**
* Loads all arenas and groups from disk
*/
public void load() {
loadArenas();
loadGroups();
}
/**
* Loads all dropper groups from disk
*/
private void loadGroups() {
Set<ParkourArenaGroup> arenaGroups = ArenaStorageHelper.loadParkourArenaGroups();
Map<UUID, ParkourArenaGroup> arenaGroupMap = new HashMap<>();
for (ParkourArenaGroup arenaGroup : arenaGroups) {
for (UUID arenaId : arenaGroup.getArenas()) {
arenaGroupMap.put(arenaId, arenaGroup);
}
}
this.arenaGroups = arenaGroupMap;
}
/**
* Saves all current arenas to disk
*/
public void saveArenas() {
try {
ArenaStorageHelper.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());
}
}
/**
* Loads all arenas from disk
*/
private void loadArenas() {
this.arenas = ArenaStorageHelper.loadParkourArenas();
// Save a map from arena name to arena id for improved performance
this.arenaNameLookup = new HashMap<>();
for (Map.Entry<UUID, ParkourArena> arena : this.arenas.entrySet()) {
String sanitizedName = arena.getValue().getArenaNameSanitized();
this.arenaNameLookup.put(sanitizedName, arena.getKey());
}
}
}

View File

@ -0,0 +1,61 @@
package net.knarcraft.dropper.arena.parkour;
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 {
private final Map<UUID, ParkourArenaSession> arenaPlayers = new HashMap<>();
/**
* Registers that the given player has started playing the given parkour arena session
*
* @param playerId <p>The id of the player that started playing</p>
* @param arena <p>The arena session to register</p>
*/
public void registerPlayer(@NotNull UUID playerId, @NotNull ParkourArenaSession arena) {
this.arenaPlayers.put(playerId, arena);
}
/**
* Removes this player from players currently playing
*
* @param playerId <p>The id of the player to remove</p>
*/
public boolean removePlayer(@NotNull UUID playerId) {
return this.arenaPlayers.remove(playerId) != null;
}
/**
* Gets the player's active parkour arena session
*
* @param playerId <p>The id of the player to get arena for</p>
* @return <p>The player's active arena session, or null if not currently playing</p>
*/
public @Nullable ParkourArenaSession getArenaSession(@NotNull UUID playerId) {
return this.arenaPlayers.getOrDefault(playerId, null);
}
/**
* Removes all active sessions for the given arena
*
* @param arena <p>The arena to remove sessions for</p>
*/
public void removeForArena(ParkourArena arena) {
for (Map.Entry<UUID, ParkourArenaSession> entry : this.arenaPlayers.entrySet()) {
if (entry.getValue().getArena() == arena) {
// Kick the player gracefully
entry.getValue().triggerQuit(false);
this.arenaPlayers.remove(entry.getKey());
}
}
}
}

View File

@ -0,0 +1,198 @@
package net.knarcraft.dropper.arena.parkour;
import net.knarcraft.dropper.MiniGames;
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 org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.jetbrains.annotations.NotNull;
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
*/
public class ParkourArenaRecordsRegistry implements ConfigurationSerializable {
private final UUID arenaId;
private final @NotNull Set<IntegerRecord> leastDeaths;
private final @NotNull Set<LongRecord> shortestTimeMilliSeconds;
/**
* Instantiates a new empty records registry
*/
public ParkourArenaRecordsRegistry(@NotNull UUID arenaId) {
this.arenaId = arenaId;
this.leastDeaths = new HashSet<>();
this.shortestTimeMilliSeconds = new HashSet<>();
}
/**
* Instantiates a new records registry
*
* @param leastDeaths <p>The existing least death records to use</p>
* @param shortestTimeMilliSeconds <p>The existing leash time records to use</p>
*/
private ParkourArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Set<IntegerRecord> leastDeaths,
@NotNull Set<LongRecord> shortestTimeMilliSeconds) {
this.arenaId = arenaId;
this.leastDeaths = new HashSet<>(leastDeaths);
this.shortestTimeMilliSeconds = new HashSet<>(shortestTimeMilliSeconds);
}
/**
* Gets all existing death records
*
* @return <p>Existing death records</p>
*/
public Set<SummableArenaRecord<Integer>> getLeastDeathsRecords() {
return new HashSet<>(this.leastDeaths);
}
/**
* Gets all existing time records
*
* @return <p>Existing time records</p>
*/
public Set<SummableArenaRecord<Long>> getShortestTimeMilliSecondsRecords() {
return new HashSet<>(this.shortestTimeMilliSeconds);
}
/**
* Registers a new deaths-record
*
* @param playerId <p>The id of the player that performed the records</p>
* @param deaths <p>The number of deaths suffered before the player finished the arena</p>
* @return <p>The result explaining what type of record was achieved</p>
*/
public @NotNull RecordResult registerDeathRecord(@NotNull UUID playerId, int deaths) {
Consumer<Integer> consumer = (value) -> {
leastDeaths.removeIf((item) -> item.getUserId().equals(playerId));
leastDeaths.add(new IntegerRecord(playerId, value));
};
Set<ArenaRecord<Integer>> asInt = new HashSet<>(leastDeaths);
return registerRecord(asInt, consumer, playerId, deaths);
}
/**
* Registers a new time-record
*
* @param playerId <p>The id of the player that performed the records</p>
* @param milliseconds <p>The number of milliseconds it took the player to finish the dropper arena</p>
* @return <p>The result explaining what type of record was achieved</p>
*/
public @NotNull RecordResult registerTimeRecord(@NotNull UUID playerId, long milliseconds) {
Consumer<Long> consumer = (value) -> {
shortestTimeMilliSeconds.removeIf((item) -> item.getUserId().equals(playerId));
shortestTimeMilliSeconds.add(new LongRecord(playerId, value));
};
Set<ArenaRecord<Long>> asLong = new HashSet<>(shortestTimeMilliSeconds);
return registerRecord(asLong, consumer, playerId, milliseconds);
}
/**
* Saves changed records
*/
private void save() {
MiniGames.getInstance().getDropperArenaHandler().saveData(this.arenaId);
}
/**
* Registers a new record if applicable
*
* @param existingRecords <p>The map of existing records to use</p>
* @param recordSetter <p>The consumer used to set a new record</p>
* @param playerId <p>The id of the player that potentially achieved a record</p>
* @param amount <p>The amount of whatever the player achieved</p>
* @return <p>The result of the player's record attempt</p>
*/
private <T extends Comparable<T>> @NotNull RecordResult registerRecord(@NotNull Set<ArenaRecord<T>> existingRecords,
@NotNull Consumer<T> recordSetter,
@NotNull UUID playerId, T amount) {
RecordResult result;
if (existingRecords.stream().allMatch((entry) -> amount.compareTo(entry.getRecord()) < 0)) {
// If the given value is less than all other values, that's a world record!
result = RecordResult.WORLD_RECORD;
recordSetter.accept(amount);
save();
return result;
}
ArenaRecord<T> playerRecord = getRecord(existingRecords, playerId);
if (playerRecord != null && amount.compareTo(playerRecord.getRecord()) < 0) {
// If the given value is less than the player's previous value, that's a personal best!
result = RecordResult.PERSONAL_BEST;
recordSetter.accept(amount);
save();
} else {
// Make sure to save the record if this is the user's first attempt
if (playerRecord == null) {
recordSetter.accept(amount);
save();
}
result = RecordResult.NONE;
}
return result;
}
/**
* Gets the record stored for the given player
*
* @param existingRecords <p>The existing records to look through</p>
* @param playerId <p>The id of the player to look for</p>
* @param <T> <p>The type of the stored record</p>
* @return <p>The record, or null if not found</p>
*/
private <T extends Comparable<T>> @Nullable ArenaRecord<T> getRecord(@NotNull Set<ArenaRecord<T>> existingRecords,
@NotNull UUID playerId) {
AtomicReference<ArenaRecord<T>> record = new AtomicReference<>();
existingRecords.forEach((item) -> {
if (item.getUserId().equals(playerId)) {
record.set(item);
}
});
return record.get();
}
@NotNull
@Override
public Map<String, Object> serialize() {
Map<String, Object> data = new HashMap<>();
data.put("arenaId", new SerializableUUID(this.arenaId));
data.put("leastDeaths", this.leastDeaths);
data.put("shortestTime", this.shortestTimeMilliSeconds);
return data;
}
/**
* Deserializes the given data
*
* @param data <p>The data to deserialize</p>
* @return <p>The deserialized records registry</p>
*/
@SuppressWarnings({"unused", "unchecked"})
public static ParkourArenaRecordsRegistry deserialize(Map<String, Object> data) {
UUID arenaId = ((SerializableUUID) data.get("arenaId")).uuid();
Set<IntegerRecord> leastDeaths =
(Set<IntegerRecord>) data.getOrDefault("leastDeaths", new HashMap<>());
Set<LongRecord> shortestTimeMilliseconds =
(Set<LongRecord>) data.getOrDefault("shortestTime", new HashMap<>());
leastDeaths.removeIf(Objects::isNull);
shortestTimeMilliseconds.removeIf(Objects::isNull);
return new ParkourArenaRecordsRegistry(arenaId, leastDeaths, shortestTimeMilliseconds);
}
}

View File

@ -0,0 +1,193 @@
package net.knarcraft.dropper.arena.parkour;
import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.config.DropperConfiguration;
import net.knarcraft.dropper.property.RecordResult;
import net.knarcraft.dropper.util.PlayerTeleporter;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.logging.Level;
/**
* A representation of a player's current session in a dropper arena
*/
public class ParkourArenaSession {
private final @NotNull ParkourArena arena;
private final @NotNull Player player;
private final @NotNull ParkourArenaGameMode gameMode;
private int deaths;
private final long startTime;
private final ParkourPlayerEntryState entryState;
/**
* Instantiates a new parkour arena session
*
* @param dropperArena <p>The arena that's being played in</p>
* @param player <p>The player playing the arena</p>
* @param gameMode <p>The game-mode</p>
*/
public ParkourArenaSession(@NotNull ParkourArena dropperArena, @NotNull Player player,
@NotNull ParkourArenaGameMode gameMode) {
this.arena = dropperArena;
this.player = player;
this.gameMode = gameMode;
this.deaths = 0;
this.startTime = System.currentTimeMillis();
DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration();
boolean makeInvisible = configuration.makePlayersInvisible();
this.entryState = new ParkourPlayerEntryState(player, makeInvisible);
// Make the player fly to improve mobility in the air
this.entryState.setArenaState();
}
/**
* Gets the state of the player when they joined the session
*
* @return <p>The player's entry state</p>
*/
public @NotNull ParkourPlayerEntryState getEntryState() {
return this.entryState;
}
/**
* 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.getDropperConfiguration().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().addCompleted(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().getDropperArenaPlayerRegistry().removePlayer(player.getUniqueId());
if (!removedSession) {
MiniGames.log(Level.SEVERE, "Unable to remove dropper 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() {
ParkourArenaRecordsRegistry recordsRegistry = this.arena.getData().recordRegistries().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 <p>The result of the record</p>
* @param type <p>The type of record set (time or deaths)</p>
*/
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
PlayerTeleporter.teleportPlayer(this.player, this.arena.getSpawnLocation(), 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 <p>The session's arena</p>
*/
public @NotNull ParkourArena getArena() {
return this.arena;
}
/**
* Gets the player playing in this session
*
* @return <p>This session's player</p>
*/
public @NotNull Player getPlayer() {
return this.player;
}
}

View File

@ -0,0 +1,81 @@
package net.knarcraft.dropper.arena.parkour;
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;
/**
* The state of a player before entering a dropper arena
*/
public class ParkourPlayerEntryState {
private final Player player;
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;
private final boolean makePlayerInvisible;
/**
* Instantiates a new player state
*
* @param player <p>The player whose state should be stored</p>
*/
public ParkourPlayerEntryState(@NotNull Player player, boolean makePlayerInvisible) {
this.player = player;
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();
this.makePlayerInvisible = makePlayerInvisible;
}
/**
* Sets the state of the stored player to the state used by arenas
*/
public void setArenaState() {
this.player.setAllowFlight(false);
this.player.setFlying(false);
this.player.setGameMode(GameMode.ADVENTURE);
this.player.setSwimming(false);
this.player.setCollidable(false);
if (this.makePlayerInvisible) {
this.player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY,
PotionEffect.INFINITE_DURATION, 3));
}
}
/**
* Restores the stored state for the stored player
*/
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);
}
}
/**
* Gets the location the player entered from
*
* @return <p>The location the player entered from</p>
*/
public Location getEntryLocation() {
return this.entryLocation;
}
}

View File

@ -1,8 +1,8 @@
package net.knarcraft.dropper.command; package net.knarcraft.dropper.command;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.DropperArena; import net.knarcraft.dropper.arena.dropper.DropperArena;
import net.knarcraft.dropper.arena.DropperArenaHandler; import net.knarcraft.dropper.arena.dropper.DropperArenaHandler;
import net.knarcraft.dropper.util.StringSanitizer; import net.knarcraft.dropper.util.StringSanitizer;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
@ -36,7 +36,7 @@ public class CreateArenaCommand implements CommandExecutor {
return false; return false;
} }
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler(); DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
DropperArena existingArena = arenaHandler.getArena(arenaName); DropperArena existingArena = arenaHandler.getArena(arenaName);
if (existingArena != null) { if (existingArena != null) {

View File

@ -1,8 +1,8 @@
package net.knarcraft.dropper.command; package net.knarcraft.dropper.command;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.ArenaEditableProperty; import net.knarcraft.dropper.arena.dropper.DropperArena;
import net.knarcraft.dropper.arena.DropperArena; import net.knarcraft.dropper.arena.dropper.DropperArenaEditableProperty;
import net.knarcraft.dropper.config.DropperConfiguration; import net.knarcraft.dropper.config.DropperConfiguration;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
@ -40,13 +40,13 @@ public class EditArenaCommand implements CommandExecutor {
return false; return false;
} }
DropperArena specifiedArena = Dropper.getInstance().getArenaHandler().getArena(arguments[0]); DropperArena specifiedArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]);
if (specifiedArena == null) { if (specifiedArena == null) {
commandSender.sendMessage("Unable to find the specified dropper arena."); commandSender.sendMessage("Unable to find the specified dropper arena.");
return false; return false;
} }
ArenaEditableProperty editableProperty = ArenaEditableProperty.getFromArgumentString(arguments[1]); DropperArenaEditableProperty editableProperty = DropperArenaEditableProperty.getFromArgumentString(arguments[1]);
if (editableProperty == null) { if (editableProperty == null) {
commandSender.sendMessage("Unknown property specified."); commandSender.sendMessage("Unknown property specified.");
return false; return false;
@ -79,7 +79,7 @@ public class EditArenaCommand implements CommandExecutor {
* @param player <p>The player trying to change the value</p> * @param player <p>The player trying to change the value</p>
* @return <p>True if the value was successfully changed</p> * @return <p>True if the value was successfully changed</p>
*/ */
private boolean changeValue(@NotNull DropperArena arena, @NotNull ArenaEditableProperty property, private boolean changeValue(@NotNull DropperArena arena, @NotNull DropperArenaEditableProperty property,
@NotNull String value, @NotNull Player player) { @NotNull String value, @NotNull Player player) {
return switch (property) { return switch (property) {
case WIN_BLOCK_TYPE -> arena.setWinBlockType(parseMaterial(value)); case WIN_BLOCK_TYPE -> arena.setWinBlockType(parseMaterial(value));

View File

@ -1,9 +1,9 @@
package net.knarcraft.dropper.command; package net.knarcraft.dropper.command;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.DropperArena; import net.knarcraft.dropper.arena.dropper.DropperArena;
import net.knarcraft.dropper.arena.DropperArenaGroup; import net.knarcraft.dropper.arena.dropper.DropperArenaGroup;
import net.knarcraft.dropper.arena.DropperArenaHandler; import net.knarcraft.dropper.arena.dropper.DropperArenaHandler;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@ -22,7 +22,7 @@ public class GroupListCommand implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) { @NotNull String[] arguments) {
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler(); DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
if (arguments.length == 0) { if (arguments.length == 0) {
displayExistingGroups(arenaHandler, commandSender); displayExistingGroups(arenaHandler, commandSender);
return true; return true;
@ -81,7 +81,7 @@ public class GroupListCommand implements TabExecutor {
@NotNull String[] arguments) { @NotNull String[] arguments) {
if (arguments.length == 1) { if (arguments.length == 1) {
List<String> groupNames = new ArrayList<>(); List<String> groupNames = new ArrayList<>();
for (DropperArenaGroup group : Dropper.getInstance().getArenaHandler().getAllGroups()) { for (DropperArenaGroup group : MiniGames.getInstance().getDropperArenaHandler().getAllGroups()) {
groupNames.add(group.getGroupName()); groupNames.add(group.getGroupName());
} }
return groupNames; return groupNames;

View File

@ -1,9 +1,9 @@
package net.knarcraft.dropper.command; package net.knarcraft.dropper.command;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.DropperArena; import net.knarcraft.dropper.arena.dropper.DropperArena;
import net.knarcraft.dropper.arena.DropperArenaGroup; import net.knarcraft.dropper.arena.dropper.DropperArenaGroup;
import net.knarcraft.dropper.arena.DropperArenaHandler; import net.knarcraft.dropper.arena.dropper.DropperArenaHandler;
import net.knarcraft.dropper.util.StringSanitizer; import net.knarcraft.dropper.util.StringSanitizer;
import net.knarcraft.dropper.util.TabCompleteHelper; import net.knarcraft.dropper.util.TabCompleteHelper;
import org.bukkit.command.Command; import org.bukkit.command.Command;
@ -27,7 +27,7 @@ public class GroupSetCommand implements TabExecutor {
return false; return false;
} }
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler(); DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
DropperArena specifiedArena = arenaHandler.getArena(arguments[0]); DropperArena specifiedArena = arenaHandler.getArena(arguments[0]);
if (specifiedArena == null) { if (specifiedArena == null) {
@ -67,7 +67,7 @@ public class GroupSetCommand implements TabExecutor {
List<String> possibleValues = new ArrayList<>(); List<String> possibleValues = new ArrayList<>();
possibleValues.add("none"); possibleValues.add("none");
possibleValues.add("GroupName"); possibleValues.add("GroupName");
for (DropperArenaGroup group : Dropper.getInstance().getArenaHandler().getAllGroups()) { for (DropperArenaGroup group : MiniGames.getInstance().getDropperArenaHandler().getAllGroups()) {
possibleValues.add(group.getGroupName()); possibleValues.add(group.getGroupName());
} }
return possibleValues; return possibleValues;

View File

@ -1,9 +1,9 @@
package net.knarcraft.dropper.command; package net.knarcraft.dropper.command;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.DropperArena; import net.knarcraft.dropper.arena.dropper.DropperArena;
import net.knarcraft.dropper.arena.DropperArenaGroup; import net.knarcraft.dropper.arena.dropper.DropperArenaGroup;
import net.knarcraft.dropper.arena.DropperArenaHandler; import net.knarcraft.dropper.arena.dropper.DropperArenaHandler;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@ -26,7 +26,7 @@ public class GroupSwapCommand implements TabExecutor {
return false; return false;
} }
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler(); DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
DropperArena arena1 = arenaHandler.getArena(arguments[0]); DropperArena arena1 = arenaHandler.getArena(arguments[0]);
if (arena1 == null) { if (arena1 == null) {
@ -57,7 +57,7 @@ public class GroupSwapCommand implements TabExecutor {
@Override @Override
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) { @NotNull String[] arguments) {
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler(); DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
if (arguments.length == 1) { if (arguments.length == 1) {
List<String> arenaNames = new ArrayList<>(); List<String> arenaNames = new ArrayList<>();
for (DropperArena dropperArena : arenaHandler.getArenasInAGroup()) { for (DropperArena dropperArena : arenaHandler.getArenasInAGroup()) {
@ -78,7 +78,7 @@ public class GroupSwapCommand implements TabExecutor {
* @return <p>The names of the arenas in the same group</p> * @return <p>The names of the arenas in the same group</p>
*/ */
private List<String> getArenaNamesInSameGroup(String arenaName) { private List<String> getArenaNamesInSameGroup(String arenaName) {
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler(); DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
DropperArena arena1 = arenaHandler.getArena(arenaName); DropperArena arena1 = arenaHandler.getArena(arenaName);
if (arena1 == null) { if (arena1 == null) {
return new ArrayList<>(); return new ArrayList<>();

View File

@ -1,11 +1,11 @@
package net.knarcraft.dropper.command; package net.knarcraft.dropper.command;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.ArenaGameMode; import net.knarcraft.dropper.arena.dropper.DropperArena;
import net.knarcraft.dropper.arena.DropperArena; import net.knarcraft.dropper.arena.dropper.DropperArenaGameMode;
import net.knarcraft.dropper.arena.DropperArenaGroup; import net.knarcraft.dropper.arena.dropper.DropperArenaGroup;
import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry; import net.knarcraft.dropper.arena.dropper.DropperArenaPlayerRegistry;
import net.knarcraft.dropper.arena.DropperArenaSession; import net.knarcraft.dropper.arena.dropper.DropperArenaSession;
import net.knarcraft.dropper.config.DropperConfiguration; import net.knarcraft.dropper.config.DropperConfiguration;
import net.knarcraft.dropper.util.PlayerTeleporter; import net.knarcraft.dropper.util.PlayerTeleporter;
import org.bukkit.command.Command; import org.bukkit.command.Command;
@ -32,14 +32,14 @@ public class JoinArenaCommand implements CommandExecutor {
} }
// Disallow joining if the player is already in a dropper arena // Disallow joining if the player is already in a dropper arena
DropperArenaSession existingSession = Dropper.getInstance().getPlayerRegistry().getArenaSession(player.getUniqueId()); DropperArenaSession existingSession = MiniGames.getInstance().getDropperArenaPlayerRegistry().getArenaSession(player.getUniqueId());
if (existingSession != null) { if (existingSession != null) {
commandSender.sendMessage("You are already in a dropper arena!"); commandSender.sendMessage("You are already in a dropper arena!");
return false; return false;
} }
// Make sure the arena exists // Make sure the arena exists
DropperArena specifiedArena = Dropper.getInstance().getArenaHandler().getArena(arguments[0]); DropperArena specifiedArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]);
if (specifiedArena == null) { if (specifiedArena == null) {
commandSender.sendMessage("Unable to find the specified dropper arena."); commandSender.sendMessage("Unable to find the specified dropper arena.");
return false; return false;
@ -64,30 +64,30 @@ public class JoinArenaCommand implements CommandExecutor {
*/ */
private boolean joinArena(DropperArena specifiedArena, Player player, String[] arguments) { private boolean joinArena(DropperArena specifiedArena, Player player, String[] arguments) {
// Find the specified game-mode // Find the specified game-mode
ArenaGameMode gameMode; DropperArenaGameMode gameMode;
if (arguments.length > 1) { if (arguments.length > 1) {
gameMode = ArenaGameMode.matchGamemode(arguments[1]); gameMode = DropperArenaGameMode.matchGamemode(arguments[1]);
} else { } else {
gameMode = ArenaGameMode.DEFAULT; gameMode = DropperArenaGameMode.DEFAULT;
} }
// Make sure the player has beaten the necessary levels // 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)) { if (arenaGroup != null && !doGroupChecks(specifiedArena, arenaGroup, gameMode, player)) {
return false; return false;
} }
// Make sure the player has beaten the arena once in normal mode before playing another mode // Make sure the player has beaten the arena once in normal mode before playing another mode
if (Dropper.getInstance().getDropperConfiguration().mustDoNormalModeFirst() && if (MiniGames.getInstance().getDropperConfiguration().mustDoNormalModeFirst() &&
gameMode != ArenaGameMode.DEFAULT && gameMode != DropperArenaGameMode.DEFAULT &&
specifiedArena.getData().hasNotCompleted(ArenaGameMode.DEFAULT, player)) { specifiedArena.getData().hasNotCompleted(DropperArenaGameMode.DEFAULT, player)) {
player.sendMessage("You must complete this arena in normal mode first!"); player.sendMessage("You must complete this arena in normal mode first!");
return false; return false;
} }
// Register the player's session // Register the player's session
DropperArenaSession newSession = new DropperArenaSession(specifiedArena, player, gameMode); DropperArenaSession newSession = new DropperArenaSession(specifiedArena, player, gameMode);
DropperArenaPlayerRegistry playerRegistry = Dropper.getInstance().getPlayerRegistry(); DropperArenaPlayerRegistry playerRegistry = MiniGames.getInstance().getDropperArenaPlayerRegistry();
playerRegistry.registerPlayer(player.getUniqueId(), newSession); playerRegistry.registerPlayer(player.getUniqueId(), newSession);
// Try to teleport the player to the arena // Try to teleport the player to the arena
@ -114,11 +114,11 @@ public class JoinArenaCommand implements CommandExecutor {
* @return <p>False if any checks failed</p> * @return <p>False if any checks failed</p>
*/ */
private boolean doGroupChecks(@NotNull DropperArena dropperArena, @NotNull DropperArenaGroup arenaGroup, private boolean doGroupChecks(@NotNull DropperArena dropperArena, @NotNull DropperArenaGroup arenaGroup,
@NotNull ArenaGameMode arenaGameMode, @NotNull Player player) { @NotNull DropperArenaGameMode arenaGameMode, @NotNull Player player) {
DropperConfiguration configuration = Dropper.getInstance().getDropperConfiguration(); DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration();
// Require that players beat all arenas in the group in the normal game-mode before trying challenge modes // Require that players beat all arenas in the group in the normal game-mode before trying challenge modes
if (configuration.mustDoNormalModeFirst() && arenaGameMode != ArenaGameMode.DEFAULT && if (configuration.mustDoNormalModeFirst() && arenaGameMode != DropperArenaGameMode.DEFAULT &&
!arenaGroup.hasBeatenAll(ArenaGameMode.DEFAULT, player)) { !arenaGroup.hasBeatenAll(DropperArenaGameMode.DEFAULT, player)) {
player.sendMessage("You have not yet beaten all arenas in this group!"); player.sendMessage("You have not yet beaten all arenas in this group!");
return false; return false;
} }

View File

@ -1,7 +1,7 @@
package net.knarcraft.dropper.command; package net.knarcraft.dropper.command;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.DropperArenaSession; import net.knarcraft.dropper.arena.dropper.DropperArenaSession;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@ -25,7 +25,7 @@ public class LeaveArenaCommand implements TabExecutor {
return false; return false;
} }
DropperArenaSession existingSession = Dropper.getInstance().getPlayerRegistry().getArenaSession( DropperArenaSession existingSession = MiniGames.getInstance().getDropperArenaPlayerRegistry().getArenaSession(
player.getUniqueId()); player.getUniqueId());
if (existingSession == null) { if (existingSession == null) {
commandSender.sendMessage("You are not in a dropper arena!"); commandSender.sendMessage("You are not in a dropper arena!");

View File

@ -1,6 +1,6 @@
package net.knarcraft.dropper.command; package net.knarcraft.dropper.command;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@ -18,7 +18,7 @@ public class ReloadCommand implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) { @NotNull String[] arguments) {
Dropper.getInstance().reload(); MiniGames.getInstance().reload();
commandSender.sendMessage("Plugin reloaded!"); commandSender.sendMessage("Plugin reloaded!");
return true; return true;
} }

View File

@ -1,7 +1,7 @@
package net.knarcraft.dropper.command; package net.knarcraft.dropper.command;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.DropperArena; import net.knarcraft.dropper.arena.dropper.DropperArena;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -21,14 +21,14 @@ public class RemoveArenaCommand implements CommandExecutor {
} }
// Get the specified arena // Get the specified arena
DropperArena targetArena = Dropper.getInstance().getArenaHandler().getArena(arguments[0]); DropperArena targetArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]);
if (targetArena == null) { if (targetArena == null) {
commandSender.sendMessage("Unable to find the specified arena"); commandSender.sendMessage("Unable to find the specified arena");
return false; return false;
} }
// Remove the arena // Remove the arena
Dropper.getInstance().getArenaHandler().removeArena(targetArena); MiniGames.getInstance().getDropperArenaHandler().removeArena(targetArena);
commandSender.sendMessage("The specified arena has been successfully removed"); commandSender.sendMessage("The specified arena has been successfully removed");
return true; return true;
} }

View File

@ -1,6 +1,6 @@
package net.knarcraft.dropper.config; package net.knarcraft.dropper.config;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
@ -16,11 +16,10 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* The configuration keeping track of the player's current configuration * The configuration keeping track of dropper settings
*/ */
public class DropperConfiguration { public class DropperConfiguration extends MiniGameConfiguration {
private FileConfiguration configuration;
private final static String rootNode = "dropper."; private final static String rootNode = "dropper.";
private double verticalVelocity; private double verticalVelocity;
@ -31,8 +30,6 @@ public class DropperConfiguration {
private boolean mustDoNormalModeFirst; private boolean mustDoNormalModeFirst;
private boolean makePlayersInvisible; private boolean makePlayersInvisible;
private boolean disableHitCollision; private boolean disableHitCollision;
private double liquidHitBoxDepth;
private double solidHitBoxDistance;
private boolean blockSneaking; private boolean blockSneaking;
private boolean blockSprinting; private boolean blockSprinting;
private Set<Material> blockWhitelist; private Set<Material> blockWhitelist;
@ -43,7 +40,7 @@ public class DropperConfiguration {
* @param configuration <p>The YAML configuration to use internally</p> * @param configuration <p>The YAML configuration to use internally</p>
*/ */
public DropperConfiguration(FileConfiguration configuration) { public DropperConfiguration(FileConfiguration configuration) {
this.configuration = configuration; super(configuration);
} }
/** /**
@ -127,30 +124,6 @@ public class DropperConfiguration {
return this.disableHitCollision; return this.disableHitCollision;
} }
/**
* Gets the negative depth a player must reach in a liquid block for fail/win detection to trigger
*
* <p>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.</p>
*
* @return <p>The liquid hit box depth to use</p>
*/
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
*
* <p>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.</p>
*
* @return <p>The solid hit box distance to use</p>
*/
public double getSolidHitBoxDistance() {
return this.solidHitBoxDistance;
}
/** /**
* Gets whether players trying to sneak while in a dropper arena to increase their downwards speed should be blocked * Gets whether players trying to sneak while in a dropper arena to increase their downwards speed should be blocked
* *
@ -169,19 +142,7 @@ public class DropperConfiguration {
return this.blockSprinting; return this.blockSprinting;
} }
/** @Override
* Loads all configuration values from disk
*
* @param configuration <p>The configuration to load</p>
*/
public void load(FileConfiguration configuration) {
this.configuration = configuration;
this.load();
}
/**
* Loads all configuration values from disk
*/
public void load() { public void load() {
this.verticalVelocity = configuration.getDouble(rootNode + "verticalVelocity", 1.0); this.verticalVelocity = configuration.getDouble(rootNode + "verticalVelocity", 1.0);
this.horizontalVelocity = (float) configuration.getDouble(rootNode + "horizontalVelocity", 1.0); this.horizontalVelocity = (float) configuration.getDouble(rootNode + "horizontalVelocity", 1.0);
@ -191,8 +152,6 @@ public class DropperConfiguration {
this.mustDoNormalModeFirst = configuration.getBoolean(rootNode + "mustDoNormalModeFirst", true); this.mustDoNormalModeFirst = configuration.getBoolean(rootNode + "mustDoNormalModeFirst", true);
this.makePlayersInvisible = configuration.getBoolean(rootNode + "makePlayersInvisible", false); this.makePlayersInvisible = configuration.getBoolean(rootNode + "makePlayersInvisible", false);
this.disableHitCollision = configuration.getBoolean(rootNode + "disableHitCollision", true); this.disableHitCollision = configuration.getBoolean(rootNode + "disableHitCollision", true);
this.liquidHitBoxDepth = configuration.getDouble(rootNode + "liquidHitBoxDepth", -0.8);
this.solidHitBoxDistance = configuration.getDouble(rootNode + "solidHitBoxDistance", 0.2);
this.blockSprinting = configuration.getBoolean(rootNode + "blockSprinting", true); this.blockSprinting = configuration.getBoolean(rootNode + "blockSprinting", true);
this.blockSneaking = configuration.getBoolean(rootNode + "blockSneaking", true); this.blockSneaking = configuration.getBoolean(rootNode + "blockSneaking", true);
sanitizeValues(); sanitizeValues();
@ -204,14 +163,6 @@ public class DropperConfiguration {
* Sanitizes configuration values to ensure they are within expected bounds * Sanitizes configuration values to ensure they are within expected bounds
*/ */
private void sanitizeValues() { private void sanitizeValues() {
if (this.liquidHitBoxDepth <= -1 || this.liquidHitBoxDepth > 0) {
this.liquidHitBoxDepth = -0.8;
}
if (this.solidHitBoxDistance <= 0 || this.solidHitBoxDistance > 1) {
this.solidHitBoxDistance = 0.2;
}
if (this.horizontalVelocity > 1 || this.horizontalVelocity <= 0) { if (this.horizontalVelocity > 1 || this.horizontalVelocity <= 0) {
this.horizontalVelocity = 1; this.horizontalVelocity = 1;
} }
@ -246,7 +197,7 @@ public class DropperConfiguration {
if (matched != null) { if (matched != null) {
this.blockWhitelist.add(matched); this.blockWhitelist.add(matched);
} else { } else {
Dropper.log(Level.WARNING, "Unable to parse: " + string); MiniGames.log(Level.WARNING, "Unable to parse: " + string);
} }
} }
} }
@ -267,7 +218,7 @@ public class DropperConfiguration {
if (tag != null) { if (tag != null) {
this.blockWhitelist.addAll(tag.getValues()); this.blockWhitelist.addAll(tag.getValues());
} else { } else {
Dropper.log(Level.WARNING, "Unable to parse: " + materialName); MiniGames.log(Level.WARNING, "Unable to parse: " + materialName);
} }
return true; return true;
} }
@ -286,8 +237,6 @@ public class DropperConfiguration {
"\n" + "Must do normal mode first: " + mustDoNormalModeFirst + "\n" + "Must do normal mode first: " + mustDoNormalModeFirst +
"\n" + "Make players invisible: " + makePlayersInvisible + "\n" + "Make players invisible: " + makePlayersInvisible +
"\n" + "Disable hit collision: " + disableHitCollision + "\n" + "Disable hit collision: " + disableHitCollision +
"\n" + "Liquid hit box depth: " + liquidHitBoxDepth +
"\n" + "Solid hit box distance: " + solidHitBoxDistance +
"\n" + "Block whitelist: "); "\n" + "Block whitelist: ");
for (Material material : blockWhitelist) { for (Material material : blockWhitelist) {
builder.append("\n - ").append(material.name()); builder.append("\n - ").append(material.name());

View File

@ -0,0 +1,37 @@
package net.knarcraft.dropper.config;
import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull;
/**
* A configuration for a mini-game
*/
public abstract class MiniGameConfiguration {
protected FileConfiguration configuration;
/**
* Instantiates a new mini-game configuration
*
* @param configuration <p>The YAML configuration to use internally</p>
*/
public MiniGameConfiguration(@NotNull FileConfiguration configuration) {
this.configuration = configuration;
}
/**
* Loads all configuration values from disk
*
* @param configuration <p>The configuration to load</p>
*/
public void load(FileConfiguration configuration) {
this.configuration = configuration;
this.load();
}
/**
* Loads all configuration values from disk, using the current file configuration
*/
public abstract void load();
}

View File

@ -0,0 +1,67 @@
package net.knarcraft.dropper.config;
import org.bukkit.configuration.file.FileConfiguration;
/**
* The configuration keeping track of parkour settings
*/
public class ParkourConfiguration extends MiniGameConfiguration {
private final static String rootNode = "parkour.";
private boolean mustDoGroupedInSequence;
private boolean ignoreRecordsUntilGroupBeatenOnce;
private boolean makePlayersInvisible;
/**
* Instantiates a new dropper configuration
*
* @param configuration <p>The YAML configuration to use internally</p>
*/
public ParkourConfiguration(FileConfiguration configuration) {
super(configuration);
}
/**
* Gets whether grouped arenas must be done in the set sequence
*
* @return <p>Whether grouped arenas must be done in sequence</p>
*/
public boolean mustDoGroupedInSequence() {
return this.mustDoGroupedInSequence;
}
/**
* Gets whether records should be discarded, unless the player has already beaten all arenas in the group
*
* @return <p>Whether to ignore records on the first play-through</p>
*/
public boolean ignoreRecordsUntilGroupBeatenOnce() {
return this.ignoreRecordsUntilGroupBeatenOnce;
}
/**
* Gets whether players should be made invisible while in an arena
*
* @return <p>Whether players should be made invisible</p>
*/
public boolean makePlayersInvisible() {
return this.makePlayersInvisible;
}
@Override
public void load() {
this.mustDoGroupedInSequence = configuration.getBoolean(rootNode + "mustDoGroupedInSequence", true);
this.ignoreRecordsUntilGroupBeatenOnce = configuration.getBoolean(rootNode + "ignoreRecordsUntilGroupBeatenOnce", false);
this.makePlayersInvisible = configuration.getBoolean(rootNode + "makePlayersInvisible", false);
}
@Override
public String toString() {
return "Current configuration:" +
"\n" + "Must do groups in sequence: " + mustDoGroupedInSequence +
"\n" + "Ignore records until group beaten once: " + ignoreRecordsUntilGroupBeatenOnce +
"\n" + "Make players invisible: " + makePlayersInvisible;
}
}

View File

@ -0,0 +1,69 @@
package net.knarcraft.dropper.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 <p>The YAML configuration to use internally</p>
*/
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
*
* <p>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.</p>
*
* @return <p>The liquid hit box depth to use</p>
*/
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
*
* <p>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.</p>
*
* @return <p>The solid hit box distance to use</p>
*/
public double getSolidHitBoxDistance() {
return this.solidHitBoxDistance;
}
@Override
public 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;
}
}

View File

@ -1,7 +1,7 @@
package net.knarcraft.dropper.listener; package net.knarcraft.dropper.listener;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.DropperArenaSession; import net.knarcraft.dropper.arena.dropper.DropperArenaSession;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@ -18,7 +18,7 @@ public class CommandListener implements Listener {
@EventHandler @EventHandler
public void onCommand(PlayerCommandPreprocessEvent event) { public void onCommand(PlayerCommandPreprocessEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
DropperArenaSession existingSession = Dropper.getInstance().getPlayerRegistry().getArenaSession( DropperArenaSession existingSession = MiniGames.getInstance().getDropperArenaPlayerRegistry().getArenaSession(
player.getUniqueId()); player.getUniqueId());
if (existingSession == null) { if (existingSession == null) {
return; return;

View File

@ -1,8 +1,8 @@
package net.knarcraft.dropper.listener; package net.knarcraft.dropper.listener;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry; import net.knarcraft.dropper.arena.dropper.DropperArenaPlayerRegistry;
import net.knarcraft.dropper.arena.DropperArenaSession; import net.knarcraft.dropper.arena.dropper.DropperArenaSession;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -25,7 +25,7 @@ public class DamageListener implements Listener {
Player player = (Player) event.getEntity(); Player player = (Player) event.getEntity();
// We don't care about damage outside arenas // We don't care about damage outside arenas
DropperArenaSession arenaSession = Dropper.getInstance().getPlayerRegistry().getArenaSession(player.getUniqueId()); DropperArenaSession arenaSession = MiniGames.getInstance().getDropperArenaPlayerRegistry().getArenaSession(player.getUniqueId());
if (arenaSession == null) { if (arenaSession == null) {
return; return;
} }
@ -44,7 +44,7 @@ public class DamageListener implements Listener {
return; return;
} }
DropperArenaPlayerRegistry registry = Dropper.getInstance().getPlayerRegistry(); DropperArenaPlayerRegistry registry = MiniGames.getInstance().getDropperArenaPlayerRegistry();
DropperArenaSession arenaSession = registry.getArenaSession(event.getEntity().getUniqueId()); DropperArenaSession arenaSession = registry.getArenaSession(event.getEntity().getUniqueId());
if (arenaSession != null) { if (arenaSession != null) {
// Cancel combustion for any player in an arena // Cancel combustion for any player in an arena

View File

@ -1,9 +1,9 @@
package net.knarcraft.dropper.listener; package net.knarcraft.dropper.listener;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.ArenaGameMode; import net.knarcraft.dropper.arena.dropper.DropperArenaGameMode;
import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry; import net.knarcraft.dropper.arena.dropper.DropperArenaPlayerRegistry;
import net.knarcraft.dropper.arena.DropperArenaSession; import net.knarcraft.dropper.arena.dropper.DropperArenaSession;
import net.knarcraft.dropper.config.DropperConfiguration; import net.knarcraft.dropper.config.DropperConfiguration;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
@ -43,7 +43,7 @@ public class MoveListener implements Listener {
} }
Player player = event.getPlayer(); Player player = event.getPlayer();
DropperArenaPlayerRegistry playerRegistry = Dropper.getInstance().getPlayerRegistry(); DropperArenaPlayerRegistry playerRegistry = MiniGames.getInstance().getDropperArenaPlayerRegistry();
DropperArenaSession arenaSession = playerRegistry.getArenaSession(player.getUniqueId()); DropperArenaSession arenaSession = playerRegistry.getArenaSession(player.getUniqueId());
if (arenaSession == null) { if (arenaSession == null) {
return; return;
@ -144,7 +144,7 @@ public class MoveListener implements Listener {
* @param session <p>The session to possibly invert flying for</p> * @param session <p>The session to possibly invert flying for</p>
*/ */
private void toggleFlyInversion(@NotNull DropperArenaSession session) { private void toggleFlyInversion(@NotNull DropperArenaSession session) {
if (session.getGameMode() != ArenaGameMode.RANDOM_INVERTED) { if (session.getGameMode() != DropperArenaGameMode.RANDOM_INVERTED) {
return; return;
} }
Player player = session.getPlayer(); Player player = session.getPlayer();

View File

@ -1,7 +1,7 @@
package net.knarcraft.dropper.listener; package net.knarcraft.dropper.listener;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.DropperArenaSession; import net.knarcraft.dropper.arena.dropper.DropperArenaSession;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -32,7 +32,7 @@ public class PlayerLeaveListener implements Listener {
return; return;
} }
Dropper.log(Level.WARNING, "Found player " + player.getUniqueId() + MiniGames.log(Level.WARNING, "Found player " + player.getUniqueId() +
" leaving in the middle of a session!"); " leaving in the middle of a session!");
leftSessions.put(player.getUniqueId(), arenaSession); leftSessions.put(player.getUniqueId(), arenaSession);
} }
@ -42,10 +42,10 @@ public class PlayerLeaveListener implements Listener {
UUID playerId = event.getPlayer().getUniqueId(); UUID playerId = event.getPlayer().getUniqueId();
// Force the player to quit from the session once they re-join // Force the player to quit from the session once they re-join
if (leftSessions.containsKey(playerId)) { if (leftSessions.containsKey(playerId)) {
Dropper.log(Level.WARNING, "Found un-exited dropper session!"); MiniGames.log(Level.WARNING, "Found un-exited dropper session!");
Bukkit.getScheduler().runTaskLater(Dropper.getInstance(), () -> { Bukkit.getScheduler().runTaskLater(MiniGames.getInstance(), () -> {
leftSessions.get(playerId).triggerQuit(false); leftSessions.get(playerId).triggerQuit(false);
Dropper.log(Level.WARNING, "Triggered a quit!"); MiniGames.log(Level.WARNING, "Triggered a quit!");
leftSessions.remove(playerId); leftSessions.remove(playerId);
}, 80); }, 80);
} }
@ -76,7 +76,7 @@ public class PlayerLeaveListener implements Listener {
* @return <p>The player's session, or null if not in a session</p> * @return <p>The player's session, or null if not in a session</p>
*/ */
private @Nullable DropperArenaSession getSession(@NotNull Player player) { private @Nullable DropperArenaSession getSession(@NotNull Player player) {
return Dropper.getInstance().getPlayerRegistry().getArenaSession(player.getUniqueId()); return MiniGames.getInstance().getDropperArenaPlayerRegistry().getArenaSession(player.getUniqueId());
} }
} }

View File

@ -1,12 +1,12 @@
package net.knarcraft.dropper.placeholder; package net.knarcraft.dropper.placeholder;
import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.ArenaGameMode; import net.knarcraft.dropper.arena.dropper.DropperArena;
import net.knarcraft.dropper.arena.DropperArena; import net.knarcraft.dropper.arena.dropper.DropperArenaGameMode;
import net.knarcraft.dropper.arena.DropperArenaGroup; import net.knarcraft.dropper.arena.dropper.DropperArenaGroup;
import net.knarcraft.dropper.arena.DropperArenaHandler; import net.knarcraft.dropper.arena.dropper.DropperArenaHandler;
import net.knarcraft.dropper.arena.DropperArenaRecordsRegistry; import net.knarcraft.dropper.arena.dropper.DropperArenaRecordsRegistry;
import net.knarcraft.dropper.arena.record.ArenaRecord; import net.knarcraft.dropper.arena.record.ArenaRecord;
import net.knarcraft.dropper.placeholder.parsing.InfoType; import net.knarcraft.dropper.placeholder.parsing.InfoType;
import net.knarcraft.dropper.placeholder.parsing.SelectionType; import net.knarcraft.dropper.placeholder.parsing.SelectionType;
@ -33,7 +33,7 @@ import java.util.function.Supplier;
*/ */
public class DropperRecordExpansion extends PlaceholderExpansion { public class DropperRecordExpansion extends PlaceholderExpansion {
private final Dropper plugin; private final MiniGames plugin;
private final Map<UUID, Set<GroupRecordCache<Integer>>> groupRecordDeathsCache; private final Map<UUID, Set<GroupRecordCache<Integer>>> groupRecordDeathsCache;
private final Map<UUID, Set<GroupRecordCache<Long>>> groupRecordTimeCache; private final Map<UUID, Set<GroupRecordCache<Long>>> groupRecordTimeCache;
@ -42,7 +42,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
* *
* @param plugin <p>A reference to the dropper plugin</p> * @param plugin <p>A reference to the dropper plugin</p>
*/ */
public DropperRecordExpansion(Dropper plugin) { public DropperRecordExpansion(MiniGames plugin) {
this.plugin = plugin; this.plugin = plugin;
this.groupRecordDeathsCache = new HashMap<>(); this.groupRecordDeathsCache = new HashMap<>();
this.groupRecordTimeCache = new HashMap<>(); this.groupRecordTimeCache = new HashMap<>();
@ -76,7 +76,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
return parameters; return parameters;
} }
RecordType recordType = RecordType.getFromString(parts[1]); RecordType recordType = RecordType.getFromString(parts[1]);
ArenaGameMode gameMode = ArenaGameMode.matchGamemode(parts[2]); DropperArenaGameMode gameMode = DropperArenaGameMode.matchGamemode(parts[2]);
SelectionType selectionType = SelectionType.getFromString(parts[3]); SelectionType selectionType = SelectionType.getFromString(parts[3]);
String identifier = parts[4]; String identifier = parts[4];
int recordNumber = Integer.parseInt(parts[5]) - 1; int recordNumber = Integer.parseInt(parts[5]) - 1;
@ -87,7 +87,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
} }
String info = null; String info = null;
DropperArenaHandler arenaHandler = plugin.getArenaHandler(); DropperArenaHandler arenaHandler = plugin.getDropperArenaHandler();
if (selectionType == SelectionType.GROUP) { if (selectionType == SelectionType.GROUP) {
info = getGroupRecord(arenaHandler, identifier, gameMode, recordType, recordNumber, infoType); info = getGroupRecord(arenaHandler, identifier, gameMode, recordType, recordNumber, infoType);
} else if (selectionType == SelectionType.ARENA) { } else if (selectionType == SelectionType.ARENA) {
@ -117,7 +117,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
* @return <p>The selected information about the record, or null if not found</p> * @return <p>The selected information about the record, or null if not found</p>
*/ */
private @Nullable String getGroupRecord(@NotNull DropperArenaHandler arenaHandler, @NotNull String identifier, private @Nullable String getGroupRecord(@NotNull DropperArenaHandler arenaHandler, @NotNull String identifier,
@NotNull ArenaGameMode gameMode, @NotNull RecordType recordType, @NotNull DropperArenaGameMode gameMode, @NotNull RecordType recordType,
int recordNumber, @NotNull InfoType infoType) { int recordNumber, @NotNull InfoType infoType) {
// Allow specifying the group UUID or the arena name // Allow specifying the group UUID or the arena name
DropperArenaGroup group; DropperArenaGroup group;
@ -154,7 +154,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
* @return <p>The record, or null if not found</p> * @return <p>The record, or null if not found</p>
*/ */
private @Nullable ArenaRecord<?> getGroupTimeRecord(@NotNull DropperArenaGroup group, private @Nullable ArenaRecord<?> getGroupTimeRecord(@NotNull DropperArenaGroup group,
@NotNull ArenaGameMode gameMode, int recordNumber) { @NotNull DropperArenaGameMode gameMode, int recordNumber) {
return getCachedGroupRecord(group, gameMode, RecordType.TIME, recordNumber, groupRecordTimeCache, return getCachedGroupRecord(group, gameMode, RecordType.TIME, recordNumber, groupRecordTimeCache,
() -> DropperGroupRecordHelper.getCombinedTime(group, gameMode)); () -> DropperGroupRecordHelper.getCombinedTime(group, gameMode));
} }
@ -168,7 +168,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
* @return <p>The record, or null if not found</p> * @return <p>The record, or null if not found</p>
*/ */
private @Nullable ArenaRecord<?> getGroupDeathRecord(@NotNull DropperArenaGroup group, private @Nullable ArenaRecord<?> getGroupDeathRecord(@NotNull DropperArenaGroup group,
@NotNull ArenaGameMode gameMode, int recordNumber) { @NotNull DropperArenaGameMode gameMode, int recordNumber) {
return getCachedGroupRecord(group, gameMode, RecordType.DEATHS, recordNumber, groupRecordDeathsCache, return getCachedGroupRecord(group, gameMode, RecordType.DEATHS, recordNumber, groupRecordDeathsCache,
() -> DropperGroupRecordHelper.getCombinedDeaths(group, gameMode)); () -> DropperGroupRecordHelper.getCombinedDeaths(group, gameMode));
} }
@ -186,7 +186,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
* @return <p>The specified record, or null if not found</p> * @return <p>The specified record, or null if not found</p>
*/ */
private <K extends Comparable<K>> @Nullable ArenaRecord<?> getCachedGroupRecord(@NotNull DropperArenaGroup group, private <K extends Comparable<K>> @Nullable ArenaRecord<?> getCachedGroupRecord(@NotNull DropperArenaGroup group,
@NotNull ArenaGameMode gameMode, @NotNull DropperArenaGameMode gameMode,
@NotNull RecordType recordType, @NotNull RecordType recordType,
int recordNumber, int recordNumber,
@NotNull Map<UUID, Set<GroupRecordCache<K>>> caches, @NotNull Map<UUID, Set<GroupRecordCache<K>>> caches,
@ -233,7 +233,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
* @return <p>The selected information about the record, or null if not found</p> * @return <p>The selected information about the record, or null if not found</p>
*/ */
private @Nullable String getArenaRecord(@NotNull DropperArenaHandler arenaHandler, @NotNull String identifier, private @Nullable String getArenaRecord(@NotNull DropperArenaHandler arenaHandler, @NotNull String identifier,
@NotNull ArenaGameMode gameMode, @NotNull RecordType recordType, @NotNull DropperArenaGameMode gameMode, @NotNull RecordType recordType,
int recordNumber, @NotNull InfoType infoType) { int recordNumber, @NotNull InfoType infoType) {
// Allow specifying the arena UUID or the arena name // Allow specifying the arena UUID or the arena name
DropperArena arena; DropperArena arena;
@ -245,7 +245,7 @@ public class DropperRecordExpansion extends PlaceholderExpansion {
if (arena == null) { if (arena == null) {
return null; return null;
} }
@NotNull Map<ArenaGameMode, DropperArenaRecordsRegistry> registries = arena.getData().recordRegistries(); @NotNull Map<DropperArenaGameMode, DropperArenaRecordsRegistry> registries = arena.getData().recordRegistries();
DropperArenaRecordsRegistry recordsRegistry = registries.get(gameMode); DropperArenaRecordsRegistry recordsRegistry = registries.get(gameMode);
ArenaRecord<?> record = getRecord(recordsRegistry, recordType, recordNumber); ArenaRecord<?> record = getRecord(recordsRegistry, recordType, recordNumber);

View File

@ -1,6 +1,6 @@
package net.knarcraft.dropper.placeholder; package net.knarcraft.dropper.placeholder;
import net.knarcraft.dropper.arena.ArenaGameMode; import net.knarcraft.dropper.arena.dropper.DropperArenaGameMode;
import net.knarcraft.dropper.arena.record.ArenaRecord; import net.knarcraft.dropper.arena.record.ArenaRecord;
import net.knarcraft.dropper.property.RecordType; import net.knarcraft.dropper.property.RecordType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -15,7 +15,8 @@ import java.util.Set;
* @param records <p>The stored records</p> * @param records <p>The stored records</p>
* @param createdTime <p>The time this cache was created</p> * @param createdTime <p>The time this cache was created</p>
*/ */
public record GroupRecordCache<K extends Comparable<K>>(@NotNull ArenaGameMode gameMode, @NotNull RecordType recordType, public record GroupRecordCache<K extends Comparable<K>>(@NotNull DropperArenaGameMode gameMode,
@NotNull RecordType recordType,
@NotNull Set<ArenaRecord<K>> records, @NotNull Set<ArenaRecord<K>> records,
@NotNull Long createdTime) { @NotNull Long createdTime) {
} }

View File

@ -1,12 +1,14 @@
package net.knarcraft.dropper.util; package net.knarcraft.dropper.util;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.ArenaGameMode; import net.knarcraft.dropper.arena.ArenaGameMode;
import net.knarcraft.dropper.arena.ArenaStorageKey; import net.knarcraft.dropper.arena.ArenaRecordsRegistry;
import net.knarcraft.dropper.arena.DropperArena; import net.knarcraft.dropper.arena.dropper.DropperArena;
import net.knarcraft.dropper.arena.DropperArenaData; import net.knarcraft.dropper.arena.dropper.DropperArenaData;
import net.knarcraft.dropper.arena.DropperArenaGroup; import net.knarcraft.dropper.arena.dropper.DropperArenaGameMode;
import net.knarcraft.dropper.arena.DropperArenaRecordsRegistry; import net.knarcraft.dropper.arena.dropper.DropperArenaGroup;
import net.knarcraft.dropper.arena.dropper.DropperArenaRecordsRegistry;
import net.knarcraft.dropper.arena.dropper.DropperArenaStorageKey;
import net.knarcraft.dropper.container.SerializableMaterial; import net.knarcraft.dropper.container.SerializableMaterial;
import net.knarcraft.dropper.container.SerializableUUID; import net.knarcraft.dropper.container.SerializableUUID;
import org.bukkit.Location; import org.bukkit.Location;
@ -30,11 +32,13 @@ import java.util.logging.Level;
*/ */
public final class ArenaStorageHelper { public final class ArenaStorageHelper {
private final static File dataFolder = MiniGames.getInstance().getDataFolder();
private final static String arenasConfigurationSection = "arenas"; private final static String arenasConfigurationSection = "arenas";
private final static String groupsConfigurationSection = "groups"; private final static String groupsConfigurationSection = "groups";
private static final File arenaFile = new File(Dropper.getInstance().getDataFolder(), "arenas.yml"); private static final File arenaFile = new File(dataFolder, "arenas.yml");
private static final File groupFile = new File(Dropper.getInstance().getDataFolder(), "groups.yml"); private static final File groupFile = new File(dataFolder, "groups.yml");
private static final File arenaDataFolder = new File(Dropper.getInstance().getDataFolder(), "arena_data"); private static final File dropperArenaDataFolder = new File(dataFolder, "dropper_arena_data");
private static final File parkourArenaDataFolder = new File(dataFolder, "parkour_arena_data");
private ArenaStorageHelper() { private ArenaStorageHelper() {
@ -85,21 +89,21 @@ public final class ArenaStorageHelper {
* @param arenas <p>The arenas to save</p> * @param arenas <p>The arenas to save</p>
* @throws IOException <p>If unable to write to the file</p> * @throws IOException <p>If unable to write to the file</p>
*/ */
public static void saveArenas(@NotNull Map<UUID, DropperArena> arenas) throws IOException { public static void saveDropperArenas(@NotNull Map<UUID, DropperArena> arenas) throws IOException {
YamlConfiguration configuration = new YamlConfiguration(); YamlConfiguration configuration = new YamlConfiguration();
ConfigurationSection arenaSection = configuration.createSection(arenasConfigurationSection); ConfigurationSection arenaSection = configuration.createSection(arenasConfigurationSection);
for (DropperArena arena : arenas.values()) { for (DropperArena arena : arenas.values()) {
//Note: While the arena name is used as the key, as the key has to be sanitized, the un-sanitized arena name //Note: While the arena name is used as the key, as the key has to be sanitized, the un-sanitized arena name
// must be stored as well // must be stored as well
@NotNull ConfigurationSection configSection = arenaSection.createSection(arena.getArenaId().toString()); @NotNull ConfigurationSection configSection = arenaSection.createSection(arena.getArenaId().toString());
configSection.set(ArenaStorageKey.ID.getKey(), new SerializableUUID(arena.getArenaId())); configSection.set(DropperArenaStorageKey.ID.getKey(), new SerializableUUID(arena.getArenaId()));
configSection.set(ArenaStorageKey.NAME.getKey(), arena.getArenaName()); configSection.set(DropperArenaStorageKey.NAME.getKey(), arena.getArenaName());
configSection.set(ArenaStorageKey.SPAWN_LOCATION.getKey(), arena.getSpawnLocation()); configSection.set(DropperArenaStorageKey.SPAWN_LOCATION.getKey(), arena.getSpawnLocation());
configSection.set(ArenaStorageKey.EXIT_LOCATION.getKey(), arena.getExitLocation()); configSection.set(DropperArenaStorageKey.EXIT_LOCATION.getKey(), arena.getExitLocation());
configSection.set(ArenaStorageKey.PLAYER_VERTICAL_VELOCITY.getKey(), arena.getPlayerVerticalVelocity()); configSection.set(DropperArenaStorageKey.PLAYER_VERTICAL_VELOCITY.getKey(), arena.getPlayerVerticalVelocity());
configSection.set(ArenaStorageKey.PLAYER_HORIZONTAL_VELOCITY.getKey(), arena.getPlayerHorizontalVelocity()); configSection.set(DropperArenaStorageKey.PLAYER_HORIZONTAL_VELOCITY.getKey(), arena.getPlayerHorizontalVelocity());
configSection.set(ArenaStorageKey.WIN_BLOCK_TYPE.getKey(), new SerializableMaterial(arena.getWinBlockType())); configSection.set(DropperArenaStorageKey.WIN_BLOCK_TYPE.getKey(), new SerializableMaterial(arena.getWinBlockType()));
saveArenaData(arena.getData()); saveDropperArenaData(arena.getData());
} }
configuration.save(arenaFile); configuration.save(arenaFile);
} }
@ -109,7 +113,7 @@ public final class ArenaStorageHelper {
* *
* @return <p>The loaded arenas, or null if the arenas configuration section is missing.</p> * @return <p>The loaded arenas, or null if the arenas configuration section is missing.</p>
*/ */
public static @NotNull Map<UUID, DropperArena> loadArenas() { public static @NotNull Map<UUID, DropperArena> loadDropperArenas() {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(arenaFile); YamlConfiguration configuration = YamlConfiguration.loadConfiguration(arenaFile);
ConfigurationSection arenaSection = configuration.getConfigurationSection(arenasConfigurationSection); ConfigurationSection arenaSection = configuration.getConfigurationSection(arenasConfigurationSection);
//If no such section exists, it must be the case that there is no data to load //If no such section exists, it must be the case that there is no data to load
@ -126,7 +130,7 @@ public final class ArenaStorageHelper {
continue; continue;
} }
DropperArena arena = loadArena(configurationSection); DropperArena arena = loadDropperArena(configurationSection);
if (arena != null) { if (arena != null) {
loadedArenas.put(arena.getArenaId(), arena); loadedArenas.put(arena.getArenaId(), arena);
} }
@ -141,20 +145,20 @@ public final class ArenaStorageHelper {
* @param configurationSection <p>The configuration section containing arena data</p> * @param configurationSection <p>The configuration section containing arena data</p>
* @return <p>The loaded arena, or null if invalid</p> * @return <p>The loaded arena, or null if invalid</p>
*/ */
private static @Nullable DropperArena loadArena(@NotNull ConfigurationSection configurationSection) { private static @Nullable DropperArena loadDropperArena(@NotNull ConfigurationSection configurationSection) {
UUID arenaId = ((SerializableUUID) configurationSection.get(ArenaStorageKey.ID.getKey(), UUID arenaId = ((SerializableUUID) configurationSection.get(DropperArenaStorageKey.ID.getKey(),
new SerializableUUID(UUID.randomUUID()))).uuid(); new SerializableUUID(UUID.randomUUID()))).uuid();
String arenaName = configurationSection.getString(ArenaStorageKey.NAME.getKey()); String arenaName = configurationSection.getString(DropperArenaStorageKey.NAME.getKey());
Location spawnLocation = (Location) configurationSection.get(ArenaStorageKey.SPAWN_LOCATION.getKey()); Location spawnLocation = (Location) configurationSection.get(DropperArenaStorageKey.SPAWN_LOCATION.getKey());
Location exitLocation = (Location) configurationSection.get(ArenaStorageKey.EXIT_LOCATION.getKey()); Location exitLocation = (Location) configurationSection.get(DropperArenaStorageKey.EXIT_LOCATION.getKey());
double verticalVelocity = configurationSection.getDouble(ArenaStorageKey.PLAYER_VERTICAL_VELOCITY.getKey()); double verticalVelocity = configurationSection.getDouble(DropperArenaStorageKey.PLAYER_VERTICAL_VELOCITY.getKey());
float horizontalVelocity = sanitizeHorizontalVelocity((float) configurationSection.getDouble( float horizontalVelocity = sanitizeHorizontalVelocity((float) configurationSection.getDouble(
ArenaStorageKey.PLAYER_HORIZONTAL_VELOCITY.getKey())); DropperArenaStorageKey.PLAYER_HORIZONTAL_VELOCITY.getKey()));
SerializableMaterial winBlockType = (SerializableMaterial) configurationSection.get( SerializableMaterial winBlockType = (SerializableMaterial) configurationSection.get(
ArenaStorageKey.WIN_BLOCK_TYPE.getKey()); DropperArenaStorageKey.WIN_BLOCK_TYPE.getKey());
if (arenaName == null || spawnLocation == null) { if (arenaName == null || spawnLocation == null) {
Dropper.log(Level.SEVERE, "Could not load the arena at configuration " + MiniGames.log(Level.SEVERE, "Could not load the arena at configuration " +
"section " + configurationSection.getName() + ". Please check the arenas storage file for issues."); "section " + configurationSection.getName() + ". Please check the arenas storage file for issues.");
return null; return null;
} }
@ -162,19 +166,19 @@ public final class ArenaStorageHelper {
winBlockType = new SerializableMaterial(Material.WATER); winBlockType = new SerializableMaterial(Material.WATER);
} }
DropperArenaData arenaData = loadArenaData(arenaId); DropperArenaData arenaData = loadDropperArenaData(arenaId);
if (arenaData == null) { if (arenaData == null) {
Dropper.log(Level.SEVERE, "Unable to load arena data for " + arenaId); MiniGames.log(Level.SEVERE, "Unable to load arena data for " + arenaId);
Map<ArenaGameMode, DropperArenaRecordsRegistry> recordRegistries = new HashMap<>(); Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries = new HashMap<>();
for (ArenaGameMode arenaGameMode : ArenaGameMode.values()) { for (ArenaGameMode arenaGameMode : DropperArenaGameMode.values()) {
recordRegistries.put(arenaGameMode, new DropperArenaRecordsRegistry(arenaId)); recordRegistries.put(arenaGameMode, new DropperArenaRecordsRegistry(arenaId));
} }
arenaData = new DropperArenaData(arenaId, recordRegistries, new HashMap<>()); arenaData = new DropperArenaData(arenaId, recordRegistries, new HashMap<>());
} }
return new DropperArena(arenaId, arenaName, spawnLocation, exitLocation, verticalVelocity, horizontalVelocity, return new DropperArena(arenaId, arenaName, spawnLocation, exitLocation, verticalVelocity, horizontalVelocity,
winBlockType.material(), arenaData, Dropper.getInstance().getArenaHandler()); winBlockType.material(), arenaData, MiniGames.getInstance().getDropperArenaHandler());
} }
/** /**
@ -182,11 +186,11 @@ public final class ArenaStorageHelper {
* *
* @param arenaData <p>The arena data to store</p> * @param arenaData <p>The arena data to store</p>
*/ */
public static void saveArenaData(@NotNull DropperArenaData arenaData) throws IOException { public static void saveDropperArenaData(@NotNull DropperArenaData arenaData) throws IOException {
YamlConfiguration configuration = new YamlConfiguration(); YamlConfiguration configuration = new YamlConfiguration();
configuration.set(ArenaStorageKey.DATA.getKey(), arenaData); configuration.set(DropperArenaStorageKey.DATA.getKey(), arenaData);
configuration.save(getArenaDataFile(arenaData.arenaId())); configuration.save(getDropperArenaDataFile(arenaData.getArenaId()));
} }
/** /**
@ -195,10 +199,10 @@ public final class ArenaStorageHelper {
* @param arenaId <p>The id of the arena to get data for</p> * @param arenaId <p>The id of the arena to get data for</p>
* @return <p>The loaded arena data</p> * @return <p>The loaded arena data</p>
*/ */
private static @Nullable DropperArenaData loadArenaData(@NotNull UUID arenaId) { private static @Nullable DropperArenaData loadDropperArenaData(@NotNull UUID arenaId) {
File arenaDataFile = getArenaDataFile(arenaId); File arenaDataFile = getDropperArenaDataFile(arenaId);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(arenaDataFile); YamlConfiguration configuration = YamlConfiguration.loadConfiguration(arenaDataFile);
return (DropperArenaData) configuration.get(ArenaStorageKey.DATA.getKey()); return (DropperArenaData) configuration.get(DropperArenaStorageKey.DATA.getKey());
} }
/** /**
@ -207,8 +211,8 @@ public final class ArenaStorageHelper {
* @param arenaId <p>The id of the arena to remove data for</p> * @param arenaId <p>The id of the arena to remove data for</p>
* @return <p>True if the data was successfully removed</p> * @return <p>True if the data was successfully removed</p>
*/ */
public static boolean removeArenaData(@NotNull UUID arenaId) { public static boolean removeDropperArenaData(@NotNull UUID arenaId) {
return getArenaDataFile(arenaId).delete(); return getDropperArenaDataFile(arenaId).delete();
} }
/** /**
@ -217,10 +221,10 @@ public final class ArenaStorageHelper {
* @param arenaId <p>The id of the arena to get a data file for</p> * @param arenaId <p>The id of the arena to get a data file for</p>
* @return <p>The file the arena's data is/should be stored in</p> * @return <p>The file the arena's data is/should be stored in</p>
*/ */
private static @NotNull File getArenaDataFile(@NotNull UUID arenaId) { private static @NotNull File getDropperArenaDataFile(@NotNull UUID arenaId) {
File arenaDataFile = new File(arenaDataFolder, arenaId + ".yml"); File arenaDataFile = new File(parkourArenaDataFolder, arenaId + ".yml");
if (!arenaDataFolder.exists() && !arenaDataFolder.mkdirs()) { if (!parkourArenaDataFolder.exists() && !parkourArenaDataFolder.mkdirs()) {
Dropper.log(Level.SEVERE, "Unable to create the arena data directories"); MiniGames.log(Level.SEVERE, "Unable to create the arena data directories");
} }
return arenaDataFile; return arenaDataFile;
} }

View File

@ -1,10 +1,10 @@
package net.knarcraft.dropper.util; package net.knarcraft.dropper.util;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.ArenaGameMode; import net.knarcraft.dropper.arena.dropper.DropperArena;
import net.knarcraft.dropper.arena.DropperArena; import net.knarcraft.dropper.arena.dropper.DropperArenaGameMode;
import net.knarcraft.dropper.arena.DropperArenaGroup; import net.knarcraft.dropper.arena.dropper.DropperArenaGroup;
import net.knarcraft.dropper.arena.DropperArenaHandler; import net.knarcraft.dropper.arena.dropper.DropperArenaHandler;
import net.knarcraft.dropper.arena.record.ArenaRecord; import net.knarcraft.dropper.arena.record.ArenaRecord;
import net.knarcraft.dropper.arena.record.SummableArenaRecord; import net.knarcraft.dropper.arena.record.SummableArenaRecord;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -33,9 +33,9 @@ public final class DropperGroupRecordHelper {
* @return <p>The combined death records</p> * @return <p>The combined death records</p>
*/ */
public static @NotNull Set<ArenaRecord<Integer>> getCombinedDeaths(@NotNull DropperArenaGroup group, public static @NotNull Set<ArenaRecord<Integer>> getCombinedDeaths(@NotNull DropperArenaGroup group,
@NotNull ArenaGameMode gameMode) { @NotNull DropperArenaGameMode gameMode) {
Map<UUID, SummableArenaRecord<Integer>> records = new HashMap<>(); Map<UUID, SummableArenaRecord<Integer>> records = new HashMap<>();
@NotNull BiFunction<DropperArena, ArenaGameMode, Set<SummableArenaRecord<Integer>>> recordSupplier = @NotNull BiFunction<DropperArena, DropperArenaGameMode, Set<SummableArenaRecord<Integer>>> recordSupplier =
(arena, aGameMode) -> arena.getData().recordRegistries().get(gameMode).getLeastDeathsRecords(); (arena, aGameMode) -> arena.getData().recordRegistries().get(gameMode).getLeastDeathsRecords();
return getCombined(group, gameMode, records, recordSupplier); return getCombined(group, gameMode, records, recordSupplier);
@ -49,9 +49,9 @@ public final class DropperGroupRecordHelper {
* @return <p>The combined least-time records</p> * @return <p>The combined least-time records</p>
*/ */
public static @NotNull Set<ArenaRecord<Long>> getCombinedTime(@NotNull DropperArenaGroup group, public static @NotNull Set<ArenaRecord<Long>> getCombinedTime(@NotNull DropperArenaGroup group,
@NotNull ArenaGameMode gameMode) { @NotNull DropperArenaGameMode gameMode) {
Map<UUID, SummableArenaRecord<Long>> records = new HashMap<>(); Map<UUID, SummableArenaRecord<Long>> records = new HashMap<>();
@NotNull BiFunction<DropperArena, ArenaGameMode, Set<SummableArenaRecord<Long>>> recordSupplier = @NotNull BiFunction<DropperArena, DropperArenaGameMode, Set<SummableArenaRecord<Long>>> recordSupplier =
(arena, aGameMode) -> arena.getData().recordRegistries().get(gameMode).getShortestTimeMilliSecondsRecords(); (arena, aGameMode) -> arena.getData().recordRegistries().get(gameMode).getShortestTimeMilliSecondsRecords();
return getCombined(group, gameMode, records, recordSupplier); return getCombined(group, gameMode, records, recordSupplier);
@ -68,13 +68,13 @@ public final class DropperGroupRecordHelper {
* @return <p>The combined records</p> * @return <p>The combined records</p>
*/ */
private static <K extends Comparable<K>> @NotNull Set<ArenaRecord<K>> getCombined(@NotNull DropperArenaGroup group, private static <K extends Comparable<K>> @NotNull Set<ArenaRecord<K>> getCombined(@NotNull DropperArenaGroup group,
@NotNull ArenaGameMode gameMode, @NotNull DropperArenaGameMode gameMode,
@NotNull Map<UUID, @NotNull Map<UUID,
SummableArenaRecord<K>> records, SummableArenaRecord<K>> records,
@NotNull BiFunction<DropperArena, @NotNull BiFunction<DropperArena,
ArenaGameMode, DropperArenaGameMode,
Set<SummableArenaRecord<K>>> recordSupplier) { Set<SummableArenaRecord<K>>> recordSupplier) {
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler(); DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
// Get all arenas in the group // Get all arenas in the group
Set<DropperArena> arenas = getArenas(arenaHandler, group); Set<DropperArena> arenas = getArenas(arenaHandler, group);
@ -137,11 +137,11 @@ public final class DropperGroupRecordHelper {
* @param <K> <p>The type of record to combine</p> * @param <K> <p>The type of record to combine</p>
*/ */
private static <K extends Comparable<K>> void combineRecords(@NotNull Set<DropperArena> arenas, private static <K extends Comparable<K>> void combineRecords(@NotNull Set<DropperArena> arenas,
@NotNull ArenaGameMode gameMode, @NotNull DropperArenaGameMode gameMode,
@NotNull Map<UUID, @NotNull Map<UUID,
SummableArenaRecord<K>> combinedRecords, SummableArenaRecord<K>> combinedRecords,
@NotNull Map<UUID, Integer> recordsFound, @NotNull Map<UUID, Integer> recordsFound,
@NotNull BiFunction<DropperArena, ArenaGameMode, @NotNull BiFunction<DropperArena, DropperArenaGameMode,
Set<SummableArenaRecord<K>>> recordSupplier) { Set<SummableArenaRecord<K>>> recordSupplier) {
for (DropperArena arena : arenas) { for (DropperArena arena : arenas) {
Set<SummableArenaRecord<K>> existingRecords = recordSupplier.apply(arena, gameMode); Set<SummableArenaRecord<K>> existingRecords = recordSupplier.apply(arena, gameMode);

View File

@ -0,0 +1,26 @@
package net.knarcraft.dropper.util;
import org.bukkit.Location;
import org.bukkit.World;
/**
* A helper class for validating whether given input is valid
*/
public final class InputValidationHelper {
private InputValidationHelper() {
}
/**
* Checks whether the given location is valid
*
* @param location <p>The location to validate</p>
* @return <p>False if the location is valid</p>
*/
public static boolean isInvalid(Location location) {
World world = location.getWorld();
return world == null || !world.getWorldBorder().isInside(location);
}
}

View File

@ -1,6 +1,6 @@
package net.knarcraft.dropper.util; package net.knarcraft.dropper.util;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
@ -57,7 +57,7 @@ public final class PlayerTeleporter {
//When teleporting a player out of the arena, sometimes the move listener is slow to react, giving the player //When teleporting a player out of the arena, sometimes the move listener is slow to react, giving the player
// lethal velocity, and causing damage. That's why the player is given 5 ticks of invulnerability // lethal velocity, and causing damage. That's why the player is given 5 ticks of invulnerability
if (!immediately) { if (!immediately) {
Bukkit.getScheduler().runTaskLater(Dropper.getInstance(), () -> player.setInvulnerable(false), 5); Bukkit.getScheduler().runTaskLater(MiniGames.getInstance(), () -> player.setInvulnerable(false), 5);
} else { } else {
player.setInvulnerable(false); player.setInvulnerable(false);
} }

View File

@ -1,8 +1,8 @@
package net.knarcraft.dropper.util; package net.knarcraft.dropper.util;
import net.knarcraft.dropper.Dropper; import net.knarcraft.dropper.MiniGames;
import net.knarcraft.dropper.arena.ArenaEditableProperty; import net.knarcraft.dropper.arena.dropper.DropperArena;
import net.knarcraft.dropper.arena.DropperArena; import net.knarcraft.dropper.arena.dropper.DropperArenaEditableProperty;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
@ -24,7 +24,7 @@ public final class TabCompleteHelper {
*/ */
public static @NotNull List<String> getArenas() { public static @NotNull List<String> getArenas() {
List<String> arenaNames = new ArrayList<>(); List<String> arenaNames = new ArrayList<>();
for (DropperArena dropperArena : Dropper.getInstance().getArenaHandler().getArenas().values()) { for (DropperArena dropperArena : MiniGames.getInstance().getDropperArenaHandler().getArenas().values()) {
arenaNames.add(dropperArena.getArenaName()); arenaNames.add(dropperArena.getArenaName());
} }
return arenaNames; return arenaNames;
@ -37,7 +37,7 @@ public final class TabCompleteHelper {
*/ */
public static @NotNull List<String> getArenaProperties() { public static @NotNull List<String> getArenaProperties() {
List<String> arenaProperties = new ArrayList<>(); List<String> arenaProperties = new ArrayList<>();
for (ArenaEditableProperty property : ArenaEditableProperty.values()) { for (DropperArenaEditableProperty property : DropperArenaEditableProperty.values()) {
arenaProperties.add(property.getArgumentString()); arenaProperties.add(property.getArgumentString());
} }
return arenaProperties; return arenaProperties;

View File

@ -1,8 +1,8 @@
name: Dropper name: Dropper
version: '${project.version}' version: '${project.version}'
main: net.knarcraft.dropper.Dropper main: net.knarcraft.dropper.MiniGames
api-version: 1.19 api-version: 1.19
description: A plugin for dropper mini-games description: A plugin that adds various mini-games
softdepend: softdepend:
- PlaceholderAPI - PlaceholderAPI

View File

@ -1,5 +1,6 @@
package net.knarcraft.dropper.arena; package net.knarcraft.dropper.arena;
import net.knarcraft.dropper.arena.dropper.DropperArenaGroup;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -10,7 +11,7 @@ import java.util.UUID;
/** /**
* Tests for arena dropper groups * Tests for arena dropper groups
*/ */
public class DropperArenaGroupTest { public class MiniGamesArenaGroupTest {
@Test @Test
public void swapTest() { public void swapTest() {