Parkour implementation safety save 4

This is just a safety save in case the code gets too broken to fix.
This commit is contained in:
Kristian Knarvik 2023-04-14 14:45:59 +02:00
parent 8f77fc5910
commit 12789980c0
16 changed files with 475 additions and 550 deletions

View File

@ -21,13 +21,13 @@ To modify the arena, use `/dropperedit <name> <property> <value>`.
| Command | Alias | Arguments | Description |
|----------------------------------------|----------|-----------------------------|-------------------------------------------------------------------------------------|
| /miniGamesReload | /mreload | | Reloads all data from disk. |
| /miniGamesLeave | /mleave | | Leaves the current mini-game. |
| /dropperList | /dlist | | Lists available dropper arenas. |
| [/dropperJoin](#dropperjoin) | /djoin | \<arena> \[mode] | Joins the selected arena. |
| /dropperLeave | /dleave | | Leaves the current dropper arena. |
| /dropperCreate | /dcreate | \<name> | Creates a new dropper arena with the given name. The spawn is set to your location. |
| /dropperRemove | /dremove | \<arena> | Removes the specified dropper arena. |
| [/dropperEdit](#dropperedit) | /dedit | \<arena> \<option> \[value] | Gets or sets a dropper arena option. |
| /dropperReload | /dreload | | Reloads all data from disk. |
| [/dropperGroupSet](#droppergroupset) | /dgset | \<arena> \<group> | Puts the given arena in the given group. Use "none" to remove an existing group. |
| /dropperGroupList | /dglist | \[group] | Lists groups, or the stages of a group if a group is specified. |
| [/dropperGroupSwap](#droppergroupswap) | /dgswap | \<arena1> \<arena2> | Swaps the two arenas in the group's ordered list. |

View File

@ -191,10 +191,10 @@ public final class MiniGames extends JavaPlugin {
this.dropperConfiguration = new DropperConfiguration(this.getConfig());
this.parkourConfiguration = new ParkourConfiguration(this.getConfig());
this.dropperArenaPlayerRegistry = new DropperArenaPlayerRegistry();
this.dropperArenaHandler = new DropperArenaHandler();
this.dropperArenaHandler = new DropperArenaHandler(this.dropperArenaPlayerRegistry);
this.dropperArenaHandler.load();
this.parkourArenaHandler = new ParkourArenaHandler();
this.parkourArenaHandler = new ParkourArenaHandler(this.parkourArenaPlayerRegistry);
this.parkourArenaHandler.load();
PluginManager pluginManager = getServer().getPluginManager();

View File

@ -0,0 +1,47 @@
package net.knarcraft.minigames.arena;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* An interface describing an arena
*/
public interface Arena {
/**
* Gets the data stored for this arena
*
* @return <p>The stored data</p>
*/
ArenaData getData();
/**
* Gets the id of this arena
*
* @return <p>This arena's identifier</p>
*/
@NotNull UUID getArenaId();
/**
* Gets this arena's sanitized name
*
* @return <p>This arena's sanitized name</p>
*/
@NotNull String getArenaNameSanitized();
/**
* Removes the data file belonging to this arena
*
* @return <p>True if successfully removed</p>
*/
boolean removeData();
/**
* Saves this arena's data
*
* @return <p>True if successfully saved</p>
*/
boolean saveData();
}

View File

@ -1,9 +1,10 @@
package net.knarcraft.minigames.arena;
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.container.SerializableUUID;
import net.knarcraft.minigames.util.StringSanitizer;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@ -11,8 +12,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
public abstract class ArenaGroup implements ConfigurationSerializable {
public abstract class ArenaGroup<K extends Arena, S extends ArenaGroup<K, S>> implements ConfigurationSerializable {
/**
* The unique id for this group of arenas
@ -24,33 +26,43 @@ public abstract class ArenaGroup implements ConfigurationSerializable {
*/
private final String groupName;
/**
* The arena handler used to convert uuids to arenas
*/
private final ArenaHandler<K, S> arenaHandler;
/**
* The arenas in this group, ordered from stage 1 to stage n
*/
protected final List<UUID> arenas;
private final List<UUID> arenas;
/**
* Instantiates a new dropper arena group
*
* @param groupName <p>The name of this group</p>
* @param groupName <p>The name of this group</p>
* @param arenaHandler <p>The arena handler used to convert uuids to arenas</p>
*/
protected ArenaGroup(@NotNull String groupName) {
protected ArenaGroup(@NotNull String groupName, @NotNull ArenaHandler<K, S> arenaHandler) {
this.groupId = UUID.randomUUID();
this.groupName = groupName;
this.arenas = new ArrayList<>();
this.arenaHandler = arenaHandler;
}
/**
* 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>
* @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>
* @param arenaHandler <p>The arena handler used to convert uuids to arenas</p>
*/
protected ArenaGroup(@NotNull UUID groupId, @NotNull String groupName, @NotNull List<UUID> arenas) {
protected ArenaGroup(@NotNull UUID groupId, @NotNull String groupName, @NotNull List<UUID> arenas,
@NotNull ArenaHandler<K, S> arenaHandler) {
this.groupId = groupId;
this.groupName = groupName;
this.arenas = new ArrayList<>(arenas);
this.arenaHandler = arenaHandler;
}
/**
@ -120,6 +132,67 @@ public abstract class ArenaGroup implements ConfigurationSerializable {
return StringSanitizer.sanitizeArenaName(this.getGroupName());
}
/**
* 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) {
for (UUID anArenaId : this.getArenas()) {
K arena = this.arenaHandler.getArena(anArenaId);
if (arena == null) {
// The arena would only be null if the arena has been deleted, but not removed from this group
MiniGames.log(Level.WARNING, "The dropper group " + this.getGroupName() +
" contains the arena id " + anArenaId + " which is not a valid arena id!");
continue;
}
if (arena.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!");
}
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;
}
K arena = this.arenaHandler.getArena(anArenaId);
if (arena == null) {
// The arena would only be null if the arena has been deleted, but not removed from this group
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 (arena.getData().hasNotCompleted(gameMode, player)) {
return false;
}
}
return false;
}
/**
* Swaps the arenas at the given indices
*
@ -156,7 +229,7 @@ public abstract class ArenaGroup implements ConfigurationSerializable {
@Override
public boolean equals(Object other) {
if (!(other instanceof DropperArenaGroup otherGroup)) {
if (!(other instanceof ArenaGroup<?, ?> otherGroup)) {
return false;
}
return this.getGroupNameSanitized().equals(otherGroup.getGroupNameSanitized());

View File

@ -0,0 +1,230 @@
package net.knarcraft.minigames.arena;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.util.StringSanitizer;
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.Set;
import java.util.UUID;
import java.util.logging.Level;
/**
* An interface describing a generic arena handler
*
* @param <K> <p>The type of arena stored</p>
* @param <S> <p>The type of arena group stored</p>
*/
public abstract class ArenaHandler<K extends Arena, S extends ArenaGroup<K, S>> {
protected Map<UUID, K> arenas = new HashMap<>();
protected Map<UUID, S> arenaGroups = new HashMap<>();
protected Map<String, UUID> arenaNameLookup = new HashMap<>();
private final ArenaPlayerRegistry<K> playerRegistry;
/**
* Instantiates a new arena handler
*
* @param playerRegistry <p>The registry keeping track of player sessions</p>
*/
public ArenaHandler(ArenaPlayerRegistry<K> playerRegistry) {
this.playerRegistry = playerRegistry;
}
/**
* Gets all arenas that are within a group
*
* @return <p>All arenas in a group</p>
*/
public @NotNull Set<K> getArenasInAGroup() {
Set<K> arenas = new HashSet<>();
for (UUID arenaId : arenaGroups.keySet()) {
arenas.add(this.arenas.get(arenaId));
}
return arenas;
}
/**
* Gets a copy of all arena groups
*
* @return <p>All arena groups</p>
*/
public Set<S> 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 S 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 S arenaGroup) {
if (arenaGroup == null) {
// No need to remove something non-existing
if (!this.arenaGroups.containsKey(arenaId)) {
return;
}
// Remove the existing group
S 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 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 S getGroup(String groupName) {
String sanitized = StringSanitizer.sanitizeArenaName(groupName);
for (S 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 K 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 K 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 K 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, K> getArenas() {
return new HashMap<>(this.arenas);
}
/**
* Removes the given arena
*
* @param arena <p>The arena to remove</p>
*/
public void removeArena(@NotNull K arena) {
UUID arenaId = arena.getArenaId();
this.playerRegistry.removeForArena(arena);
this.arenas.remove(arenaId);
this.arenaNameLookup.remove(arena.getArenaNameSanitized());
this.arenaGroups.remove(arenaId);
if (!arena.removeData()) {
MiniGames.log(Level.WARNING, "Unable to remove 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) {
K arena = getArena(arenaId);
if (arena != null) {
if (!arena.saveData()) {
MiniGames.log(Level.WARNING, "Unable to save data for arena with id " + arenaId +
" because of a write exception!");
}
} else {
MiniGames.log(Level.WARNING, "Unable to save data for arena with id " + arenaId +
" because the arena could not be found!");
}
}
/**
* Saves all current groups to disk
*/
public abstract void saveGroups();
/**
* Loads all groups from disk
*/
protected abstract void loadGroups();
/**
* Loads all arenas and groups from disk
*/
public void load() {
loadArenas();
loadGroups();
}
/**
* Saves all current arenas to disk
*/
public abstract void saveArenas();
/**
* Loads all arenas from disk
*/
protected abstract void loadArenas();
}

View File

@ -0,0 +1,17 @@
package net.knarcraft.minigames.arena;
/**
* A registry keeping track of all player sessions for some arenas
*
* @param <K> <p>The type of arena this registry stores</p>
*/
public interface ArenaPlayerRegistry<K extends Arena> {
/**
* Removes all active sessions for the given arena
*
* @param arena <p>The arena to remove sessions for</p>
*/
void removeForArena(K arena);
}

View File

@ -1,15 +1,18 @@
package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.Arena;
import net.knarcraft.minigames.arena.ArenaGameMode;
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
import net.knarcraft.minigames.config.DropperConfiguration;
import net.knarcraft.minigames.util.DropperArenaStorageHelper;
import net.knarcraft.minigames.util.StringSanitizer;
import org.bukkit.Location;
import org.bukkit.Material;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@ -19,7 +22,7 @@ import static net.knarcraft.minigames.util.InputValidationHelper.isInvalid;
/**
* A representation of one dropper arena
*/
public class DropperArena {
public class DropperArena implements Arena {
/**
* An unique and persistent identifier for this arena
@ -212,6 +215,21 @@ public class DropperArena {
return StringSanitizer.sanitizeArenaName(this.getArenaName());
}
@Override
public boolean removeData() {
return DropperArenaStorageHelper.removeDropperArenaData(getArenaId());
}
@Override
public boolean saveData() {
try {
DropperArenaStorageHelper.saveDropperArenaData(getData());
return true;
} catch (IOException e) {
return false;
}
}
/**
* Sets the spawn location for this arena
*

View File

@ -4,19 +4,17 @@ import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaGroup;
import net.knarcraft.minigames.container.SerializableUUID;
import net.knarcraft.minigames.util.SerializableConverter;
import org.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 {
public class DropperArenaGroup extends ArenaGroup<DropperArena, DropperArenaGroup> {
/**
* Instantiates a new dropper arena group
@ -24,7 +22,7 @@ public class DropperArenaGroup extends ArenaGroup {
* @param groupName <p>The name of this group</p>
*/
public DropperArenaGroup(@NotNull String groupName) {
super(groupName);
super(groupName, MiniGames.getInstance().getDropperArenaHandler());
}
/**
@ -35,71 +33,7 @@ public class DropperArenaGroup extends ArenaGroup {
* @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;
super(groupId, groupName, arenas, MiniGames.getInstance().getDropperArenaHandler());
}
/**

View File

@ -1,10 +1,8 @@
package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaHandler;
import net.knarcraft.minigames.util.DropperArenaStorageHelper;
import net.knarcraft.minigames.util.StringSanitizer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.HashMap;
@ -17,176 +15,18 @@ import java.util.logging.Level;
/**
* A handler that keeps track of all dropper arenas
*/
public class DropperArenaHandler {
private Map<UUID, DropperArena> arenas = new HashMap<>();
private Map<UUID, DropperArenaGroup> arenaGroups = new HashMap<>();
private Map<String, UUID> arenaNameLookup = new HashMap<>();
public class DropperArenaHandler extends ArenaHandler<DropperArena, DropperArenaGroup> {
/**
* Gets all arenas that are within a group
* Instantiates a new arena handler
*
* @return <p>All arenas in a group</p>
* @param playerRegistry <p>The registry keeping track of player sessions</p>
*/
public @NotNull Set<DropperArena> getArenasInAGroup() {
Set<DropperArena> arenas = new HashSet<>();
for (UUID arenaId : arenaGroups.keySet()) {
arenas.add(this.arenas.get(arenaId));
}
return arenas;
public DropperArenaHandler(DropperArenaPlayerRegistry playerRegistry) {
super(playerRegistry);
}
/**
* Gets a copy of all dropper groups
*
* @return <p>All dropper groups</p>
*/
public Set<DropperArenaGroup> 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 DropperArenaGroup 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 DropperArenaGroup arenaGroup) {
if (arenaGroup == null) {
// No need to remove something non-existing
if (!this.arenaGroups.containsKey(arenaId)) {
return;
}
// Remove the existing group
DropperArenaGroup 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 DropperArenaGroup getGroup(String groupName) {
String sanitized = StringSanitizer.sanitizeArenaName(groupName);
for (DropperArenaGroup 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 DropperArena 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 DropperArena 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 DropperArena 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, DropperArena> getArenas() {
return new HashMap<>(this.arenas);
}
/**
* Removes the given arena
*
* @param arena <p>The arena to remove</p>
*/
public void removeArena(@NotNull DropperArena arena) {
UUID arenaId = arena.getArenaId();
MiniGames.getInstance().getDropperArenaPlayerRegistry().removeForArena(arena);
this.arenas.remove(arenaId);
this.arenaNameLookup.remove(arena.getArenaNameSanitized());
this.arenaGroups.remove(arenaId);
if (!DropperArenaStorageHelper.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 {
DropperArenaStorageHelper.saveDropperArenaData(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
*/
@Override
public void saveGroups() {
try {
DropperArenaStorageHelper.saveDropperArenaGroups(new HashSet<>(this.arenaGroups.values()));
@ -197,18 +37,8 @@ public class DropperArenaHandler {
}
}
/**
* Loads all arenas and groups from disk
*/
public void load() {
loadArenas();
loadGroups();
}
/**
* Loads all dropper groups from disk
*/
private void loadGroups() {
@Override
protected void loadGroups() {
Set<DropperArenaGroup> arenaGroups = DropperArenaStorageHelper.loadDropperArenaGroups();
Map<UUID, DropperArenaGroup> arenaGroupMap = new HashMap<>();
for (DropperArenaGroup arenaGroup : arenaGroups) {
@ -219,9 +49,7 @@ public class DropperArenaHandler {
this.arenaGroups = arenaGroupMap;
}
/**
* Saves all current arenas to disk
*/
@Override
public void saveArenas() {
try {
DropperArenaStorageHelper.saveDropperArenas(this.arenas);
@ -232,10 +60,8 @@ public class DropperArenaHandler {
}
}
/**
* Loads all arenas from disk
*/
private void loadArenas() {
@Override
protected void loadArenas() {
this.arenas = DropperArenaStorageHelper.loadDropperArenas();
// Save a map from arena name to arena id for improved performance

View File

@ -1,5 +1,6 @@
package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -10,7 +11,7 @@ import java.util.UUID;
/**
* A registry to keep track of which players are playing in which arenas
*/
public class DropperArenaPlayerRegistry {
public class DropperArenaPlayerRegistry implements ArenaPlayerRegistry<DropperArena> {
private final Map<UUID, DropperArenaSession> arenaPlayers = new HashMap<>();

View File

@ -1,15 +1,18 @@
package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.Arena;
import net.knarcraft.minigames.arena.ArenaGameMode;
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
import net.knarcraft.minigames.util.MaterialHelper;
import net.knarcraft.minigames.util.ParkourArenaStorageHelper;
import net.knarcraft.minigames.util.StringSanitizer;
import org.bukkit.Location;
import org.bukkit.Material;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@ -23,7 +26,7 @@ import static net.knarcraft.minigames.util.InputValidationHelper.isInvalid;
/**
* A representation of one parkour arena
*/
public class ParkourArena {
public class ParkourArena implements Arena {
/**
* An unique and persistent identifier for this arena
@ -249,6 +252,21 @@ public class ParkourArena {
return StringSanitizer.sanitizeArenaName(this.getArenaName());
}
@Override
public boolean removeData() {
return ParkourArenaStorageHelper.removeParkourArenaData(getArenaId());
}
@Override
public boolean saveData() {
try {
ParkourArenaStorageHelper.saveParkourArenaData(getData());
return true;
} catch (IOException e) {
return false;
}
}
/**
* Sets the spawn location for this arena
*

View File

@ -3,19 +3,17 @@ package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaGroup;
import net.knarcraft.minigames.container.SerializableUUID;
import org.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 {
public class ParkourArenaGroup extends ArenaGroup<ParkourArena, ParkourArenaGroup> {
/**
* Instantiates a new parkour arena group
@ -23,7 +21,7 @@ public class ParkourArenaGroup extends ArenaGroup {
* @param groupName <p>The name of this group</p>
*/
public ParkourArenaGroup(@NotNull String groupName) {
super(groupName);
super(groupName, MiniGames.getInstance().getParkourArenaHandler());
}
/**
@ -34,71 +32,7 @@ public class ParkourArenaGroup extends ArenaGroup {
* @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 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, "The parkour group " + this.getGroupName() +
" contains the arena id " + anArenaId + " which is not a valid arena id!");
continue;
}
if (parkourArena.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 parkour 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;
super(groupId, groupName, arenas, MiniGames.getInstance().getParkourArenaHandler());
}
/**

View File

@ -1,10 +1,8 @@
package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaHandler;
import net.knarcraft.minigames.util.ParkourArenaStorageHelper;
import net.knarcraft.minigames.util.StringSanitizer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.HashMap;
@ -17,176 +15,18 @@ import java.util.logging.Level;
/**
* A handler that keeps track of all parkour 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<>();
public class ParkourArenaHandler extends ArenaHandler<ParkourArena, ParkourArenaGroup> {
/**
* Gets all arenas that are within a group
* Instantiates a new arena handler
*
* @return <p>All arenas in a group</p>
* @param playerRegistry <p>The registry keeping track of player sessions</p>
*/
public @NotNull Set<ParkourArena> getArenasInAGroup() {
Set<ParkourArena> arenas = new HashSet<>();
for (UUID arenaId : arenaGroups.keySet()) {
arenas.add(this.arenas.get(arenaId));
}
return arenas;
public ParkourArenaHandler(ParkourArenaPlayerRegistry playerRegistry) {
super(playerRegistry);
}
/**
* Gets a copy of all parkour groups
*
* @return <p>All parkour 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 parkour 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 (!ParkourArenaStorageHelper.removeParkourArenaData(arenaId)) {
MiniGames.log(Level.WARNING, "Unable to remove parkour 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 {
ParkourArenaStorageHelper.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 parkour groups to disk
*/
@Override
public void saveGroups() {
try {
ParkourArenaStorageHelper.saveParkourArenaGroups(new HashSet<>(this.arenaGroups.values()));
@ -197,18 +37,8 @@ public class ParkourArenaHandler {
}
}
/**
* Loads all arenas and groups from disk
*/
public void load() {
loadArenas();
loadGroups();
}
/**
* Loads all parkour groups from disk
*/
private void loadGroups() {
@Override
protected void loadGroups() {
Set<ParkourArenaGroup> arenaGroups = ParkourArenaStorageHelper.loadParkourArenaGroups();
Map<UUID, ParkourArenaGroup> arenaGroupMap = new HashMap<>();
for (ParkourArenaGroup arenaGroup : arenaGroups) {
@ -219,9 +49,7 @@ public class ParkourArenaHandler {
this.arenaGroups = arenaGroupMap;
}
/**
* Saves all current arenas to disk
*/
@Override
public void saveArenas() {
try {
ParkourArenaStorageHelper.saveParkourArenas(this.arenas);
@ -232,10 +60,8 @@ public class ParkourArenaHandler {
}
}
/**
* Loads all arenas from disk
*/
private void loadArenas() {
@Override
protected void loadArenas() {
this.arenas = ParkourArenaStorageHelper.loadParkourArenas();
// Save a map from arena name to arena id for improved performance

View File

@ -1,5 +1,6 @@
package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -10,7 +11,7 @@ import java.util.UUID;
/**
* A registry to keep track of which players are playing in which arenas
*/
public class ParkourArenaPlayerRegistry {
public class ParkourArenaPlayerRegistry implements ArenaPlayerRegistry<ParkourArena> {
private final Map<UUID, ParkourArenaSession> arenaPlayers = new HashMap<>();

View File

@ -25,8 +25,8 @@ public class CommandListener implements Listener {
}
List<String> allowedCommands = new ArrayList<>();
allowedCommands.add("/dropperleave");
allowedCommands.add("/dleave");
allowedCommands.add("/miniGamesLeave");
allowedCommands.add("/mLeave");
String message = event.getMessage();
if (!message.startsWith("/")) {
@ -34,7 +34,7 @@ public class CommandListener implements Listener {
}
for (String command : allowedCommands) {
if (message.equals(command)) {
if (message.equalsIgnoreCase(command)) {
return;
}
}

View File

@ -34,9 +34,9 @@ commands:
- Existing groups will be listed if used without an argument
- Supplying a group shows the group's arenas
description: Lists existing groups and their arenas
dropperReload:
miniGamesReload:
aliases:
- dreload
- mreload
permission: dropper.admin
usage: /<command>
description: Reloads all data from disk
@ -56,9 +56,9 @@ commands:
- inverted = A game-mode where the WASD buttons are inverted
- random = A game-mode where the WASD buttons toggle between being inverted and not
description: Used to join a dropper arena
dropperLeave:
miniGamesLeave:
aliases:
- dleave
- mleave
permission: dropper.join
usage: /<command>
description: Used to leave the current dropper arena
@ -83,28 +83,28 @@ commands:
usage: /<command> <arena>
description: Used to remove an existing dropper arena
permissions:
dropper.*:
minigames.*:
description: Gives all permissions
default: false
children:
- dropper.admin
dropper.admin:
- minigames.admin
minigames.admin:
description: Gives all permissions
default: op
children:
- dropper.join
- dropper.create
- dropper.edit
- dropper.remove
dropper.join:
description: Allows a player to participate in dropper arenas
- minigames.join
- minigames.create
- minigames.edit
- minigames.remove
minigames.join:
description: Allows a player to participate in mini-games arenas
default: true
dropper.create:
description: Allows a player to create a new dropper arena
minigames.create:
description: Allows a player to create a new mini-games arena
default: false
dropper.edit:
description: Allows a player to edit an existing dropper arena
minigames.edit:
description: Allows a player to edit an existing mini-games arena
default: false
dropper.remove:
description: Allows a player to remove a dropper arena
minigames.remove:
description: Allows a player to remove a mini-games arena
default: false