Parkour implementation safety save 3

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 22:49:31 +02:00
parent 1acaebb3bc
commit 8f77fc5910
12 changed files with 650 additions and 426 deletions

View File

@ -1,33 +0,0 @@
package net.knarcraft.minigames.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

@ -1,4 +1,8 @@
package net.knarcraft.minigames.arena; package net.knarcraft.minigames.arena;
/**
* An interface describing any arena game-mode
*/
public interface ArenaGameMode { public interface ArenaGameMode {
} }

View File

@ -1,7 +1,6 @@
package net.knarcraft.minigames.arena.dropper; package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.Arena;
import net.knarcraft.minigames.arena.ArenaGameMode; import net.knarcraft.minigames.arena.ArenaGameMode;
import net.knarcraft.minigames.arena.ArenaRecordsRegistry; import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
import net.knarcraft.minigames.config.DropperConfiguration; import net.knarcraft.minigames.config.DropperConfiguration;
@ -20,7 +19,7 @@ import static net.knarcraft.minigames.util.InputValidationHelper.isInvalid;
/** /**
* A representation of one dropper arena * A representation of one dropper arena
*/ */
public class DropperArena implements Arena { public class DropperArena {
/** /**
* An unique and persistent identifier for this arena * An unique and persistent identifier for this arena
@ -134,12 +133,20 @@ public class DropperArena implements Arena {
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;
} }
@ -196,7 +203,11 @@ public class DropperArena implements Arena {
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());
} }

View File

@ -1,7 +1,7 @@
package net.knarcraft.minigames.arena.dropper; package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.util.ArenaStorageHelper; import net.knarcraft.minigames.util.DropperArenaStorageHelper;
import net.knarcraft.minigames.util.StringSanitizer; import net.knarcraft.minigames.util.StringSanitizer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -163,7 +163,7 @@ public class DropperArenaHandler {
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.removeDropperArenaData(arenaId)) { if (!DropperArenaStorageHelper.removeDropperArenaData(arenaId)) {
MiniGames.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!");
} }
@ -177,7 +177,7 @@ public class DropperArenaHandler {
*/ */
public void saveData(UUID arenaId) { public void saveData(UUID arenaId) {
try { try {
ArenaStorageHelper.saveDropperArenaData(this.arenas.get(arenaId).getData()); DropperArenaStorageHelper.saveDropperArenaData(this.arenas.get(arenaId).getData());
} catch (IOException e) { } catch (IOException e) {
MiniGames.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!");
MiniGames.log(Level.SEVERE, e.getMessage()); MiniGames.log(Level.SEVERE, e.getMessage());
@ -189,7 +189,7 @@ public class DropperArenaHandler {
*/ */
public void saveGroups() { public void saveGroups() {
try { try {
ArenaStorageHelper.saveDropperArenaGroups(new HashSet<>(this.arenaGroups.values())); DropperArenaStorageHelper.saveDropperArenaGroups(new HashSet<>(this.arenaGroups.values()));
} catch (IOException e) { } catch (IOException e) {
MiniGames.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!");
@ -209,7 +209,7 @@ public class DropperArenaHandler {
* Loads all dropper groups from disk * Loads all dropper groups from disk
*/ */
private void loadGroups() { private void loadGroups() {
Set<DropperArenaGroup> arenaGroups = ArenaStorageHelper.loadDropperArenaGroups(); Set<DropperArenaGroup> arenaGroups = DropperArenaStorageHelper.loadDropperArenaGroups();
Map<UUID, DropperArenaGroup> arenaGroupMap = new HashMap<>(); Map<UUID, DropperArenaGroup> arenaGroupMap = new HashMap<>();
for (DropperArenaGroup arenaGroup : arenaGroups) { for (DropperArenaGroup arenaGroup : arenaGroups) {
for (UUID arenaId : arenaGroup.getArenas()) { for (UUID arenaId : arenaGroup.getArenas()) {
@ -224,7 +224,7 @@ public class DropperArenaHandler {
*/ */
public void saveArenas() { public void saveArenas() {
try { try {
ArenaStorageHelper.saveDropperArenas(this.arenas); DropperArenaStorageHelper.saveDropperArenas(this.arenas);
} catch (IOException e) { } catch (IOException e) {
MiniGames.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!");
@ -236,7 +236,7 @@ public class DropperArenaHandler {
* Loads all arenas from disk * Loads all arenas from disk
*/ */
private void loadArenas() { private void loadArenas() {
this.arenas = ArenaStorageHelper.loadDropperArenas(); this.arenas = DropperArenaStorageHelper.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

@ -3,6 +3,7 @@ package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaGameMode; import net.knarcraft.minigames.arena.ArenaGameMode;
import net.knarcraft.minigames.arena.ArenaRecordsRegistry; import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
import net.knarcraft.minigames.util.MaterialHelper;
import net.knarcraft.minigames.util.StringSanitizer; import net.knarcraft.minigames.util.StringSanitizer;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
@ -55,6 +56,11 @@ public class ParkourArena {
*/ */
private @Nullable Location winLocation; private @Nullable Location winLocation;
/**
* The names of the block types constituting this arena's kill plane
*/
private @Nullable Set<String> killPlaneBlockNames;
/** /**
* The block types constituting this arena's kill plane * The block types constituting this arena's kill plane
*/ */
@ -80,19 +86,23 @@ public class ParkourArena {
* @param spawnLocation <p>The location players spawn in when entering 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 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 parkour arena</p> * @param winBlockType <p>The material of the block players have to hit to win this parkour arena</p>
* @param winLocation <p>The location a player has to reach to win this arena</p>
* @param parkourArenaData <p>The arena data keeping track of which players have done what in this 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> * @param arenaHandler <p>The arena handler used for saving any changes</p>
*/ */
public ParkourArena(@NotNull UUID arenaId, @NotNull String arenaName, @NotNull Location spawnLocation, public ParkourArena(@NotNull UUID arenaId, @NotNull String arenaName, @NotNull Location spawnLocation,
@Nullable Location exitLocation, @NotNull Material winBlockType, @Nullable Location exitLocation, @NotNull Material winBlockType, @Nullable Location winLocation,
@Nullable Set<Material> killPlaneBlocks, @NotNull List<Location> checkpoints, @Nullable Set<String> killPlaneBlockNames, @NotNull List<Location> checkpoints,
@NotNull ParkourArenaData parkourArenaData, @NotNull ParkourArenaHandler arenaHandler) { @NotNull ParkourArenaData parkourArenaData, @NotNull ParkourArenaHandler arenaHandler) {
this.arenaId = arenaId; this.arenaId = arenaId;
this.arenaName = arenaName; this.arenaName = arenaName;
this.spawnLocation = spawnLocation; this.spawnLocation = spawnLocation;
this.exitLocation = exitLocation; this.exitLocation = exitLocation;
this.winBlockType = winBlockType; this.winBlockType = winBlockType;
this.killPlaneBlocks = killPlaneBlocks; this.winLocation = winLocation;
this.killPlaneBlockNames = killPlaneBlockNames;
this.killPlaneBlocks = this.killPlaneBlockNames == null ? null : MaterialHelper.loadMaterialList(
new ArrayList<>(killPlaneBlockNames));
this.checkpoints = checkpoints; this.checkpoints = checkpoints;
this.parkourArenaData = parkourArenaData; this.parkourArenaData = parkourArenaData;
this.parkourArenaHandler = arenaHandler; this.parkourArenaHandler = arenaHandler;
@ -208,6 +218,15 @@ public class ParkourArena {
} }
} }
/**
* Gets the names of the block types used for this parkour arena's kill plane
*
* @return <p>The names of the types of blocks that cause a loss</p>
*/
public @Nullable Set<String> getKillPlaneBlockNames() {
return this.killPlaneBlockNames;
}
/** /**
* Gets all checkpoint locations for this arena * Gets all checkpoint locations for this arena
* *
@ -309,7 +328,7 @@ public class ParkourArena {
if (isInvalid(newLocation)) { if (isInvalid(newLocation)) {
return false; return false;
} else { } else {
this.exitLocation = newLocation.clone(); this.winLocation = newLocation.clone();
parkourArenaHandler.saveArenas(); parkourArenaHandler.saveArenas();
return true; return true;
} }
@ -318,19 +337,10 @@ public class ParkourArena {
/** /**
* Sets the type of blocks constituting this arena's kill plane * Sets the type of blocks constituting this arena's kill plane
* *
* @param killPlaneBlocks <p>The blocks that will cause players to lose</p> * @param killPlaneBlockNames <p>The names of the blocks that will cause players to lose</p>
* @return <p>True if successfully changed</p>
*/ */
public boolean setKillPlaneBlocks(@NotNull Set<Material> killPlaneBlocks) { public void setKillPlaneBlocks(@NotNull Set<String> killPlaneBlockNames) {
for (Material material : killPlaneBlocks) { this.killPlaneBlocks = MaterialHelper.loadMaterialList(new ArrayList<>(killPlaneBlockNames));
// Make sure no nulls have entered the set
if (material == null) {
return false;
}
}
this.killPlaneBlocks = new HashSet<>(killPlaneBlocks);
return true;
} }
/** /**

View File

@ -1,7 +1,7 @@
package net.knarcraft.minigames.arena.parkour; package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.util.ArenaStorageHelper; import net.knarcraft.minigames.util.ParkourArenaStorageHelper;
import net.knarcraft.minigames.util.StringSanitizer; import net.knarcraft.minigames.util.StringSanitizer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -163,7 +163,7 @@ public class ParkourArenaHandler {
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.removeParkourArenaData(arenaId)) { if (!ParkourArenaStorageHelper.removeParkourArenaData(arenaId)) {
MiniGames.log(Level.WARNING, "Unable to remove parkour arena data file " + arenaId + ".yml. " + MiniGames.log(Level.WARNING, "Unable to remove parkour arena data file " + arenaId + ".yml. " +
"You must remove it manually!"); "You must remove it manually!");
} }
@ -177,7 +177,7 @@ public class ParkourArenaHandler {
*/ */
public void saveData(UUID arenaId) { public void saveData(UUID arenaId) {
try { try {
ArenaStorageHelper.saveParkourArenaData(this.arenas.get(arenaId).getData()); ParkourArenaStorageHelper.saveParkourArenaData(this.arenas.get(arenaId).getData());
} catch (IOException e) { } catch (IOException e) {
MiniGames.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!");
MiniGames.log(Level.SEVERE, e.getMessage()); MiniGames.log(Level.SEVERE, e.getMessage());
@ -189,7 +189,7 @@ public class ParkourArenaHandler {
*/ */
public void saveGroups() { public void saveGroups() {
try { try {
ArenaStorageHelper.saveParkourArenaGroups(new HashSet<>(this.arenaGroups.values())); ParkourArenaStorageHelper.saveParkourArenaGroups(new HashSet<>(this.arenaGroups.values()));
} catch (IOException e) { } catch (IOException e) {
MiniGames.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!");
@ -209,7 +209,7 @@ public class ParkourArenaHandler {
* Loads all parkour groups from disk * Loads all parkour groups from disk
*/ */
private void loadGroups() { private void loadGroups() {
Set<ParkourArenaGroup> arenaGroups = ArenaStorageHelper.loadParkourArenaGroups(); Set<ParkourArenaGroup> arenaGroups = ParkourArenaStorageHelper.loadParkourArenaGroups();
Map<UUID, ParkourArenaGroup> arenaGroupMap = new HashMap<>(); Map<UUID, ParkourArenaGroup> arenaGroupMap = new HashMap<>();
for (ParkourArenaGroup arenaGroup : arenaGroups) { for (ParkourArenaGroup arenaGroup : arenaGroups) {
for (UUID arenaId : arenaGroup.getArenas()) { for (UUID arenaId : arenaGroup.getArenas()) {
@ -224,7 +224,7 @@ public class ParkourArenaHandler {
*/ */
public void saveArenas() { public void saveArenas() {
try { try {
ArenaStorageHelper.saveParkourArenas(this.arenas); ParkourArenaStorageHelper.saveParkourArenas(this.arenas);
} catch (IOException e) { } catch (IOException e) {
MiniGames.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!");
@ -236,7 +236,7 @@ public class ParkourArenaHandler {
* Loads all arenas from disk * Loads all arenas from disk
*/ */
private void loadArenas() { private void loadArenas() {
this.arenas = ArenaStorageHelper.loadParkourArenas(); this.arenas = ParkourArenaStorageHelper.loadParkourArenas();
// 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,20 +1,13 @@
package net.knarcraft.minigames.config; package net.knarcraft.minigames.config;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.util.MaterialHelper;
import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Tag;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* A configuration for a mini-game * A configuration for a mini-game
@ -51,52 +44,9 @@ public abstract class MiniGameConfiguration {
/** /**
* Loads the materials specified in the block whitelist * Loads the materials specified in the block whitelist
*/ */
protected @NotNull Set<Material> loadMaterialList(@NotNull String path) { public @NotNull Set<Material> loadMaterialList(@NotNull String path) {
Set<Material> parsedMaterials = new HashSet<>();
List<?> blockWhitelist = configuration.getList(path, new ArrayList<>()); List<?> blockWhitelist = configuration.getList(path, new ArrayList<>());
for (Object value : blockWhitelist) { return MaterialHelper.loadMaterialList(blockWhitelist);
if (!(value instanceof String string)) {
continue;
}
// Try to parse a material tag first
if (parseMaterialTag(parsedMaterials, string)) {
continue;
}
// Try to parse a material name
Material matched = Material.matchMaterial(string);
if (matched != null) {
parsedMaterials.add(matched);
} else {
MiniGames.log(Level.WARNING, "Unable to parse: " + string);
}
}
return parsedMaterials;
}
/**
* Tries to parse the material tag in the specified material name
*
* @param targetSet <p>The set all parsed materials should be added to</p>
* @param materialName <p>The material name that might be a material tag</p>
* @return <p>True if a tag was found</p>
*/
protected boolean parseMaterialTag(@NotNull Set<Material> targetSet, @NotNull String materialName) {
Pattern pattern = Pattern.compile("^\\+([a-zA-Z_]+)");
Matcher matcher = pattern.matcher(materialName);
if (matcher.find()) {
// The material is a material tag
Tag<Material> tag = Bukkit.getTag(Tag.REGISTRY_BLOCKS, NamespacedKey.minecraft(
matcher.group(1).toLowerCase()), Material.class);
if (tag != null) {
targetSet.addAll(tag.getValues());
} else {
MiniGames.log(Level.WARNING, "Unable to parse: " + materialName);
}
return true;
}
return false;
} }
} }

View File

@ -1,305 +1,18 @@
package net.knarcraft.minigames.util; package net.knarcraft.minigames.util;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaGameMode;
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
import net.knarcraft.minigames.arena.dropper.DropperArena;
import net.knarcraft.minigames.arena.dropper.DropperArenaData;
import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
import net.knarcraft.minigames.arena.dropper.DropperArenaRecordsRegistry;
import net.knarcraft.minigames.arena.dropper.DropperArenaStorageKey;
import net.knarcraft.minigames.arena.parkour.ParkourArena;
import net.knarcraft.minigames.arena.parkour.ParkourArenaData;
import net.knarcraft.minigames.arena.parkour.ParkourArenaStorageKey;
import net.knarcraft.minigames.container.SerializableMaterial;
import net.knarcraft.minigames.container.SerializableUUID;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
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.UUID;
import java.util.logging.Level; import java.util.logging.Level;
/**
* A helper class for saving and loading arenas
*/
public final class ArenaStorageHelper { public final class ArenaStorageHelper {
private final static File dataFolder = MiniGames.getInstance().getDataFolder();
private final static String dropperArenasConfigurationSection = "dropperArenas";
private final static String dropperGroupsConfigurationSection = "dropperGroups";
private final static String parkourArenasConfigurationSection = "parkourArenas";
private final static String parkourGroupsConfigurationSection = "parkourGroups";
private static final File arenaFile = new File(dataFolder, "arenas.yml");
private static final File groupFile = new File(dataFolder, "groups.yml");
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() {
} }
/**
* Saves the given dropper arena groups
*
* @param arenaGroups <p>The arena groups to save</p>
* @throws IOException <p>If unable to write to the file</p>
*/
public static void saveDropperArenaGroups(@NotNull Set<DropperArenaGroup> arenaGroups) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
ConfigurationSection groupSection = configuration.createSection(dropperGroupsConfigurationSection);
for (DropperArenaGroup arenaGroup : arenaGroups) {
groupSection.set(arenaGroup.getGroupId().toString(), arenaGroup);
}
configuration.save(groupFile);
}
/**
* Loads all existing dropper arena groups
*
* @return <p>The loaded arena groups</p>
*/
public static @NotNull Set<DropperArenaGroup> loadDropperArenaGroups() {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(groupFile);
ConfigurationSection groupSection = configuration.getConfigurationSection(dropperGroupsConfigurationSection);
//If no such section exists, it must be the case that there is no data to load
if (groupSection == null) {
return new HashSet<>();
}
Set<DropperArenaGroup> arenaGroups = new HashSet<>();
for (String sectionName : groupSection.getKeys(false)) {
arenaGroups.add((DropperArenaGroup) groupSection.get(sectionName));
}
return arenaGroups;
}
/**
* Saves the given arenas
*
* @param arenas <p>The arenas to save</p>
* @throws IOException <p>If unable to write to the file</p>
*/
public static void saveDropperArenas(@NotNull Map<UUID, DropperArena> arenas) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
ConfigurationSection arenaSection = configuration.createSection(dropperArenasConfigurationSection);
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
// must be stored as well
@NotNull ConfigurationSection configSection = arenaSection.createSection(arena.getArenaId().toString());
configSection.set(DropperArenaStorageKey.ID.getKey(), new SerializableUUID(arena.getArenaId()));
configSection.set(DropperArenaStorageKey.NAME.getKey(), arena.getArenaName());
configSection.set(DropperArenaStorageKey.SPAWN_LOCATION.getKey(), arena.getSpawnLocation());
configSection.set(DropperArenaStorageKey.EXIT_LOCATION.getKey(), arena.getExitLocation());
configSection.set(DropperArenaStorageKey.PLAYER_VERTICAL_VELOCITY.getKey(), arena.getPlayerVerticalVelocity());
configSection.set(DropperArenaStorageKey.PLAYER_HORIZONTAL_VELOCITY.getKey(), arena.getPlayerHorizontalVelocity());
configSection.set(DropperArenaStorageKey.WIN_BLOCK_TYPE.getKey(), new SerializableMaterial(arena.getWinBlockType()));
saveDropperArenaData(arena.getData());
}
configuration.save(arenaFile);
}
/**
* Saves the given arenas
*
* @param arenas <p>The arenas to save</p>
* @throws IOException <p>If unable to write to the file</p>
*/
public static void saveParkourArenas(@NotNull Map<UUID, ParkourArena> arenas) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
ConfigurationSection arenaSection = configuration.createSection(parkourArenasConfigurationSection);
for (ParkourArena 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
// must be stored as well
@NotNull ConfigurationSection configSection = arenaSection.createSection(arena.getArenaId().toString());
configSection.set(ParkourArenaStorageKey.ID.getKey(), new SerializableUUID(arena.getArenaId()));
configSection.set(ParkourArenaStorageKey.NAME.getKey(), arena.getArenaName());
configSection.set(ParkourArenaStorageKey.SPAWN_LOCATION.getKey(), arena.getSpawnLocation());
configSection.set(ParkourArenaStorageKey.EXIT_LOCATION.getKey(), arena.getExitLocation());
configSection.set(ParkourArenaStorageKey.WIN_BLOCK_TYPE.getKey(), new SerializableMaterial(arena.getWinBlockType()));
configSection.set(ParkourArenaStorageKey.WIN_LOCATION.getKey(), arena.getWinLocation());
configSection.set(ParkourArenaStorageKey.KILL_PLANE_BLOCKS.getKey(), arena.getKillPlaneBlocks());
configSection.set(ParkourArenaStorageKey.CHECKPOINTS.getKey(), arena.getCheckpoints());
saveParkourArenaData(arena.getData());
}
configuration.save(arenaFile);
}
/**
* Loads all arenas
*
* @return <p>The loaded arenas, or null if the arenas configuration section is missing.</p>
*/
public static @NotNull Map<UUID, DropperArena> loadDropperArenas() {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(arenaFile);
ConfigurationSection arenaSection = configuration.getConfigurationSection(dropperArenasConfigurationSection);
//If no such section exists, it must be the case that there is no data to load
if (arenaSection == null) {
return new HashMap<>();
}
Map<UUID, DropperArena> loadedArenas = new HashMap<>();
for (String sectionName : arenaSection.getKeys(false)) {
ConfigurationSection configurationSection = arenaSection.getConfigurationSection(sectionName);
//I'm not sure whether this could actually happen
if (configurationSection == null) {
continue;
}
DropperArena arena = loadDropperArena(configurationSection);
if (arena != null) {
loadedArenas.put(arena.getArenaId(), arena);
}
}
return loadedArenas;
}
/**
* Loads an arena from the given configuration section
*
* @param configurationSection <p>The configuration section containing arena data</p>
* @return <p>The loaded arena, or null if invalid</p>
*/
private static @Nullable DropperArena loadDropperArena(@NotNull ConfigurationSection configurationSection) {
UUID arenaId = ((SerializableUUID) configurationSection.get(DropperArenaStorageKey.ID.getKey(),
new SerializableUUID(UUID.randomUUID()))).getRawValue();
String arenaName = configurationSection.getString(DropperArenaStorageKey.NAME.getKey());
Location spawnLocation = (Location) configurationSection.get(DropperArenaStorageKey.SPAWN_LOCATION.getKey());
Location exitLocation = (Location) configurationSection.get(DropperArenaStorageKey.EXIT_LOCATION.getKey());
double verticalVelocity = configurationSection.getDouble(DropperArenaStorageKey.PLAYER_VERTICAL_VELOCITY.getKey());
float horizontalVelocity = sanitizeHorizontalVelocity((float) configurationSection.getDouble(
DropperArenaStorageKey.PLAYER_HORIZONTAL_VELOCITY.getKey()));
SerializableMaterial winBlockType = (SerializableMaterial) configurationSection.get(
DropperArenaStorageKey.WIN_BLOCK_TYPE.getKey());
if (arenaName == null || spawnLocation == null) {
MiniGames.log(Level.SEVERE, "Could not load the arena at configuration " +
"section " + configurationSection.getName() + ". Please check the arenas storage file for issues.");
return null;
}
if (winBlockType == null) {
winBlockType = new SerializableMaterial(Material.WATER);
}
DropperArenaData arenaData = loadDropperArenaData(arenaId);
if (arenaData == null) {
MiniGames.log(Level.SEVERE, "Unable to load arena data for " + arenaId);
Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries = new HashMap<>();
for (ArenaGameMode arenaGameMode : DropperArenaGameMode.values()) {
recordRegistries.put(arenaGameMode, new DropperArenaRecordsRegistry(arenaId));
}
arenaData = new DropperArenaData(arenaId, recordRegistries, new HashMap<>());
}
return new DropperArena(arenaId, arenaName, spawnLocation, exitLocation, verticalVelocity, horizontalVelocity,
winBlockType.getRawValue(), arenaData, MiniGames.getInstance().getDropperArenaHandler());
}
/**
* Stores the given arena data to a file
*
* @param arenaData <p>The arena data to store</p>
*/
public static void saveParkourArenaData(@NotNull ParkourArenaData arenaData) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
configuration.set(ParkourArenaStorageKey.DATA.getKey(), arenaData);
configuration.save(getParkourArenaDataFile(arenaData.getArenaId()));
}
/**
* Stores the given arena data to a file
*
* @param arenaData <p>The arena data to store</p>
*/
public static void saveDropperArenaData(@NotNull DropperArenaData arenaData) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
configuration.set(DropperArenaStorageKey.DATA.getKey(), arenaData);
configuration.save(getDropperArenaDataFile(arenaData.getArenaId()));
}
/**
* Loads arena data for the given arena id
*
* @param arenaId <p>The id of the arena to get data for</p>
* @return <p>The loaded arena data</p>
*/
private static @Nullable DropperArenaData loadDropperArenaData(@NotNull UUID arenaId) {
File arenaDataFile = getDropperArenaDataFile(arenaId);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(arenaDataFile);
return (DropperArenaData) configuration.get(DropperArenaStorageKey.DATA.getKey());
}
/**
* Loads arena data for the given arena id
*
* @param arenaId <p>The id of the arena to get data for</p>
* @return <p>The loaded arena data</p>
*/
private static @Nullable ParkourArenaData loadParkourArenaData(@NotNull UUID arenaId) {
File arenaDataFile = getParkourArenaDataFile(arenaId);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(arenaDataFile);
return (ParkourArenaData) configuration.get(ParkourArenaStorageKey.DATA.getKey());
}
/**
* Removes data for the arena with the given id
*
* @param arenaId <p>The id of the arena to remove data for</p>
* @return <p>True if the data was successfully removed</p>
*/
public static boolean removeDropperArenaData(@NotNull UUID arenaId) {
return getDropperArenaDataFile(arenaId).delete();
}
/**
* Removes data for the arena with the given id
*
* @param arenaId <p>The id of the arena to remove data for</p>
* @return <p>True if the data was successfully removed</p>
*/
public static boolean removeParkourArenaData(@NotNull UUID arenaId) {
return getParkourArenaDataFile(arenaId).delete();
}
/**
* Gets the file used to store the given arena id's data
*
* @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>
*/
private static @NotNull File getDropperArenaDataFile(@NotNull UUID arenaId) {
return getArenaDataFile(dropperArenaDataFolder, arenaId);
}
/**
* Gets the file used to store the given arena id's data
*
* @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>
*/
private static @NotNull File getParkourArenaDataFile(@NotNull UUID arenaId) {
return getArenaDataFile(parkourArenaDataFolder, arenaId);
}
/** /**
* Gets the file used to store the given arena id's data * Gets the file used to store the given arena id's data
* *
@ -307,7 +20,7 @@ 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(File root, @NotNull UUID arenaId) { static @NotNull File getArenaDataFile(File root, @NotNull UUID arenaId) {
File arenaDataFile = new File(root, arenaId + ".yml"); File arenaDataFile = new File(root, arenaId + ".yml");
if (!root.exists() && !root.mkdirs()) { if (!root.exists() && !root.mkdirs()) {
MiniGames.log(Level.SEVERE, "Unable to create the arena data directories"); MiniGames.log(Level.SEVERE, "Unable to create the arena data directories");
@ -315,20 +28,4 @@ public final class ArenaStorageHelper {
return arenaDataFile; return arenaDataFile;
} }
/**
* Sanitizes the given horizontal velocity to make sure it doesn't leave its bounds
*
* @param horizontalVelocity <p>The horizontal velocity to sanitize</p>
* @return <p>The sanitized horizontal velocity</p>
*/
private static float sanitizeHorizontalVelocity(float horizontalVelocity) {
if (horizontalVelocity < -1) {
return -1;
} else if (horizontalVelocity > 1) {
return 1;
} else {
return horizontalVelocity;
}
}
} }

View File

@ -0,0 +1,257 @@
package net.knarcraft.minigames.util;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaGameMode;
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
import net.knarcraft.minigames.arena.dropper.DropperArena;
import net.knarcraft.minigames.arena.dropper.DropperArenaData;
import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
import net.knarcraft.minigames.arena.dropper.DropperArenaRecordsRegistry;
import net.knarcraft.minigames.arena.dropper.DropperArenaStorageKey;
import net.knarcraft.minigames.container.SerializableMaterial;
import net.knarcraft.minigames.container.SerializableUUID;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
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;
import static net.knarcraft.minigames.util.ArenaStorageHelper.getArenaDataFile;
/**
* A helper class for saving and loading arenas
*/
public final class DropperArenaStorageHelper {
private final static File dataFolder = MiniGames.getInstance().getDataFolder();
private final static String dropperArenasConfigurationSection = "dropperArenas";
private final static String dropperGroupsConfigurationSection = "dropperGroups";
private static final File dropperArenaFile = new File(dataFolder, "dropper_arenas.yml");
private static final File dropperGroupFile = new File(dataFolder, "dropper_groups.yml");
private static final File dropperArenaDataFolder = new File(dataFolder, "dropper_arena_data");
private DropperArenaStorageHelper() {
}
/**
* Saves the given dropper arena groups
*
* @param arenaGroups <p>The arena groups to save</p>
* @throws IOException <p>If unable to write to the file</p>
*/
public static void saveDropperArenaGroups(@NotNull Set<DropperArenaGroup> arenaGroups) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
ConfigurationSection groupSection = configuration.createSection(dropperGroupsConfigurationSection);
for (DropperArenaGroup arenaGroup : arenaGroups) {
groupSection.set(arenaGroup.getGroupId().toString(), arenaGroup);
}
configuration.save(dropperGroupFile);
}
/**
* Loads all existing dropper arena groups
*
* @return <p>The loaded arena groups</p>
*/
public static @NotNull Set<DropperArenaGroup> loadDropperArenaGroups() {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(dropperGroupFile);
ConfigurationSection groupSection = configuration.getConfigurationSection(dropperGroupsConfigurationSection);
//If no such section exists, it must be the case that there is no data to load
if (groupSection == null) {
return new HashSet<>();
}
Set<DropperArenaGroup> arenaGroups = new HashSet<>();
for (String sectionName : groupSection.getKeys(false)) {
arenaGroups.add((DropperArenaGroup) groupSection.get(sectionName));
}
return arenaGroups;
}
/**
* Saves the given arenas
*
* @param arenas <p>The arenas to save</p>
* @throws IOException <p>If unable to write to the file</p>
*/
public static void saveDropperArenas(@NotNull Map<UUID, DropperArena> arenas) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
ConfigurationSection arenaSection = configuration.createSection(dropperArenasConfigurationSection);
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
// must be stored as well
@NotNull ConfigurationSection configSection = arenaSection.createSection(arena.getArenaId().toString());
configSection.set(DropperArenaStorageKey.ID.getKey(), new SerializableUUID(arena.getArenaId()));
configSection.set(DropperArenaStorageKey.NAME.getKey(), arena.getArenaName());
configSection.set(DropperArenaStorageKey.SPAWN_LOCATION.getKey(), arena.getSpawnLocation());
configSection.set(DropperArenaStorageKey.EXIT_LOCATION.getKey(), arena.getExitLocation());
configSection.set(DropperArenaStorageKey.PLAYER_VERTICAL_VELOCITY.getKey(), arena.getPlayerVerticalVelocity());
configSection.set(DropperArenaStorageKey.PLAYER_HORIZONTAL_VELOCITY.getKey(), arena.getPlayerHorizontalVelocity());
configSection.set(DropperArenaStorageKey.WIN_BLOCK_TYPE.getKey(), new SerializableMaterial(arena.getWinBlockType()));
saveDropperArenaData(arena.getData());
}
configuration.save(dropperArenaFile);
}
/**
* Loads all arenas
*
* @return <p>The loaded arenas, or null if the arenas configuration section is missing.</p>
*/
public static @NotNull Map<UUID, DropperArena> loadDropperArenas() {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(dropperArenaFile);
ConfigurationSection arenaSection = configuration.getConfigurationSection(dropperArenasConfigurationSection);
//If no such section exists, it must be the case that there is no data to load
if (arenaSection == null) {
return new HashMap<>();
}
Map<UUID, DropperArena> loadedArenas = new HashMap<>();
for (String sectionName : arenaSection.getKeys(false)) {
ConfigurationSection configurationSection = arenaSection.getConfigurationSection(sectionName);
//I'm not sure whether this could actually happen
if (configurationSection == null) {
continue;
}
DropperArena arena = loadDropperArena(configurationSection);
if (arena != null) {
loadedArenas.put(arena.getArenaId(), arena);
}
}
return loadedArenas;
}
/**
* Loads an arena from the given configuration section
*
* @param configurationSection <p>The configuration section containing arena data</p>
* @return <p>The loaded arena, or null if invalid</p>
*/
private static @Nullable DropperArena loadDropperArena(@NotNull ConfigurationSection configurationSection) {
UUID arenaId = ((SerializableUUID) configurationSection.get(DropperArenaStorageKey.ID.getKey(),
new SerializableUUID(UUID.randomUUID()))).getRawValue();
String arenaName = configurationSection.getString(DropperArenaStorageKey.NAME.getKey());
Location spawnLocation = (Location) configurationSection.get(DropperArenaStorageKey.SPAWN_LOCATION.getKey());
Location exitLocation = (Location) configurationSection.get(DropperArenaStorageKey.EXIT_LOCATION.getKey());
double verticalVelocity = configurationSection.getDouble(DropperArenaStorageKey.PLAYER_VERTICAL_VELOCITY.getKey());
float horizontalVelocity = sanitizeHorizontalVelocity((float) configurationSection.getDouble(
DropperArenaStorageKey.PLAYER_HORIZONTAL_VELOCITY.getKey()));
SerializableMaterial winBlockType = (SerializableMaterial) configurationSection.get(
DropperArenaStorageKey.WIN_BLOCK_TYPE.getKey());
if (arenaName == null || spawnLocation == null) {
MiniGames.log(Level.SEVERE, "Could not load the arena at configuration " +
"section " + configurationSection.getName() + ". Please check the dropper_arenas storage file for issues.");
return null;
}
if (winBlockType == null) {
winBlockType = new SerializableMaterial(Material.WATER);
}
// Generate new, empty arena data if not available
DropperArenaData arenaData = loadDropperArenaData(arenaId);
if (arenaData == null) {
MiniGames.log(Level.SEVERE, "Unable to load arena data for dropper arena" + arenaId);
arenaData = getEmptyDropperData(arenaId);
}
return new DropperArena(arenaId, arenaName, spawnLocation, exitLocation, verticalVelocity, horizontalVelocity,
winBlockType.getRawValue(), arenaData, MiniGames.getInstance().getDropperArenaHandler());
}
/**
* Gets empty dropper data
*
* @param arenaId <p>The id to get parkour data for</p>
* @return <p>Empty parkour data</p>
*/
private static @NotNull DropperArenaData getEmptyDropperData(@NotNull UUID arenaId) {
Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries = new HashMap<>();
Map<ArenaGameMode, Set<UUID>> playersCompleted = new HashMap<>();
for (ArenaGameMode arenaGameMode : DropperArenaGameMode.values()) {
recordRegistries.put(arenaGameMode, new DropperArenaRecordsRegistry(arenaId));
playersCompleted.put(arenaGameMode, new HashSet<>());
}
return new DropperArenaData(arenaId, recordRegistries, playersCompleted);
}
/**
* Stores the given arena data to a file
*
* @param arenaData <p>The arena data to store</p>
*/
public static void saveDropperArenaData(@NotNull DropperArenaData arenaData) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
configuration.set(DropperArenaStorageKey.DATA.getKey(), arenaData);
configuration.save(getDropperArenaDataFile(arenaData.getArenaId()));
}
/**
* Loads arena data for the given arena id
*
* @param arenaId <p>The id of the arena to get data for</p>
* @return <p>The loaded arena data</p>
*/
private static @Nullable DropperArenaData loadDropperArenaData(@NotNull UUID arenaId) {
File arenaDataFile = getDropperArenaDataFile(arenaId);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(arenaDataFile);
return (DropperArenaData) configuration.get(DropperArenaStorageKey.DATA.getKey());
}
/**
* Removes data for the arena with the given id
*
* @param arenaId <p>The id of the arena to remove data for</p>
* @return <p>True if the data was successfully removed</p>
*/
public static boolean removeDropperArenaData(@NotNull UUID arenaId) {
return getDropperArenaDataFile(arenaId).delete();
}
/**
* Gets the file used to store the given arena id's data
*
* @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>
*/
private static @NotNull File getDropperArenaDataFile(@NotNull UUID arenaId) {
return getArenaDataFile(dropperArenaDataFolder, arenaId);
}
/**
* Sanitizes the given horizontal velocity to make sure it doesn't leave its bounds
*
* @param horizontalVelocity <p>The horizontal velocity to sanitize</p>
* @return <p>The sanitized horizontal velocity</p>
*/
private static float sanitizeHorizontalVelocity(float horizontalVelocity) {
if (horizontalVelocity < -1) {
return -1;
} else if (horizontalVelocity > 1) {
return 1;
} else {
return horizontalVelocity;
}
}
}

View File

@ -0,0 +1,76 @@
package net.knarcraft.minigames.util;
import net.knarcraft.minigames.MiniGames;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Tag;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A helper class for dealing with and parsing materials
*/
public final class MaterialHelper {
private MaterialHelper() {
}
/**
* Loads the materials specified in the block whitelist
*/
public static @NotNull Set<Material> loadMaterialList(@NotNull List<?> materials) {
Set<Material> parsedMaterials = new HashSet<>();
for (Object value : materials) {
if (!(value instanceof String string)) {
continue;
}
// Try to parse a material tag first
if (parseMaterialTag(parsedMaterials, string)) {
continue;
}
// Try to parse a material name
Material matched = Material.matchMaterial(string);
if (matched != null) {
parsedMaterials.add(matched);
} else {
MiniGames.log(Level.WARNING, "Unable to parse: " + string);
}
}
return parsedMaterials;
}
/**
* Tries to parse the material tag in the specified material name
*
* @param targetSet <p>The set all parsed materials should be added to</p>
* @param materialName <p>The material name that might be a material tag</p>
* @return <p>True if a tag was found</p>
*/
private static boolean parseMaterialTag(@NotNull Set<Material> targetSet, @NotNull String materialName) {
Pattern pattern = Pattern.compile("^\\+([a-zA-Z_]+)");
Matcher matcher = pattern.matcher(materialName);
if (matcher.find()) {
// The material is a material tag
Tag<Material> tag = Bukkit.getTag(Tag.REGISTRY_BLOCKS, NamespacedKey.minecraft(
matcher.group(1).toLowerCase()), Material.class);
if (tag != null) {
targetSet.addAll(tag.getValues());
} else {
MiniGames.log(Level.WARNING, "Unable to parse: " + materialName);
}
return true;
}
return false;
}
}

View File

@ -0,0 +1,252 @@
package net.knarcraft.minigames.util;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaGameMode;
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
import net.knarcraft.minigames.arena.parkour.ParkourArena;
import net.knarcraft.minigames.arena.parkour.ParkourArenaData;
import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode;
import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
import net.knarcraft.minigames.arena.parkour.ParkourArenaRecordsRegistry;
import net.knarcraft.minigames.arena.parkour.ParkourArenaStorageKey;
import net.knarcraft.minigames.container.SerializableMaterial;
import net.knarcraft.minigames.container.SerializableUUID;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import static net.knarcraft.minigames.util.ArenaStorageHelper.getArenaDataFile;
/**
* A helper class for saving and loading parkour arenas
*/
public final class ParkourArenaStorageHelper {
private ParkourArenaStorageHelper() {
}
private final static File dataFolder = MiniGames.getInstance().getDataFolder();
private final static String parkourArenasConfigurationSection = "parkourArenas";
private final static String parkourGroupsConfigurationSection = "parkourGroups";
private static final File parkourGroupFile = new File(dataFolder, "parkour_groups.yml");
private static final File parkourArenaFile = new File(dataFolder, "parkour_arenas.yml");
private static final File parkourArenaDataFolder = new File(dataFolder, "parkour_arena_data");
/**
* Saves the given parkour arena groups
*
* @param arenaGroups <p>The arena groups to save</p>
* @throws IOException <p>If unable to write to the file</p>
*/
public static void saveParkourArenaGroups(@NotNull Set<ParkourArenaGroup> arenaGroups) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
ConfigurationSection groupSection = configuration.createSection(parkourGroupsConfigurationSection);
for (ParkourArenaGroup arenaGroup : arenaGroups) {
groupSection.set(arenaGroup.getGroupId().toString(), arenaGroup);
}
configuration.save(parkourGroupFile);
}
/**
* Loads all existing parkour arena groups
*
* @return <p>The loaded arena groups</p>
*/
public static @NotNull Set<ParkourArenaGroup> loadParkourArenaGroups() {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(parkourGroupFile);
ConfigurationSection groupSection = configuration.getConfigurationSection(parkourGroupsConfigurationSection);
//If no such section exists, it must be the case that there is no data to load
if (groupSection == null) {
return new HashSet<>();
}
Set<ParkourArenaGroup> arenaGroups = new HashSet<>();
for (String sectionName : groupSection.getKeys(false)) {
arenaGroups.add((ParkourArenaGroup) groupSection.get(sectionName));
}
return arenaGroups;
}
/**
* Saves the given arenas
*
* @param arenas <p>The arenas to save</p>
* @throws IOException <p>If unable to write to the file</p>
*/
public static void saveParkourArenas(@NotNull Map<UUID, ParkourArena> arenas) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
ConfigurationSection arenaSection = configuration.createSection(parkourArenasConfigurationSection);
for (ParkourArena 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
// must be stored as well
@NotNull ConfigurationSection configSection = arenaSection.createSection(arena.getArenaId().toString());
configSection.set(ParkourArenaStorageKey.ID.getKey(), new SerializableUUID(arena.getArenaId()));
configSection.set(ParkourArenaStorageKey.NAME.getKey(), arena.getArenaName());
configSection.set(ParkourArenaStorageKey.SPAWN_LOCATION.getKey(), arena.getSpawnLocation());
configSection.set(ParkourArenaStorageKey.EXIT_LOCATION.getKey(), arena.getExitLocation());
configSection.set(ParkourArenaStorageKey.WIN_BLOCK_TYPE.getKey(), new SerializableMaterial(arena.getWinBlockType()));
configSection.set(ParkourArenaStorageKey.WIN_LOCATION.getKey(), arena.getWinLocation());
configSection.set(ParkourArenaStorageKey.KILL_PLANE_BLOCKS.getKey(), arena.getKillPlaneBlockNames());
configSection.set(ParkourArenaStorageKey.CHECKPOINTS.getKey(), arena.getCheckpoints());
saveParkourArenaData(arena.getData());
}
configuration.save(parkourArenaFile);
}
/**
* Loads all arenas
*
* @return <p>The loaded arenas, or null if the arenas configuration section is missing.</p>
*/
public static @NotNull Map<UUID, ParkourArena> loadParkourArenas() {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(parkourArenaFile);
ConfigurationSection arenaSection = configuration.getConfigurationSection(parkourArenasConfigurationSection);
//If no such section exists, it must be the case that there is no data to load
if (arenaSection == null) {
return new HashMap<>();
}
Map<UUID, ParkourArena> loadedArenas = new HashMap<>();
for (String sectionName : arenaSection.getKeys(false)) {
ConfigurationSection configurationSection = arenaSection.getConfigurationSection(sectionName);
//I'm not sure whether this could actually happen
if (configurationSection == null) {
continue;
}
ParkourArena arena = loadParkourArena(configurationSection);
if (arena != null) {
loadedArenas.put(arena.getArenaId(), arena);
}
}
return loadedArenas;
}
/**
* Loads an arena from the given configuration section
*
* @param configurationSection <p>The configuration section containing arena data</p>
* @return <p>The loaded arena, or null if invalid</p>
*/
@SuppressWarnings("unchecked")
private static @Nullable ParkourArena loadParkourArena(@NotNull ConfigurationSection configurationSection) {
UUID arenaId = ((SerializableUUID) configurationSection.get(ParkourArenaStorageKey.ID.getKey(),
new SerializableUUID(UUID.randomUUID()))).getRawValue();
String arenaName = configurationSection.getString(ParkourArenaStorageKey.NAME.getKey());
Location spawnLocation = (Location) configurationSection.get(ParkourArenaStorageKey.SPAWN_LOCATION.getKey());
Location exitLocation = (Location) configurationSection.get(ParkourArenaStorageKey.EXIT_LOCATION.getKey());
Location winLocation = (Location) configurationSection.get(ParkourArenaStorageKey.WIN_LOCATION.getKey());
SerializableMaterial winBlockType = (SerializableMaterial) configurationSection.get(
ParkourArenaStorageKey.WIN_BLOCK_TYPE.getKey());
List<?> killPlaneBlockNames = configurationSection.getList(ParkourArenaStorageKey.KILL_PLANE_BLOCKS.getKey());
List<Location> checkpoints = (List<Location>) configurationSection.get(ParkourArenaStorageKey.CHECKPOINTS.getKey());
// The arena name and spawn location must be present
if (arenaName == null || spawnLocation == null) {
MiniGames.log(Level.SEVERE, "Could not load the arena at configuration " +
"section " + configurationSection.getName() + ". Please check the parkour_arenas storage file for issues.");
return null;
}
// Fall back to the default win block
if (winBlockType == null) {
winBlockType = new SerializableMaterial(Material.EMERALD_BLOCK);
}
// Generate new, empty arena data if not available
ParkourArenaData arenaData = loadParkourArenaData(arenaId);
if (arenaData == null) {
MiniGames.log(Level.SEVERE, "Unable to load arena data for parkour arena" + arenaId);
arenaData = getEmptyParkourData(arenaId);
}
if (checkpoints == null) {
checkpoints = new ArrayList<>();
}
return new ParkourArena(arenaId, arenaName, spawnLocation, exitLocation, winBlockType.getRawValue(), winLocation,
(Set<String>) killPlaneBlockNames, checkpoints, arenaData, MiniGames.getInstance().getParkourArenaHandler());
}
/**
* Gets empty parkour data
*
* @param arenaId <p>The id to get parkour data for</p>
* @return <p>Empty parkour data</p>
*/
private static @NotNull ParkourArenaData getEmptyParkourData(@NotNull UUID arenaId) {
Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries = new HashMap<>();
Map<ArenaGameMode, Set<UUID>> playersCompleted = new HashMap<>();
for (ArenaGameMode arenaGameMode : ParkourArenaGameMode.values()) {
recordRegistries.put(arenaGameMode, new ParkourArenaRecordsRegistry(arenaId));
playersCompleted.put(arenaGameMode, new HashSet<>());
}
return new ParkourArenaData(arenaId, recordRegistries, playersCompleted);
}
/**
* Stores the given arena data to a file
*
* @param arenaData <p>The arena data to store</p>
*/
public static void saveParkourArenaData(@NotNull ParkourArenaData arenaData) throws IOException {
YamlConfiguration configuration = new YamlConfiguration();
configuration.set(ParkourArenaStorageKey.DATA.getKey(), arenaData);
configuration.save(getParkourArenaDataFile(arenaData.getArenaId()));
}
/**
* Loads arena data for the given arena id
*
* @param arenaId <p>The id of the arena to get data for</p>
* @return <p>The loaded arena data</p>
*/
private static @Nullable ParkourArenaData loadParkourArenaData(@NotNull UUID arenaId) {
File arenaDataFile = getParkourArenaDataFile(arenaId);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(arenaDataFile);
return (ParkourArenaData) configuration.get(ParkourArenaStorageKey.DATA.getKey());
}
/**
* Removes data for the arena with the given id
*
* @param arenaId <p>The id of the arena to remove data for</p>
* @return <p>True if the data was successfully removed</p>
*/
public static boolean removeParkourArenaData(@NotNull UUID arenaId) {
return getParkourArenaDataFile(arenaId).delete();
}
/**
* Gets the file used to store the given arena id's data
*
* @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>
*/
private static @NotNull File getParkourArenaDataFile(@NotNull UUID arenaId) {
return getArenaDataFile(parkourArenaDataFolder, arenaId);
}
}