diff --git a/src/main/java/net/knarcraft/minigames/MiniGames.java b/src/main/java/net/knarcraft/minigames/MiniGames.java index f26726a..c6ca215 100644 --- a/src/main/java/net/knarcraft/minigames/MiniGames.java +++ b/src/main/java/net/knarcraft/minigames/MiniGames.java @@ -1,19 +1,23 @@ package net.knarcraft.minigames; +import net.knarcraft.minigames.arena.ArenaPlayerRegistry; import net.knarcraft.minigames.arena.ArenaSession; +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.DropperArenaHandler; import net.knarcraft.minigames.arena.dropper.DropperArenaPlayerRegistry; import net.knarcraft.minigames.arena.dropper.DropperArenaRecordsRegistry; -import net.knarcraft.minigames.arena.dropper.DropperArenaSession; +import net.knarcraft.minigames.arena.dropper.DropperPlayerEntryState; +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.ParkourArenaHandler; import net.knarcraft.minigames.arena.parkour.ParkourArenaPlayerRegistry; import net.knarcraft.minigames.arena.parkour.ParkourArenaRecordsRegistry; +import net.knarcraft.minigames.arena.parkour.ParkourPlayerEntryState; import net.knarcraft.minigames.arena.record.IntegerRecord; import net.knarcraft.minigames.arena.record.LongRecord; import net.knarcraft.minigames.command.LeaveArenaCommand; @@ -48,7 +52,7 @@ import net.knarcraft.minigames.container.SerializableUUID; import net.knarcraft.minigames.listener.CommandListener; import net.knarcraft.minigames.listener.DamageListener; import net.knarcraft.minigames.listener.MoveListener; -import net.knarcraft.minigames.listener.PlayerLeaveListener; +import net.knarcraft.minigames.listener.PlayerStateChangeListener; import net.knarcraft.minigames.placeholder.DropperRecordExpansion; import net.knarcraft.minigames.placeholder.ParkourRecordExpansion; import org.bukkit.Bukkit; @@ -56,7 +60,6 @@ import org.bukkit.command.CommandExecutor; import org.bukkit.command.PluginCommand; import org.bukkit.command.TabCompleter; import org.bukkit.configuration.serialization.ConfigurationSerialization; -import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; @@ -76,11 +79,11 @@ public final class MiniGames extends JavaPlugin { private DropperConfiguration dropperConfiguration; private ParkourConfiguration parkourConfiguration; private DropperArenaHandler dropperArenaHandler; - private DropperArenaPlayerRegistry dropperArenaPlayerRegistry; + private ArenaPlayerRegistry dropperArenaPlayerRegistry; private DropperRecordExpansion dropperRecordExpansion; private ParkourRecordExpansion parkourRecordExpansion; private ParkourArenaHandler parkourArenaHandler; - private ParkourArenaPlayerRegistry parkourArenaPlayerRegistry; + private ArenaPlayerRegistry parkourArenaPlayerRegistry; /** * Gets an instance of this plugin @@ -114,7 +117,7 @@ public final class MiniGames extends JavaPlugin { * * @return

A dropper arena player registry

*/ - public DropperArenaPlayerRegistry getDropperArenaPlayerRegistry() { + public ArenaPlayerRegistry getDropperArenaPlayerRegistry() { return this.dropperArenaPlayerRegistry; } @@ -123,7 +126,7 @@ public final class MiniGames extends JavaPlugin { * * @return

A parkour arena player registry

*/ - public ParkourArenaPlayerRegistry getParkourArenaPlayerRegistry() { + public ArenaPlayerRegistry getParkourArenaPlayerRegistry() { return this.parkourArenaPlayerRegistry; } @@ -163,7 +166,7 @@ public final class MiniGames extends JavaPlugin { * @return

The player's current session, or null if not found

*/ public @Nullable ArenaSession getSession(@NotNull UUID playerId) { - DropperArenaSession dropperArenaSession = dropperArenaPlayerRegistry.getArenaSession(playerId); + ArenaSession dropperArenaSession = dropperArenaPlayerRegistry.getArenaSession(playerId); if (dropperArenaSession != null) { return dropperArenaSession; } @@ -216,6 +219,8 @@ public final class MiniGames extends JavaPlugin { ConfigurationSerialization.registerClass(ParkourArenaData.class); ConfigurationSerialization.registerClass(ParkourArenaGroup.class); ConfigurationSerialization.registerClass(ParkourArenaGameMode.class); + ConfigurationSerialization.registerClass(DropperPlayerEntryState.class); + ConfigurationSerialization.registerClass(ParkourPlayerEntryState.class); } @Override @@ -239,7 +244,7 @@ public final class MiniGames extends JavaPlugin { PluginManager pluginManager = getServer().getPluginManager(); pluginManager.registerEvents(new DamageListener(), this); pluginManager.registerEvents(new MoveListener(this.dropperConfiguration, this.parkourConfiguration), this); - pluginManager.registerEvents(new PlayerLeaveListener(), this); + pluginManager.registerEvents(new PlayerStateChangeListener(), this); pluginManager.registerEvents(new CommandListener(), this); registerCommand("miniGamesReload", new ReloadCommand(), null); @@ -277,12 +282,12 @@ public final class MiniGames extends JavaPlugin { @Override public void onDisable() { - // Throw out currently playing players before exiting - for (Player player : getServer().getOnlinePlayers()) { - ArenaSession session = getSession(player.getUniqueId()); - if (session != null) { - session.triggerQuit(true); - } + // Kill all sessions before exiting + for (DropperArena arena : dropperArenaHandler.getArenas().values()) { + dropperArenaPlayerRegistry.removeForArena(arena, true); + } + for (ParkourArena arena : parkourArenaHandler.getArenas().values()) { + parkourArenaPlayerRegistry.removeForArena(arena, true); } } diff --git a/src/main/java/net/knarcraft/minigames/arena/AbstractArenaPlayerRegistry.java b/src/main/java/net/knarcraft/minigames/arena/AbstractArenaPlayerRegistry.java new file mode 100644 index 0000000..c9b17d7 --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/arena/AbstractArenaPlayerRegistry.java @@ -0,0 +1,107 @@ +package net.knarcraft.minigames.arena; + +import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.util.ArenaStorageHelper; +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; + +/** + * A player registry to keep track of currently playing players + * + * @param

The type of arena stored

+ */ +public abstract class AbstractArenaPlayerRegistry implements ArenaPlayerRegistry { + + private final Map arenaPlayers = new HashMap<>(); + private final Map entryStates = new HashMap<>(); + + /** + * Instantiates a new arena player registry + */ + public AbstractArenaPlayerRegistry() { + loadEntryStates(); + } + + @Override + public @Nullable PlayerEntryState getEntryState(@NotNull UUID playerId) { + return this.entryStates.get(playerId); + } + + @Override + public void registerPlayer(@NotNull UUID playerId, @NotNull ArenaSession arenaSession) { + this.arenaPlayers.put(playerId, arenaSession); + this.entryStates.put(playerId, arenaSession.getEntryState()); + this.saveEntryStates(); + } + + @Override + public boolean removePlayer(@NotNull UUID playerId, boolean restoreState) { + // Try and restore the state. If it cannot be restored, retain the entry state + PlayerEntryState entryState = this.entryStates.remove(playerId); + if (restoreState) { + if (entryState.restore()) { + this.saveEntryStates(); + } else { + this.entryStates.put(playerId, entryState); + } + } else { + this.saveEntryStates(); + } + + return this.arenaPlayers.remove(playerId) != null; + } + + @Override + public @Nullable ArenaSession getArenaSession(@NotNull UUID playerId) { + return this.arenaPlayers.getOrDefault(playerId, null); + } + + @Override + public void removeForArena(K arena, boolean immediately) { + for (Map.Entry entry : this.arenaPlayers.entrySet()) { + if (entry.getValue().getArena() == arena) { + // Kick the player gracefully + entry.getValue().triggerQuit(immediately); + this.arenaPlayers.remove(entry.getKey()); + } + } + } + + /** + * Gets a string key unique to this type of player registry + * + * @return

A unique key used for entry state storage

+ */ + protected abstract String getEntryStateStorageKey(); + + /** + * Saves all entry states to disk + */ + private void saveEntryStates() { + ArenaStorageHelper.storeArenaPlayerEntryStates(getEntryStateStorageKey(), new HashSet<>(entryStates.values())); + } + + /** + * Loads all entry states from disk + */ + private void loadEntryStates() { + this.entryStates.clear(); + Set entryStates = ArenaStorageHelper.getArenaPlayerEntryStates(getEntryStateStorageKey()); + for (PlayerEntryState entryState : entryStates) { + this.entryStates.put(entryState.getPlayerId(), entryState); + } + if (this.entryStates.size() > 0) { + MiniGames.log(Level.WARNING, entryStates.size() + " un-exited sessions found. This happens if " + + "players leave in the middle of a game, or if the server crashes. MiniGames will do its best " + + "to fix the players' states."); + } + } + +} diff --git a/src/main/java/net/knarcraft/minigames/arena/AbstractArenaSession.java b/src/main/java/net/knarcraft/minigames/arena/AbstractArenaSession.java new file mode 100644 index 0000000..79cff03 --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/arena/AbstractArenaSession.java @@ -0,0 +1,107 @@ +package net.knarcraft.minigames.arena; + +import net.knarcraft.minigames.config.Message; +import net.knarcraft.minigames.container.PlaceholderContainer; +import net.knarcraft.minigames.property.RecordResult; +import net.knarcraft.minigames.util.PlayerTeleporter; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public abstract class AbstractArenaSession implements ArenaSession { + + private final @NotNull Arena arena; + private final @NotNull ArenaGameMode gameMode; + private final @NotNull Player player; + protected int deaths; + protected final long startTime; + protected PlayerEntryState entryState; + + /** + * Instantiates a new abstract arena session + * + * @param arena

The arena that's being played in

+ * @param player

The player playing the arena

+ * @param gameMode

The game-mode

+ */ + public AbstractArenaSession(@NotNull Arena arena, @NotNull Player player, @NotNull ArenaGameMode gameMode) { + this.arena = arena; + this.player = player; + this.gameMode = gameMode; + this.deaths = 0; + this.startTime = System.currentTimeMillis(); + } + + @Override + public void triggerQuit(boolean immediately) { + // Stop this session + removeSession(); + // Teleport the player out of the arena + teleportToExit(immediately); + + player.sendMessage(Message.SUCCESS_ARENA_QUIT.getMessage()); + } + + /** + * Announces a record set by this player + * + * @param recordResult

The result of the record

+ * @param type

The type of record set (time or deaths)

+ */ + protected void announceRecord(@NotNull RecordResult recordResult, @NotNull String type) { + if (recordResult == RecordResult.NONE) { + return; + } + + // Gets a string representation of the played game-mode + String gameModeString = getGameModeString(); + + Message recordInfoMessage = switch (recordResult) { + case WORLD_RECORD -> Message.RECORD_ACHIEVED_GLOBAL; + case PERSONAL_BEST -> Message.RECORD_ACHIEVED_PERSONAL; + default -> throw new IllegalStateException("Unexpected value: " + recordResult); + }; + String recordInfo = recordInfoMessage.getPartialMessage("{recordType}", type); + + PlaceholderContainer placeholderContainer = new PlaceholderContainer().add("{gameMode}", gameModeString); + placeholderContainer.add("{recordInfo}", recordInfo); + player.sendMessage(Message.SUCCESS_RECORD_ACHIEVED.getMessage(placeholderContainer)); + } + + /** + * Registers the player's record if necessary, and prints record information to the player + */ + protected void registerRecord() { + ArenaRecordsRegistry recordsRegistry = this.arena.getData().getRecordRegistries().get(this.gameMode); + long timeElapsed = System.currentTimeMillis() - this.startTime; + announceRecord(recordsRegistry.registerTimeRecord(this.player.getUniqueId(), timeElapsed), "time"); + announceRecord(recordsRegistry.registerDeathRecord(this.player.getUniqueId(), this.deaths), "least deaths"); + } + + /** + * Teleports the playing player out of the arena + */ + protected void teleportToExit(boolean immediately) { + // Teleport the player out of the arena + Location exitLocation; + if (this.arena.getExitLocation() != null) { + exitLocation = this.arena.getExitLocation(); + } else { + exitLocation = this.entryState.getEntryLocation(); + } + PlayerTeleporter.teleportPlayer(this.player, exitLocation, true, immediately); + } + + /** + * Gets the string representation of the session's game-mode + * + * @return

The string representation

+ */ + protected abstract String getGameModeString(); + + /** + * Removes this session from current sessions + */ + protected abstract void removeSession(); + +} diff --git a/src/main/java/net/knarcraft/minigames/arena/AbstractPlayerEntryState.java b/src/main/java/net/knarcraft/minigames/arena/AbstractPlayerEntryState.java index c5cdf04..d1bf53b 100644 --- a/src/main/java/net/knarcraft/minigames/arena/AbstractPlayerEntryState.java +++ b/src/main/java/net/knarcraft/minigames/arena/AbstractPlayerEntryState.java @@ -1,5 +1,8 @@ package net.knarcraft.minigames.arena; +import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.container.SerializableUUID; +import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -7,12 +10,17 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.jetbrains.annotations.NotNull; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Level; + /** * An abstract representation of a player's entry state */ public abstract class AbstractPlayerEntryState implements PlayerEntryState { - protected final Player player; + protected final UUID playerId; private final boolean makePlayerInvisible; private final Location entryLocation; private final boolean originalIsFlying; @@ -29,7 +37,7 @@ public abstract class AbstractPlayerEntryState implements PlayerEntryState { * @param makePlayerInvisible

Whether players should be made invisible while in the arena

*/ public AbstractPlayerEntryState(@NotNull Player player, boolean makePlayerInvisible) { - this.player = player; + this.playerId = player.getUniqueId(); this.makePlayerInvisible = makePlayerInvisible; this.entryLocation = player.getLocation().clone(); this.originalIsFlying = player.isFlying(); @@ -40,24 +48,71 @@ public abstract class AbstractPlayerEntryState implements PlayerEntryState { this.originalCollideAble = player.isCollidable(); } + /** + * Instantiates a new abstract player entry state + * + * @param playerId

The id of the player whose state this should keep track of

+ * @param makePlayerInvisible

Whether players should be made invisible while in the arena

+ * @param entryLocation

The location the player entered from

+ * @param originalIsFlying

Whether the player was flying before entering the arena

+ * @param originalGameMode

The game-mode of the player before entering the arena

+ * @param originalAllowFlight

Whether the player was allowed flight before entering the arena

+ * @param originalInvulnerable

Whether the player was invulnerable before entering the arena

+ * @param originalIsSwimming

Whether the player was swimming before entering the arena

+ * @param originalCollideAble

Whether the player was collide-able before entering the arena

+ */ + public AbstractPlayerEntryState(@NotNull UUID playerId, boolean makePlayerInvisible, Location entryLocation, + boolean originalIsFlying, GameMode originalGameMode, boolean originalAllowFlight, + boolean originalInvulnerable, boolean originalIsSwimming, + boolean originalCollideAble) { + this.playerId = playerId; + this.makePlayerInvisible = makePlayerInvisible; + this.entryLocation = entryLocation; + this.originalIsFlying = originalIsFlying; + this.originalGameMode = originalGameMode; + this.originalAllowFlight = originalAllowFlight; + this.originalInvulnerable = originalInvulnerable; + this.originalIsSwimming = originalIsSwimming; + this.originalCollideAble = originalCollideAble; + } + + @Override + public @NotNull UUID getPlayerId() { + return this.playerId; + } + @Override public void setArenaState() { + Player player = getPlayer(); + if (player == null) { + return; + } if (this.makePlayerInvisible) { - this.player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, + player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, PotionEffect.INFINITE_DURATION, 3)); } } @Override - public void restore() { - this.player.setFlying(this.originalIsFlying); - this.player.setGameMode(this.originalGameMode); - this.player.setAllowFlight(this.originalAllowFlight); - this.player.setInvulnerable(this.originalInvulnerable); - this.player.setSwimming(this.originalIsSwimming); - this.player.setCollidable(this.originalCollideAble); + public boolean restore() { + Player player = getPlayer(); + if (player == null) { + return false; + } + restore(player); + return true; + } + + @Override + public void restore(@NotNull Player player) { + player.setFlying(this.originalIsFlying); + player.setGameMode(this.originalGameMode); + player.setAllowFlight(this.originalAllowFlight); + player.setInvulnerable(this.originalInvulnerable); + player.setSwimming(this.originalIsSwimming); + player.setCollidable(this.originalCollideAble); if (this.makePlayerInvisible) { - this.player.removePotionEffect(PotionEffectType.INVISIBILITY); + player.removePotionEffect(PotionEffectType.INVISIBILITY); } } @@ -66,4 +121,34 @@ public abstract class AbstractPlayerEntryState implements PlayerEntryState { return this.entryLocation; } + /** + * Gets the player this entry state belongs to + * + * @return

The player, or null if not currently online

+ */ + protected Player getPlayer() { + Player player = Bukkit.getOfflinePlayer(this.playerId).getPlayer(); + if (player == null) { + MiniGames.log(Level.WARNING, "Unable to change state for player with id " + this.playerId + + " because the player was not found on the server."); + } + return player; + } + + @NotNull + @Override + public Map serialize() { + Map data = new HashMap<>(); + data.put("playerId", new SerializableUUID(this.playerId)); + data.put("makePlayerInvisible", this.makePlayerInvisible); + data.put("entryLocation", this.entryLocation); + data.put("originalIsFlying", this.originalIsFlying); + data.put("originalGameMode", this.originalGameMode.name()); + data.put("originalAllowFlight", this.originalAllowFlight); + data.put("originalInvulnerable", this.originalInvulnerable); + data.put("originalIsSwimming", this.originalIsSwimming); + data.put("originalCollideAble", this.originalCollideAble); + return data; + } + } diff --git a/src/main/java/net/knarcraft/minigames/arena/Arena.java b/src/main/java/net/knarcraft/minigames/arena/Arena.java index 97e46d4..ad15e81 100644 --- a/src/main/java/net/knarcraft/minigames/arena/Arena.java +++ b/src/main/java/net/knarcraft/minigames/arena/Arena.java @@ -3,6 +3,7 @@ package net.knarcraft.minigames.arena; import org.bukkit.Location; import org.bukkit.block.Block; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.UUID; @@ -83,4 +84,11 @@ public interface Arena { */ @NotNull Location getSpawnLocation(); + /** + * Gets this arena's exit location + * + * @return

This arena's exit location, or null if no such location is set.

+ */ + @Nullable Location getExitLocation(); + } diff --git a/src/main/java/net/knarcraft/minigames/arena/ArenaHandler.java b/src/main/java/net/knarcraft/minigames/arena/ArenaHandler.java index 3c71900..6c37279 100644 --- a/src/main/java/net/knarcraft/minigames/arena/ArenaHandler.java +++ b/src/main/java/net/knarcraft/minigames/arena/ArenaHandler.java @@ -170,7 +170,7 @@ public abstract class ArenaHandler> */ public void removeArena(@NotNull K arena) { UUID arenaId = arena.getArenaId(); - this.playerRegistry.removeForArena(arena); + this.playerRegistry.removeForArena(arena, false); this.arenas.remove(arenaId); this.arenaNameLookup.remove(arena.getArenaNameSanitized()); this.arenaGroups.remove(arenaId); diff --git a/src/main/java/net/knarcraft/minigames/arena/ArenaPlayerRegistry.java b/src/main/java/net/knarcraft/minigames/arena/ArenaPlayerRegistry.java index d7734a6..cb10111 100644 --- a/src/main/java/net/knarcraft/minigames/arena/ArenaPlayerRegistry.java +++ b/src/main/java/net/knarcraft/minigames/arena/ArenaPlayerRegistry.java @@ -1,5 +1,10 @@ package net.knarcraft.minigames.arena; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + /** * A registry keeping track of all player sessions for some arenas * @@ -7,11 +12,44 @@ package net.knarcraft.minigames.arena; */ public interface ArenaPlayerRegistry { + /** + * Gets the current entry state for the given player + * + * @param playerId

The id of the player to get an entry state for

+ * @return

The entry state of the player, or null if not found

+ */ + @Nullable PlayerEntryState getEntryState(@NotNull UUID playerId); + + /** + * Registers that the given player has started playing the given dropper arena session + * + * @param playerId

The id of the player that started playing

+ * @param arenaSession

The arena session to register

+ */ + void registerPlayer(@NotNull UUID playerId, @NotNull ArenaSession arenaSession); + + /** + * Removes this player from players currently playing + * + * @param playerId

The id of the player to remove

+ * @param restoreState

Whether to restore the state of the player as part of the removal

+ */ + boolean removePlayer(@NotNull UUID playerId, boolean restoreState); + + /** + * Gets the player's active dropper arena session + * + * @param playerId

The id of the player to get arena for

+ * @return

The player's active arena session, or null if not currently playing

+ */ + @Nullable ArenaSession getArenaSession(@NotNull UUID playerId); + /** * Removes all active sessions for the given arena * - * @param arena

The arena to remove sessions for

+ * @param arena

The arena to remove sessions for

+ * @param immediately

Whether to immediately teleport the player

*/ - void removeForArena(K arena); + void removeForArena(K arena, boolean immediately); } diff --git a/src/main/java/net/knarcraft/minigames/arena/ArenaSession.java b/src/main/java/net/knarcraft/minigames/arena/ArenaSession.java index ffe4a99..f1afd43 100644 --- a/src/main/java/net/knarcraft/minigames/arena/ArenaSession.java +++ b/src/main/java/net/knarcraft/minigames/arena/ArenaSession.java @@ -1,6 +1,5 @@ package net.knarcraft.minigames.arena; -import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; /** @@ -8,13 +7,6 @@ import org.jetbrains.annotations.NotNull; */ public interface ArenaSession { - /** - * Gets the game-mode the player is playing in this session - * - * @return

The game-mode for this session

- */ - @NotNull ArenaGameMode getGameMode(); - /** * Gets the state of the player when they joined the session * @@ -46,11 +38,4 @@ public interface ArenaSession { */ @NotNull Arena getArena(); - /** - * Gets the player playing in this session - * - * @return

This session's player

- */ - @NotNull Player getPlayer(); - } diff --git a/src/main/java/net/knarcraft/minigames/arena/EditablePropertyType.java b/src/main/java/net/knarcraft/minigames/arena/EditablePropertyType.java new file mode 100644 index 0000000..6f07e87 --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/arena/EditablePropertyType.java @@ -0,0 +1,43 @@ +package net.knarcraft.minigames.arena; + +/** + * The type of one editable property + */ +public enum EditablePropertyType { + + /** + * The property is a location + */ + LOCATION, + + /** + * The property is an arena name + */ + ARENA_NAME, + + /** + * The property is a horizontal velocity + */ + HORIZONTAL_VELOCITY, + + /** + * The property is a vertical velocity (fly speed) + */ + VERTICAL_VELOCITY, + + /** + * The property is a material that specifies a block + */ + BLOCK_TYPE, + + /** + * The property clears a checkpoint + */ + CHECKPOINT_CLEAR, + + /** + * The property is a comma-separated list of materials + */ + MATERIAL_LIST + +} diff --git a/src/main/java/net/knarcraft/minigames/arena/PlayerEntryState.java b/src/main/java/net/knarcraft/minigames/arena/PlayerEntryState.java index cfbc91f..da3f8d8 100644 --- a/src/main/java/net/knarcraft/minigames/arena/PlayerEntryState.java +++ b/src/main/java/net/knarcraft/minigames/arena/PlayerEntryState.java @@ -1,11 +1,15 @@ package net.knarcraft.minigames.arena; import org.bukkit.Location; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.entity.Player; + +import java.util.UUID; /** * The stored state of a player */ -public interface PlayerEntryState { +public interface PlayerEntryState extends ConfigurationSerializable { /** * Sets the state of the stored player to the state used by the arena @@ -15,7 +19,21 @@ public interface PlayerEntryState { /** * Restores the stored state for the stored player */ - void restore(); + boolean restore(); + + /** + * Restores the stored state for the given player + * + * @param player

A player object that's refers to the same player as the stored player

+ */ + void restore(Player player); + + /** + * Gets the id of the player this state belongs to + * + * @return

The player the state belongs to

+ */ + UUID getPlayerId(); /** * Gets the location the player entered from diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArena.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArena.java index 60cb457..eb5cbc0 100644 --- a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArena.java +++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArena.java @@ -150,11 +150,7 @@ public class DropperArena implements Arena { return this.spawnLocation.clone(); } - /** - * Gets this arena's exit location - * - * @return

This arena's exit location, or null if no such location is set.

- */ + @Override public @Nullable Location getExitLocation() { return this.exitLocation != null ? this.exitLocation.clone() : null; } diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaEditableProperty.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaEditableProperty.java index 9e77748..8dc6b5e 100644 --- a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaEditableProperty.java +++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaEditableProperty.java @@ -1,5 +1,6 @@ package net.knarcraft.minigames.arena.dropper; +import net.knarcraft.minigames.arena.EditablePropertyType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,45 +14,62 @@ public enum DropperArenaEditableProperty { /** * The name of the arena */ - NAME("name", DropperArena::getArenaName), + NAME("name", DropperArena::getArenaName, EditablePropertyType.ARENA_NAME), /** * The arena's spawn location */ - SPAWN_LOCATION("spawnLocation", (arena) -> String.valueOf(arena.getSpawnLocation())), + SPAWN_LOCATION("spawnLocation", (arena) -> String.valueOf(arena.getSpawnLocation()), + EditablePropertyType.LOCATION), /** * The arena's exit location */ - EXIT_LOCATION("exitLocation", (arena) -> String.valueOf(arena.getExitLocation())), + EXIT_LOCATION("exitLocation", (arena) -> String.valueOf(arena.getExitLocation()), + EditablePropertyType.LOCATION), /** * The arena's vertical velocity */ - VERTICAL_VELOCITY("verticalVelocity", (arena) -> String.valueOf(arena.getPlayerVerticalVelocity())), + VERTICAL_VELOCITY("verticalVelocity", (arena) -> String.valueOf(arena.getPlayerVerticalVelocity()), + EditablePropertyType.VERTICAL_VELOCITY), /** * The arena's horizontal velocity */ - HORIZONTAL_VELOCITY("horizontalVelocity", (arena) -> String.valueOf(arena.getPlayerHorizontalVelocity())), + HORIZONTAL_VELOCITY("horizontalVelocity", (arena) -> String.valueOf(arena.getPlayerHorizontalVelocity()), + EditablePropertyType.HORIZONTAL_VELOCITY), /** * The arena's win block type */ - WIN_BLOCK_TYPE("winBlockType", (arena) -> arena.getWinBlockType().toString()), + WIN_BLOCK_TYPE("winBlockType", (arena) -> arena.getWinBlockType().toString(), + EditablePropertyType.BLOCK_TYPE), ; private final @NotNull String argumentString; private final Function currentValueProvider; + private final EditablePropertyType propertyType; /** * Instantiates a new arena editable property * * @param argumentString

The argument string used to specify this property

*/ - DropperArenaEditableProperty(@NotNull String argumentString, Function currentValueProvider) { + DropperArenaEditableProperty(@NotNull String argumentString, Function currentValueProvider, + EditablePropertyType propertyType) { this.argumentString = argumentString; this.currentValueProvider = currentValueProvider; + this.propertyType = propertyType; + } + + /** + * Gets the type of property this editable property represents + * + * @return

The type of this property

+ */ + public EditablePropertyType getPropertyType() { + return this.propertyType; } /** diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaHandler.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaHandler.java index e2e4eac..bc07a47 100644 --- a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaHandler.java +++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaHandler.java @@ -2,6 +2,8 @@ package net.knarcraft.minigames.arena.dropper; import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.ArenaHandler; +import net.knarcraft.minigames.arena.ArenaPlayerRegistry; +import net.knarcraft.minigames.config.Message; import net.knarcraft.minigames.util.DropperArenaStorageHelper; import java.io.IOException; @@ -22,7 +24,7 @@ public class DropperArenaHandler extends ArenaHandlerThe registry keeping track of player sessions

*/ - public DropperArenaHandler(DropperArenaPlayerRegistry playerRegistry) { + public DropperArenaHandler(ArenaPlayerRegistry playerRegistry) { super(playerRegistry); } @@ -31,8 +33,7 @@ public class DropperArenaHandler extends ArenaHandler(this.arenaGroups.values())); } catch (IOException e) { - MiniGames.log(Level.SEVERE, "Unable to save current arena groups! " + - "Data loss can occur!"); + MiniGames.log(Level.SEVERE, Message.ERROR_CANNOT_SAVE_ARENA_GROUPS.getMessage()); MiniGames.log(Level.SEVERE, e.getMessage()); } } diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaPlayerRegistry.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaPlayerRegistry.java index 8c3b327..8e82940 100644 --- a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaPlayerRegistry.java +++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaPlayerRegistry.java @@ -1,62 +1,15 @@ package net.knarcraft.minigames.arena.dropper; -import net.knarcraft.minigames.arena.ArenaPlayerRegistry; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import net.knarcraft.minigames.arena.AbstractArenaPlayerRegistry; /** * A registry to keep track of which players are playing in which arenas */ -public class DropperArenaPlayerRegistry implements ArenaPlayerRegistry { +public class DropperArenaPlayerRegistry extends AbstractArenaPlayerRegistry { - private final Map arenaPlayers = new HashMap<>(); - - /** - * Registers that the given player has started playing the given dropper arena session - * - * @param playerId

The id of the player that started playing

- * @param arena

The arena session to register

- */ - public void registerPlayer(@NotNull UUID playerId, @NotNull DropperArenaSession arena) { - this.arenaPlayers.put(playerId, arena); - } - - /** - * Removes this player from players currently playing - * - * @param playerId

The id of the player to remove

- */ - public boolean removePlayer(@NotNull UUID playerId) { - return this.arenaPlayers.remove(playerId) != null; - } - - /** - * Gets the player's active dropper arena session - * - * @param playerId

The id of the player to get arena for

- * @return

The player's active arena session, or null if not currently playing

- */ - public @Nullable DropperArenaSession getArenaSession(@NotNull UUID playerId) { - return this.arenaPlayers.getOrDefault(playerId, null); - } - - /** - * Removes all active sessions for the given arena - * - * @param arena

The arena to remove sessions for

- */ - public void removeForArena(DropperArena arena) { - for (Map.Entry entry : this.arenaPlayers.entrySet()) { - if (entry.getValue().getArena() == arena) { - // Kick the player gracefully - entry.getValue().triggerQuit(false); - this.arenaPlayers.remove(entry.getKey()); - } - } + @Override + protected String getEntryStateStorageKey() { + return "dropper"; } } diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaSession.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaSession.java index 2ff11c3..9d995b5 100644 --- a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaSession.java +++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperArenaSession.java @@ -1,13 +1,11 @@ package net.knarcraft.minigames.arena.dropper; import net.knarcraft.minigames.MiniGames; -import net.knarcraft.minigames.arena.ArenaRecordsRegistry; -import net.knarcraft.minigames.arena.ArenaSession; +import net.knarcraft.minigames.arena.AbstractArenaSession; import net.knarcraft.minigames.arena.PlayerEntryState; import net.knarcraft.minigames.config.DropperConfiguration; -import net.knarcraft.minigames.property.RecordResult; +import net.knarcraft.minigames.config.Message; import net.knarcraft.minigames.util.PlayerTeleporter; -import org.bukkit.Location; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -16,14 +14,11 @@ import java.util.logging.Level; /** * A representation of a player's current session in a dropper arena */ -public class DropperArenaSession implements ArenaSession { +public class DropperArenaSession extends AbstractArenaSession { private final @NotNull DropperArena arena; private final @NotNull Player player; private final @NotNull DropperArenaGameMode gameMode; - private int deaths; - private final long startTime; - private final PlayerEntryState entryState; /** * Instantiates a new dropper arena session @@ -34,173 +29,19 @@ public class DropperArenaSession implements ArenaSession { */ public DropperArenaSession(@NotNull DropperArena dropperArena, @NotNull Player player, @NotNull DropperArenaGameMode gameMode) { + super(dropperArena, player, gameMode); this.arena = dropperArena; this.player = player; this.gameMode = gameMode; - this.deaths = 0; - this.startTime = System.currentTimeMillis(); DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration(); boolean makeInvisible = configuration.makePlayersInvisible(); boolean disableCollision = configuration.disableHitCollision(); this.entryState = new DropperPlayerEntryState(player, gameMode, makeInvisible, disableCollision, dropperArena.getPlayerHorizontalVelocity()); - // Make the player fly to improve mobility in the air this.entryState.setArenaState(); } - /** - * Gets the game-mode the player is playing in this session - * - * @return

The game-mode for this session

- */ - public @NotNull DropperArenaGameMode getGameMode() { - return this.gameMode; - } - - /** - * Gets the state of the player when they joined the session - * - * @return

The player's entry state

- */ - public @NotNull PlayerEntryState getEntryState() { - return this.entryState; - } - - /** - * Triggers a win for the player playing in this session - */ - public void triggerWin() { - // Stop this session - stopSession(); - - // Check for, and display, records - MiniGames miniGames = MiniGames.getInstance(); - boolean ignore = miniGames.getDropperConfiguration().ignoreRecordsUntilGroupBeatenOnce(); - DropperArenaGroup group = miniGames.getDropperArenaHandler().getGroup(this.arena.getArenaId()); - if (!ignore || group == null || group.hasBeatenAll(this.gameMode, this.player)) { - registerRecord(); - } - - // Mark the arena as cleared - if (this.arena.getData().setCompleted(this.gameMode, this.player)) { - this.player.sendMessage("You cleared the arena!"); - } - this.player.sendMessage("You won!"); - - // Teleport the player out of the arena - teleportToExit(false); - } - - /** - * Teleports the playing player out of the arena - * - * @param immediately

Whether to to the teleportation immediately, not using any timers

- */ - private void teleportToExit(boolean immediately) { - // Teleport the player out of the arena - Location exitLocation; - if (this.arena.getExitLocation() != null) { - exitLocation = this.arena.getExitLocation(); - } else { - exitLocation = this.entryState.getEntryLocation(); - } - PlayerTeleporter.teleportPlayer(this.player, exitLocation, true, immediately); - } - - /** - * Removes this session from current sessions - */ - private void removeSession() { - // Remove this session for game sessions to stop listeners from fiddling more with the player - boolean removedSession = MiniGames.getInstance().getDropperArenaPlayerRegistry().removePlayer(player.getUniqueId()); - if (!removedSession) { - MiniGames.log(Level.SEVERE, "Unable to remove dropper arena session for " + player.getName() + ". " + - "This will have unintended consequences."); - } - } - - /** - * Registers the player's record if necessary, and prints record information to the player - */ - private void registerRecord() { - ArenaRecordsRegistry recordsRegistry = this.arena.getData().getRecordRegistries().get(this.gameMode); - long timeElapsed = System.currentTimeMillis() - this.startTime; - announceRecord(recordsRegistry.registerTimeRecord(this.player.getUniqueId(), timeElapsed), "time"); - announceRecord(recordsRegistry.registerDeathRecord(this.player.getUniqueId(), this.deaths), "least deaths"); - } - - /** - * Announces a record set by this player - * - * @param recordResult

The result of the record

- * @param type

The type of record set (time or deaths)

- */ - private void announceRecord(@NotNull RecordResult recordResult, @NotNull String type) { - if (recordResult == RecordResult.NONE) { - return; - } - - // Gets a string representation of the played game-mode - String gameModeString = switch (this.gameMode) { - case DEFAULT -> "default"; - case INVERTED -> "inverted"; - case RANDOM_INVERTED -> "random"; - }; - - String recordString = "You just set a %s on the %s game-mode!"; - recordString = switch (recordResult) { - case WORLD_RECORD -> String.format(recordString, "new %s record", gameModeString); - case PERSONAL_BEST -> String.format(recordString, "personal %s record", gameModeString); - default -> throw new IllegalStateException("Unexpected value: " + recordResult); - }; - player.sendMessage(String.format(recordString, type)); - } - - /** - * Triggers a loss for the player playing in this session - */ - public void triggerLoss() { - this.deaths++; - //Teleport the player back to the top - PlayerTeleporter.teleportPlayer(this.player, this.arena.getSpawnLocation(), true, false); - this.entryState.setArenaState(); - } - - /** - * Triggers a quit for the player playing in this session - * - * @param immediately

Whether to to the teleportation immediately, not using any timers

- */ - public void triggerQuit(boolean immediately) { - // Stop this session - stopSession(); - // Teleport the player out of the arena - teleportToExit(immediately); - - player.sendMessage("You quit the arena!"); - } - - /** - * Stops this session, and disables flight mode - */ - private void stopSession() { - // Remove this session from game sessions to stop listeners from fiddling more with the player - removeSession(); - - // Remove flight mode - entryState.restore(); - } - - /** - * Gets the arena this session is being played in - * - * @return

The session's arena

- */ - public @NotNull DropperArena getArena() { - return this.arena; - } - /** * Gets the player playing in this session * @@ -210,4 +51,74 @@ public class DropperArenaSession implements ArenaSession { return this.player; } + /** + * Gets the game-mode the player is playing in this session + * + * @return

The game-mode for this session

+ */ + public @NotNull DropperArenaGameMode getGameMode() { + return this.gameMode; + } + + @Override + public @NotNull PlayerEntryState getEntryState() { + return this.entryState; + } + + @Override + public void triggerWin() { + // Stop this session + removeSession(); + + // Check for, and display, records + MiniGames miniGames = MiniGames.getInstance(); + boolean ignore = miniGames.getDropperConfiguration().ignoreRecordsUntilGroupBeatenOnce(); + DropperArenaGroup group = miniGames.getDropperArenaHandler().getGroup(this.arena.getArenaId()); + if (!ignore || group == null || group.hasBeatenAll(this.gameMode, this.player)) { + registerRecord(); + } + + // Mark the arena as cleared + if (this.arena.getData().setCompleted(this.gameMode, this.player)) { + this.player.sendMessage(Message.SUCCESS_ARENA_FIRST_CLEAR.getMessage()); + } + this.player.sendMessage(Message.SUCCESS_ARENA_WIN.getMessage()); + + // Teleport the player out of the arena + teleportToExit(false); + } + + @Override + public void triggerLoss() { + this.deaths++; + //Teleport the player back to the top + PlayerTeleporter.teleportPlayer(this.player, this.arena.getSpawnLocation(), true, false); + this.entryState.setArenaState(); + } + + @Override + public @NotNull DropperArena getArena() { + return this.arena; + } + + @Override + protected void removeSession() { + // Remove this session for game sessions to stop listeners from fiddling more with the player + boolean removedSession = MiniGames.getInstance().getDropperArenaPlayerRegistry().removePlayer( + player.getUniqueId(), true); + if (!removedSession) { + MiniGames.log(Level.SEVERE, "Unable to remove dropper arena session for " + player.getName() + ". " + + "This will have unintended consequences."); + } + } + + @Override + protected String getGameModeString() { + return switch (this.gameMode) { + case DEFAULT -> "default"; + case INVERTED -> "inverted"; + case RANDOM_INVERTED -> "random"; + }; + } + } diff --git a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperPlayerEntryState.java b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperPlayerEntryState.java index c72fedb..3a8e993 100644 --- a/src/main/java/net/knarcraft/minigames/arena/dropper/DropperPlayerEntryState.java +++ b/src/main/java/net/knarcraft/minigames/arena/dropper/DropperPlayerEntryState.java @@ -1,10 +1,15 @@ package net.knarcraft.minigames.arena.dropper; import net.knarcraft.minigames.arena.AbstractPlayerEntryState; +import net.knarcraft.minigames.container.SerializableUUID; import org.bukkit.GameMode; +import org.bukkit.Location; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import java.util.Map; +import java.util.UUID; + /** * The state of a player before entering a dropper arena */ @@ -29,29 +34,106 @@ public class DropperPlayerEntryState extends AbstractPlayerEntryState { this.horizontalVelocity = horizontalVelocity; } + /** + * Instantiates a new parkour player entry state + * + * @param playerId

The id of the player whose state this should keep track of

+ * @param makePlayerInvisible

Whether players should be made invisible while in the arena

+ * @param entryLocation

The location the player entered from

+ * @param originalIsFlying

Whether the player was flying before entering the arena

+ * @param originalGameMode

The game-mode of the player before entering the arena

+ * @param originalAllowFlight

Whether the player was allowed flight before entering the arena

+ * @param originalInvulnerable

Whether the player was invulnerable before entering the arena

+ * @param originalIsSwimming

Whether the player was swimming before entering the arena

+ * @param originalCollideAble

Whether the player was collide-able before entering the arena

+ */ + public DropperPlayerEntryState(@NotNull UUID playerId, boolean makePlayerInvisible, Location entryLocation, + boolean originalIsFlying, GameMode originalGameMode, boolean originalAllowFlight, + boolean originalInvulnerable, boolean originalIsSwimming, + boolean originalCollideAble, float originalFlySpeed, boolean disableHitCollision, + float horizontalVelocity, DropperArenaGameMode arenaGameMode) { + super(playerId, makePlayerInvisible, entryLocation, originalIsFlying, originalGameMode, originalAllowFlight, + originalInvulnerable, originalIsSwimming, originalCollideAble); + this.originalFlySpeed = originalFlySpeed; + this.disableHitCollision = disableHitCollision; + this.horizontalVelocity = horizontalVelocity; + this.arenaGameMode = arenaGameMode; + } + @Override public void setArenaState() { super.setArenaState(); - this.player.setAllowFlight(true); - this.player.setFlying(true); - this.player.setGameMode(GameMode.ADVENTURE); - this.player.setSwimming(false); + Player player = getPlayer(); + if (player == null) { + return; + } + player.setAllowFlight(true); + player.setFlying(true); + player.setGameMode(GameMode.ADVENTURE); + player.setSwimming(false); if (this.disableHitCollision) { - this.player.setCollidable(false); + player.setCollidable(false); } // If playing on the inverted game-mode, negate the horizontal velocity to swap the controls if (this.arenaGameMode == DropperArenaGameMode.INVERTED) { - this.player.setFlySpeed(-this.horizontalVelocity); + player.setFlySpeed(-this.horizontalVelocity); } else { - this.player.setFlySpeed(this.horizontalVelocity); + player.setFlySpeed(this.horizontalVelocity); } } @Override - public void restore() { - super.restore(); - this.player.setFlySpeed(this.originalFlySpeed); + public boolean restore() { + Player player = getPlayer(); + if (player == null) { + return false; + } + this.restore(player); + return true; + } + + @Override + public void restore(@NotNull Player player) { + super.restore(player); + player.setFlySpeed(this.originalFlySpeed); + } + + @NotNull + @Override + public Map serialize() { + Map data = super.serialize(); + data.put("originalFlySpeed", this.originalFlySpeed); + data.put("disableHitCollision", this.disableHitCollision); + data.put("horizontalVelocity", this.horizontalVelocity); + data.put("arenaGameMode", this.arenaGameMode); + return data; + } + + /** + * Deserializes a ParkourPlayerEntryState from the given data + * + * @return

The data to deserialize

+ */ + @SuppressWarnings("unused") + public static DropperPlayerEntryState deserialize(Map data) { + UUID playerId = ((SerializableUUID) data.get("playerId")).getRawValue(); + boolean makePlayerInvisible = (boolean) data.get("makePlayerInvisible"); + Location entryLocation = (Location) data.get("entryLocation"); + boolean originalIsFlying = (boolean) data.get("originalIsFlying"); + GameMode originalGameMode = GameMode.valueOf((String) data.get("originalGameMode")); + boolean originalAllowFlight = (boolean) data.get("originalAllowFlight"); + boolean originalInvulnerable = (boolean) data.get("originalInvulnerable"); + boolean originalIsSwimming = (boolean) data.get("originalIsSwimming"); + boolean originalCollideAble = (boolean) data.get("originalCollideAble"); + float originalFlySpeed = ((Number) data.get("originalFlySpeed")).floatValue(); + boolean disableHitCollision = (boolean) data.get("disableHitCollision"); + float horizontalVelocity = ((Number) data.get("horizontalVelocity")).floatValue(); + DropperArenaGameMode arenaGameMode = (DropperArenaGameMode) data.get("arenaGameMode"); + + return new DropperPlayerEntryState(playerId, makePlayerInvisible, entryLocation, originalIsFlying, + originalGameMode, originalAllowFlight, originalInvulnerable, originalIsSwimming, originalCollideAble, + originalFlySpeed, disableHitCollision, horizontalVelocity, arenaGameMode); } } diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java index 9775a2f..088649f 100644 --- a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java +++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArena.java @@ -162,11 +162,7 @@ public class ParkourArena implements Arena { return this.spawnLocation; } - /** - * Gets this arena's exit location - * - * @return

This arena's exit location, or null if no such location is set.

- */ + @Override public @Nullable Location getExitLocation() { return this.exitLocation; } diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaEditableProperty.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaEditableProperty.java index 06672c1..8e90c93 100644 --- a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaEditableProperty.java +++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaEditableProperty.java @@ -1,5 +1,6 @@ package net.knarcraft.minigames.arena.parkour; +import net.knarcraft.minigames.arena.EditablePropertyType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,22 +14,25 @@ public enum ParkourArenaEditableProperty { /** * The name of the arena */ - NAME("name", ParkourArena::getArenaName), + NAME("name", ParkourArena::getArenaName, EditablePropertyType.ARENA_NAME), /** * The arena's spawn location */ - SPAWN_LOCATION("spawnLocation", (arena) -> String.valueOf(arena.getSpawnLocation())), + SPAWN_LOCATION("spawnLocation", (arena) -> String.valueOf(arena.getSpawnLocation()), + EditablePropertyType.LOCATION), /** * The arena's exit location */ - EXIT_LOCATION("exitLocation", (arena) -> String.valueOf(arena.getExitLocation())), + EXIT_LOCATION("exitLocation", (arena) -> String.valueOf(arena.getExitLocation()), + EditablePropertyType.LOCATION), /** * The arena's win block type */ - WIN_BLOCK_TYPE("winBlockType", (arena) -> arena.getWinBlockType().toString()), + WIN_BLOCK_TYPE("winBlockType", (arena) -> arena.getWinBlockType().toString(), + EditablePropertyType.BLOCK_TYPE), /** * The arena's win location (overrides the win block type) @@ -39,35 +43,50 @@ public enum ParkourArenaEditableProperty { } else { return "null"; } - }), + }, EditablePropertyType.LOCATION), /** * The arena's check points. Specifically used for adding. */ - CHECKPOINT_ADD("checkpointAdd", (arena) -> String.valueOf(arena.getCheckpoints())), + CHECKPOINT_ADD("checkpointAdd", (arena) -> String.valueOf(arena.getCheckpoints()), + EditablePropertyType.LOCATION), /** * The arena's check points. Specifically used for clearing. */ - CHECKPOINT_CLEAR("checkpointClear", (arena) -> String.valueOf(arena.getCheckpoints())), + CHECKPOINT_CLEAR("checkpointClear", (arena) -> String.valueOf(arena.getCheckpoints()), + EditablePropertyType.CHECKPOINT_CLEAR), /** * The blocks constituting the arena's lethal blocks */ - KILL_PLANE_BLOCKS("killPlaneBlocks", (arena) -> String.valueOf(arena.getKillPlaneBlockNames())), + KILL_PLANE_BLOCKS("killPlaneBlocks", (arena) -> String.valueOf(arena.getKillPlaneBlockNames()), + EditablePropertyType.MATERIAL_LIST), ; private final @NotNull String argumentString; private final Function currentValueProvider; + private final EditablePropertyType propertyType; /** * Instantiates a new arena editable property * * @param argumentString

The argument string used to specify this property

*/ - ParkourArenaEditableProperty(@NotNull String argumentString, Function currentValueProvider) { + ParkourArenaEditableProperty(@NotNull String argumentString, Function currentValueProvider, + EditablePropertyType propertyType) { this.argumentString = argumentString; this.currentValueProvider = currentValueProvider; + this.propertyType = propertyType; + } + + /** + * Gets the type of property this editable property represents + * + * @return

The type of this property

+ */ + public EditablePropertyType getPropertyType() { + return this.propertyType; } /** diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaHandler.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaHandler.java index d177879..7e52ff6 100644 --- a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaHandler.java +++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaHandler.java @@ -2,6 +2,8 @@ package net.knarcraft.minigames.arena.parkour; import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.ArenaHandler; +import net.knarcraft.minigames.arena.ArenaPlayerRegistry; +import net.knarcraft.minigames.config.Message; import net.knarcraft.minigames.util.ParkourArenaStorageHelper; import java.io.IOException; @@ -22,7 +24,7 @@ public class ParkourArenaHandler extends ArenaHandlerThe registry keeping track of player sessions

*/ - public ParkourArenaHandler(ParkourArenaPlayerRegistry playerRegistry) { + public ParkourArenaHandler(ArenaPlayerRegistry playerRegistry) { super(playerRegistry); } @@ -31,8 +33,7 @@ public class ParkourArenaHandler extends ArenaHandler(this.arenaGroups.values())); } catch (IOException e) { - MiniGames.log(Level.SEVERE, "Unable to save current arena groups! " + - "Data loss can occur!"); + MiniGames.log(Level.SEVERE, Message.ERROR_CANNOT_SAVE_ARENA_GROUPS.getMessage()); MiniGames.log(Level.SEVERE, e.getMessage()); } } diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaPlayerRegistry.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaPlayerRegistry.java index 0a20c07..f863e18 100644 --- a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaPlayerRegistry.java +++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaPlayerRegistry.java @@ -1,62 +1,15 @@ package net.knarcraft.minigames.arena.parkour; -import net.knarcraft.minigames.arena.ArenaPlayerRegistry; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import net.knarcraft.minigames.arena.AbstractArenaPlayerRegistry; /** * A registry to keep track of which players are playing in which arenas */ -public class ParkourArenaPlayerRegistry implements ArenaPlayerRegistry { +public class ParkourArenaPlayerRegistry extends AbstractArenaPlayerRegistry { - private final Map arenaPlayers = new HashMap<>(); - - /** - * Registers that the given player has started playing the given parkour arena session - * - * @param playerId

The id of the player that started playing

- * @param arena

The arena session to register

- */ - public void registerPlayer(@NotNull UUID playerId, @NotNull ParkourArenaSession arena) { - this.arenaPlayers.put(playerId, arena); - } - - /** - * Removes this player from players currently playing - * - * @param playerId

The id of the player to remove

- */ - public boolean removePlayer(@NotNull UUID playerId) { - return this.arenaPlayers.remove(playerId) != null; - } - - /** - * Gets the player's active parkour arena session - * - * @param playerId

The id of the player to get arena for

- * @return

The player's active arena session, or null if not currently playing

- */ - public @Nullable ParkourArenaSession getArenaSession(@NotNull UUID playerId) { - return this.arenaPlayers.getOrDefault(playerId, null); - } - - /** - * Removes all active sessions for the given arena - * - * @param arena

The arena to remove sessions for

- */ - public void removeForArena(ParkourArena arena) { - for (Map.Entry entry : this.arenaPlayers.entrySet()) { - if (entry.getValue().getArena() == arena) { - // Kick the player gracefully - entry.getValue().triggerQuit(false); - this.arenaPlayers.remove(entry.getKey()); - } - } + @Override + protected String getEntryStateStorageKey() { + return "parkour"; } } diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaSession.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaSession.java index c21fb49..6ed5aac 100644 --- a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaSession.java +++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourArenaSession.java @@ -1,12 +1,10 @@ package net.knarcraft.minigames.arena.parkour; import net.knarcraft.minigames.MiniGames; -import net.knarcraft.minigames.arena.ArenaGameMode; -import net.knarcraft.minigames.arena.ArenaRecordsRegistry; -import net.knarcraft.minigames.arena.ArenaSession; +import net.knarcraft.minigames.arena.AbstractArenaSession; import net.knarcraft.minigames.arena.PlayerEntryState; +import net.knarcraft.minigames.config.Message; import net.knarcraft.minigames.config.ParkourConfiguration; -import net.knarcraft.minigames.property.RecordResult; import net.knarcraft.minigames.util.PlayerTeleporter; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -18,14 +16,11 @@ import java.util.logging.Level; /** * A representation of a player's current session in a parkour arena */ -public class ParkourArenaSession implements ArenaSession { +public class ParkourArenaSession extends AbstractArenaSession { private final @NotNull ParkourArena arena; private final @NotNull Player player; private final @NotNull ParkourArenaGameMode gameMode; - private int deaths; - private final long startTime; - private final PlayerEntryState entryState; private Location reachedCheckpoint = null; /** @@ -37,33 +32,17 @@ public class ParkourArenaSession implements ArenaSession { */ public ParkourArenaSession(@NotNull ParkourArena parkourArena, @NotNull Player player, @NotNull ParkourArenaGameMode gameMode) { + super(parkourArena, player, gameMode); this.arena = parkourArena; this.player = player; this.gameMode = gameMode; - this.deaths = 0; - this.startTime = System.currentTimeMillis(); ParkourConfiguration configuration = MiniGames.getInstance().getParkourConfiguration(); boolean makeInvisible = configuration.makePlayersInvisible(); this.entryState = new ParkourPlayerEntryState(player, makeInvisible); - // Make the player fly to improve mobility in the air this.entryState.setArenaState(); } - @Override - public @NotNull ArenaGameMode getGameMode() { - return this.gameMode; - } - - /** - * Gets the state of the player when they joined the session - * - * @return

The player's entry state

- */ - public @NotNull PlayerEntryState getEntryState() { - return this.entryState; - } - /** * Registers the checkpoint this session's player has reached * @@ -82,12 +61,15 @@ public class ParkourArenaSession implements ArenaSession { return this.reachedCheckpoint; } - /** - * Triggers a win for the player playing in this session - */ + @Override + public @NotNull PlayerEntryState getEntryState() { + return this.entryState; + } + + @Override public void triggerWin() { // Stop this session - stopSession(); + removeSession(); // Check for, and display, records MiniGames miniGames = MiniGames.getInstance(); @@ -99,78 +81,15 @@ public class ParkourArenaSession implements ArenaSession { // Mark the arena as cleared if (this.arena.getData().setCompleted(this.gameMode, this.player)) { - this.player.sendMessage("You cleared the arena!"); + this.player.sendMessage(Message.SUCCESS_ARENA_FIRST_CLEAR.getMessage()); } - this.player.sendMessage("You won!"); + this.player.sendMessage(Message.SUCCESS_ARENA_WIN.getMessage()); // Teleport the player out of the arena teleportToExit(false); } - /** - * Teleports the playing player out of the arena - */ - private void teleportToExit(boolean immediately) { - // Teleport the player out of the arena - Location exitLocation; - if (this.arena.getExitLocation() != null) { - exitLocation = this.arena.getExitLocation(); - } else { - exitLocation = this.entryState.getEntryLocation(); - } - PlayerTeleporter.teleportPlayer(this.player, exitLocation, true, immediately); - } - - /** - * Removes this session from current sessions - */ - private void removeSession() { - // Remove this session for game sessions to stop listeners from fiddling more with the player - boolean removedSession = MiniGames.getInstance().getParkourArenaPlayerRegistry().removePlayer(player.getUniqueId()); - if (!removedSession) { - MiniGames.log(Level.SEVERE, "Unable to remove parkour arena session for " + player.getName() + ". " + - "This will have unintended consequences."); - } - } - - /** - * Registers the player's record if necessary, and prints record information to the player - */ - private void registerRecord() { - ArenaRecordsRegistry recordsRegistry = this.arena.getData().getRecordRegistries().get(this.gameMode); - long timeElapsed = System.currentTimeMillis() - this.startTime; - announceRecord(recordsRegistry.registerTimeRecord(this.player.getUniqueId(), timeElapsed), "time"); - announceRecord(recordsRegistry.registerDeathRecord(this.player.getUniqueId(), this.deaths), "least deaths"); - } - - /** - * Announces a record set by this player - * - * @param recordResult

The result of the record

- * @param type

The type of record set (time or deaths)

- */ - private void announceRecord(@NotNull RecordResult recordResult, @NotNull String type) { - if (recordResult == RecordResult.NONE) { - return; - } - - // Gets a string representation of the played game-mode - String gameModeString = switch (this.gameMode) { - case DEFAULT -> "default"; - }; - - String recordString = "You just set a %s on the %s game-mode!"; - recordString = switch (recordResult) { - case WORLD_RECORD -> String.format(recordString, "new %s record", gameModeString); - case PERSONAL_BEST -> String.format(recordString, "personal %s record", gameModeString); - default -> throw new IllegalStateException("Unexpected value: " + recordResult); - }; - player.sendMessage(String.format(recordString, type)); - } - - /** - * Triggers a loss for the player playing in this session - */ + @Override public void triggerLoss() { this.deaths++; //Teleport the player back to the top @@ -179,45 +98,27 @@ public class ParkourArenaSession implements ArenaSession { this.entryState.setArenaState(); } - /** - * Triggers a quit for the player playing in this session - */ - public void triggerQuit(boolean immediately) { - // Stop this session - stopSession(); - // Teleport the player out of the arena - teleportToExit(immediately); - - player.sendMessage("You quit the arena!"); - } - - /** - * Stops this session, and disables flight mode - */ - private void stopSession() { - // Remove this session from game sessions to stop listeners from fiddling more with the player - removeSession(); - - // Remove flight mode - entryState.restore(); - } - - /** - * Gets the arena this session is being played in - * - * @return

The session's arena

- */ + @Override public @NotNull ParkourArena getArena() { return this.arena; } - /** - * Gets the player playing in this session - * - * @return

This session's player

- */ - public @NotNull Player getPlayer() { - return this.player; + @Override + protected void removeSession() { + // Remove this session for game sessions to stop listeners from fiddling more with the player + boolean removedSession = MiniGames.getInstance().getParkourArenaPlayerRegistry().removePlayer( + player.getUniqueId(), true); + if (!removedSession) { + MiniGames.log(Level.SEVERE, "Unable to remove parkour arena session for " + player.getName() + ". " + + "This will have unintended consequences."); + } + } + + @Override + protected String getGameModeString() { + return switch (this.gameMode) { + case DEFAULT -> "default"; + }; } } diff --git a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourPlayerEntryState.java b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourPlayerEntryState.java index 74d95f2..10e229b 100644 --- a/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourPlayerEntryState.java +++ b/src/main/java/net/knarcraft/minigames/arena/parkour/ParkourPlayerEntryState.java @@ -1,10 +1,15 @@ package net.knarcraft.minigames.arena.parkour; import net.knarcraft.minigames.arena.AbstractPlayerEntryState; +import net.knarcraft.minigames.container.SerializableUUID; import org.bukkit.GameMode; +import org.bukkit.Location; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import java.util.Map; +import java.util.UUID; + /** * The state of a player before entering a parkour arena */ @@ -19,14 +24,60 @@ public class ParkourPlayerEntryState extends AbstractPlayerEntryState { super(player, makePlayerInvisible); } + /** + * Instantiates a new parkour player entry state + * + * @param playerId

The id of the player whose state this should keep track of

+ * @param makePlayerInvisible

Whether players should be made invisible while in the arena

+ * @param entryLocation

The location the player entered from

+ * @param originalIsFlying

Whether the player was flying before entering the arena

+ * @param originalGameMode

The game-mode of the player before entering the arena

+ * @param originalAllowFlight

Whether the player was allowed flight before entering the arena

+ * @param originalInvulnerable

Whether the player was invulnerable before entering the arena

+ * @param originalIsSwimming

Whether the player was swimming before entering the arena

+ * @param originalCollideAble

Whether the player was collide-able before entering the arena

+ */ + public ParkourPlayerEntryState(@NotNull UUID playerId, boolean makePlayerInvisible, Location entryLocation, + boolean originalIsFlying, GameMode originalGameMode, boolean originalAllowFlight, + boolean originalInvulnerable, boolean originalIsSwimming, + boolean originalCollideAble) { + super(playerId, makePlayerInvisible, entryLocation, originalIsFlying, originalGameMode, originalAllowFlight, + originalInvulnerable, originalIsSwimming, originalCollideAble); + } + @Override public void setArenaState() { super.setArenaState(); - this.player.setAllowFlight(false); - this.player.setFlying(false); - this.player.setGameMode(GameMode.ADVENTURE); - this.player.setSwimming(false); - this.player.setCollidable(false); + Player player = getPlayer(); + if (player == null) { + return; + } + player.setAllowFlight(false); + player.setFlying(false); + player.setGameMode(GameMode.ADVENTURE); + player.setSwimming(false); + player.setCollidable(false); + } + + /** + * Deserializes a ParkourPlayerEntryState from the given data + * + * @return

The data to deserialize

+ */ + @SuppressWarnings("unused") + public static ParkourPlayerEntryState deserialize(Map data) { + UUID playerId = ((SerializableUUID) data.get("playerId")).getRawValue(); + boolean makePlayerInvisible = (boolean) data.get("makePlayerInvisible"); + Location entryLocation = (Location) data.get("entryLocation"); + boolean originalIsFlying = (boolean) data.get("originalIsFlying"); + GameMode originalGameMode = GameMode.valueOf((String) data.get("originalGameMode")); + boolean originalAllowFlight = (boolean) data.get("originalAllowFlight"); + boolean originalInvulnerable = (boolean) data.get("originalInvulnerable"); + boolean originalIsSwimming = (boolean) data.get("originalIsSwimming"); + boolean originalCollideAble = (boolean) data.get("originalCollideAble"); + + return new ParkourPlayerEntryState(playerId, makePlayerInvisible, entryLocation, originalIsFlying, + originalGameMode, originalAllowFlight, originalInvulnerable, originalIsSwimming, originalCollideAble); } } diff --git a/src/main/java/net/knarcraft/minigames/command/JoinArenaTabCompleter.java b/src/main/java/net/knarcraft/minigames/command/JoinArenaTabCompleter.java index 8e0c74d..d33828e 100644 --- a/src/main/java/net/knarcraft/minigames/command/JoinArenaTabCompleter.java +++ b/src/main/java/net/knarcraft/minigames/command/JoinArenaTabCompleter.java @@ -11,6 +11,8 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; +import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains; + /** * An abstract class for an arena joining tab-completer */ @@ -34,13 +36,13 @@ public abstract class JoinArenaTabCompleter implements TabCompleter { public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] arguments) { if (arguments.length == 1) { - return arenaNameSupplier.get(); + return filterMatchingContains(arenaNameSupplier.get(), arguments[0]); } else if (arguments.length == 2) { List gameModes = new ArrayList<>(); for (ArenaGameMode gameMode : gameMode.getValues()) { gameModes.add(gameMode.name().toLowerCase()); } - return gameModes; + return filterMatchingContains(gameModes, arguments[1]); } else { return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/minigames/command/LeaveArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/LeaveArenaCommand.java index 3e70afd..177508e 100644 --- a/src/main/java/net/knarcraft/minigames/command/LeaveArenaCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/LeaveArenaCommand.java @@ -2,6 +2,7 @@ package net.knarcraft.minigames.command; import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.ArenaSession; +import net.knarcraft.minigames.config.Message; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; @@ -21,13 +22,13 @@ public class LeaveArenaCommand implements TabExecutor { public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) { if (!(commandSender instanceof Player player)) { - commandSender.sendMessage("This command must be used by a player"); + commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage()); return false; } ArenaSession existingSession = MiniGames.getInstance().getSession(player.getUniqueId()); if (existingSession == null) { - commandSender.sendMessage("You are not in a mini-games arena!"); + commandSender.sendMessage(Message.ERROR_NOT_IN_ARENA.getMessage()); return false; } diff --git a/src/main/java/net/knarcraft/minigames/command/ReloadCommand.java b/src/main/java/net/knarcraft/minigames/command/ReloadCommand.java index cec650a..36168b7 100644 --- a/src/main/java/net/knarcraft/minigames/command/ReloadCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/ReloadCommand.java @@ -1,6 +1,7 @@ package net.knarcraft.minigames.command; import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.config.Message; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; @@ -19,7 +20,7 @@ public class ReloadCommand implements TabExecutor { public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] arguments) { MiniGames.getInstance().reload(); - commandSender.sendMessage("Plugin reloaded!"); + commandSender.sendMessage(Message.SUCCESS_PLUGIN_RELOADED.getMessage()); return true; } diff --git a/src/main/java/net/knarcraft/minigames/command/dropper/CreateDropperArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/CreateDropperArenaCommand.java index 622b0b4..623c133 100644 --- a/src/main/java/net/knarcraft/minigames/command/dropper/CreateDropperArenaCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/dropper/CreateDropperArenaCommand.java @@ -3,6 +3,7 @@ package net.knarcraft.minigames.command.dropper; import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArenaHandler; +import net.knarcraft.minigames.config.Message; import net.knarcraft.minigames.util.StringSanitizer; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -19,7 +20,7 @@ public class CreateDropperArenaCommand implements CommandExecutor { public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] arguments) { if (!(commandSender instanceof Player player)) { - commandSender.sendMessage("This command must be used by a player"); + commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage()); return false; } @@ -40,13 +41,13 @@ public class CreateDropperArenaCommand implements CommandExecutor { DropperArena existingArena = arenaHandler.getArena(arenaName); if (existingArena != null) { - commandSender.sendMessage("There already exists a dropper arena with that name!"); + commandSender.sendMessage(Message.ERROR_ARENA_NAME_COLLISION.getMessage()); return false; } DropperArena arena = new DropperArena(arenaName, player.getLocation(), arenaHandler); arenaHandler.addArena(arena); - commandSender.sendMessage("The arena was successfully created!"); + commandSender.sendMessage(Message.SUCCESS_ARENA_CREATED.getMessage()); return true; } diff --git a/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupListCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupListCommand.java index fe7525d..c571e09 100644 --- a/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupListCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupListCommand.java @@ -4,6 +4,7 @@ import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArenaGroup; import net.knarcraft.minigames.arena.dropper.DropperArenaHandler; +import net.knarcraft.minigames.config.Message; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; @@ -14,6 +15,8 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; +import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains; + /** * The command for listing groups and the stages within */ @@ -58,12 +61,12 @@ public class DropperGroupListCommand implements TabExecutor { @NotNull String groupName) { DropperArenaGroup arenaGroup = arenaHandler.getGroup(groupName); if (arenaGroup == null) { - sender.sendMessage("Unable to find the specified group!"); + sender.sendMessage(Message.ERROR_GROUP_NOT_FOUND.getMessage()); return false; } // Send a list of all stages (arenas in the group) - StringBuilder builder = new StringBuilder(groupName).append("'s stages:").append("\n"); + StringBuilder builder = new StringBuilder(Message.SUCCESS_GROUP_STAGES.getMessage("{group}", groupName)); int counter = 1; for (UUID arenaId : arenaGroup.getArenas()) { DropperArena arena = arenaHandler.getArena(arenaId); @@ -84,7 +87,7 @@ public class DropperGroupListCommand implements TabExecutor { for (DropperArenaGroup group : MiniGames.getInstance().getDropperArenaHandler().getAllGroups()) { groupNames.add(group.getGroupName()); } - return groupNames; + return filterMatchingContains(groupNames, arguments[0]); } else { return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSetCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSetCommand.java index 68c14b1..b1d3008 100644 --- a/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSetCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSetCommand.java @@ -4,6 +4,7 @@ import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArenaGroup; import net.knarcraft.minigames.arena.dropper.DropperArenaHandler; +import net.knarcraft.minigames.config.Message; import net.knarcraft.minigames.util.StringSanitizer; import net.knarcraft.minigames.util.TabCompleteHelper; import org.bukkit.command.Command; @@ -15,6 +16,8 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains; + /** * The command for setting the group of an arena */ @@ -31,7 +34,7 @@ public class DropperGroupSetCommand implements TabExecutor { DropperArena specifiedArena = arenaHandler.getArena(arguments[0]); if (specifiedArena == null) { - commandSender.sendMessage("Unable to find the specified dropper arena."); + commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage()); return false; } @@ -53,7 +56,7 @@ public class DropperGroupSetCommand implements TabExecutor { arenaHandler.setGroup(specifiedArena.getArenaId(), arenaGroup); - commandSender.sendMessage("The arena's group has been updated"); + commandSender.sendMessage(Message.SUCCESS_ARENA_GROUP_UPDATED.getMessage()); return true; } @@ -62,7 +65,7 @@ public class DropperGroupSetCommand implements TabExecutor { public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] arguments) { if (arguments.length == 1) { - return TabCompleteHelper.getDropperArenas(); + return filterMatchingContains(TabCompleteHelper.getDropperArenas(), arguments[0]); } else if (arguments.length == 2) { List possibleValues = new ArrayList<>(); possibleValues.add("none"); @@ -70,7 +73,7 @@ public class DropperGroupSetCommand implements TabExecutor { for (DropperArenaGroup group : MiniGames.getInstance().getDropperArenaHandler().getAllGroups()) { possibleValues.add(group.getGroupName()); } - return possibleValues; + return filterMatchingContains(possibleValues, arguments[1]); } else { return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSwapCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSwapCommand.java index 4de5f2b..f1832ab 100644 --- a/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSwapCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/dropper/DropperGroupSwapCommand.java @@ -4,6 +4,7 @@ import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArenaGroup; import net.knarcraft.minigames.arena.dropper.DropperArenaHandler; +import net.knarcraft.minigames.config.Message; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; @@ -14,6 +15,8 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; +import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains; + /** * The command for swapping the order of two arenas in a group */ @@ -30,26 +33,26 @@ public class DropperGroupSwapCommand implements TabExecutor { DropperArena arena1 = arenaHandler.getArena(arguments[0]); if (arena1 == null) { - commandSender.sendMessage("Unable to find the first specified dropper arena."); + commandSender.sendMessage(Message.ERROR_ARENA_1_NOT_FOUND.getMessage()); return false; } DropperArena arena2 = arenaHandler.getArena(arguments[1]); if (arena2 == null) { - commandSender.sendMessage("Unable to find the second specified dropper arena."); + commandSender.sendMessage(Message.ERROR_ARENA_2_NOT_FOUND.getMessage()); return false; } DropperArenaGroup arena1Group = arenaHandler.getGroup(arena1.getArenaId()); DropperArenaGroup arena2Group = arenaHandler.getGroup(arena2.getArenaId()); if (arena1Group == null || !arena1Group.equals(arena2Group)) { - commandSender.sendMessage("You cannot swap arenas in different groups!"); + commandSender.sendMessage(Message.ERROR_SWAP_DIFFERENT_GROUPS.getMessage()); return false; } arena1Group.swapArenas(arena1Group.getArenas().indexOf(arena1.getArenaId()), arena1Group.getArenas().indexOf(arena2.getArenaId())); - commandSender.sendMessage("The arenas have been swapped!"); + commandSender.sendMessage(Message.SUCCESS_ARENAS_SWAPPED.getMessage()); return true; } @@ -63,9 +66,9 @@ public class DropperGroupSwapCommand implements TabExecutor { for (DropperArena dropperArena : arenaHandler.getArenasInAGroup()) { arenaNames.add(dropperArena.getArenaName()); } - return arenaNames; + return filterMatchingContains(arenaNames, arguments[0]); } else if (arguments.length == 2) { - return getArenaNamesInSameGroup(arguments[0]); + return filterMatchingContains(getArenaNamesInSameGroup(arguments[0]), arguments[1]); } else { return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaCommand.java index 2e6eb30..7152f30 100644 --- a/src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaCommand.java @@ -4,6 +4,8 @@ import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty; import net.knarcraft.minigames.config.DropperConfiguration; +import net.knarcraft.minigames.config.Message; +import net.knarcraft.minigames.container.PlaceholderContainer; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.command.Command; @@ -32,7 +34,7 @@ public class EditDropperArenaCommand implements CommandExecutor { public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] arguments) { if (!(commandSender instanceof Player player)) { - commandSender.sendMessage("This command must be used by a player"); + commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage()); return false; } @@ -42,29 +44,29 @@ public class EditDropperArenaCommand implements CommandExecutor { DropperArena specifiedArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]); if (specifiedArena == null) { - commandSender.sendMessage("Unable to find the specified dropper arena."); + commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage()); return false; } DropperArenaEditableProperty editableProperty = DropperArenaEditableProperty.getFromArgumentString(arguments[1]); if (editableProperty == null) { - commandSender.sendMessage("Unknown property specified."); + commandSender.sendMessage(Message.ERROR_UNKNOWN_PROPERTY.getMessage()); return false; } - String currentValueFormat = "Current value of %s is: %s"; - if (arguments.length < 3) { // Print the current value of the property String value = editableProperty.getCurrentValueAsString(specifiedArena); - commandSender.sendMessage(String.format(currentValueFormat, editableProperty.getArgumentString(), value)); + commandSender.sendMessage(Message.SUCCESS_CURRENT_VALUE.getMessage(new PlaceholderContainer().add( + "{property}", editableProperty.getArgumentString()).add("{value}", value))); return true; } else { boolean successful = changeValue(specifiedArena, editableProperty, arguments[2], player); if (successful) { - player.sendMessage(String.format("Property %s changed to: %s", editableProperty, arguments[2])); + player.sendMessage(Message.SUCCESS_PROPERTY_CHANGED.getMessage("{property}", + editableProperty.getArgumentString())); } else { - player.sendMessage("Unable to change the property. Make sure your input is valid!"); + player.sendMessage(Message.ERROR_PROPERTY_INPUT_INVALID.getMessage()); } return successful; } diff --git a/src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaTabCompleter.java b/src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaTabCompleter.java index 0f85127..2488606 100644 --- a/src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaTabCompleter.java +++ b/src/main/java/net/knarcraft/minigames/command/dropper/EditDropperArenaTabCompleter.java @@ -1,5 +1,6 @@ package net.knarcraft.minigames.command.dropper; +import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty; import net.knarcraft.minigames.util.TabCompleteHelper; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -10,6 +11,8 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains; + /** * The tab-completer for the edit arena command */ @@ -17,14 +20,18 @@ public class EditDropperArenaTabCompleter implements TabCompleter { @Override public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, - @NotNull String label, @NotNull String[] args) { - if (args.length == 1) { - return TabCompleteHelper.getDropperArenas(); - } else if (args.length == 2) { - return TabCompleteHelper.getDropperArenaProperties(); - } else if (args.length == 3) { - //TODO: Tab-complete possible values for the given property - return null; + @NotNull String label, @NotNull String[] arguments) { + if (arguments.length == 1) { + return filterMatchingContains(TabCompleteHelper.getDropperArenas(), arguments[0]); + } else if (arguments.length == 2) { + return filterMatchingContains(TabCompleteHelper.getDropperArenaProperties(), arguments[1]); + } else if (arguments.length == 3) { + DropperArenaEditableProperty property = DropperArenaEditableProperty.getFromArgumentString(arguments[1]); + if (property == null) { + return new ArrayList<>(); + } + return filterMatchingContains(TabCompleteHelper.getTabCompleteSuggestions(property.getPropertyType()), + arguments[2]); } else { return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/minigames/command/dropper/JoinDropperArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/JoinDropperArenaCommand.java index 155e99e..a5c4b6e 100644 --- a/src/main/java/net/knarcraft/minigames/command/dropper/JoinDropperArenaCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/dropper/JoinDropperArenaCommand.java @@ -1,12 +1,13 @@ package net.knarcraft.minigames.command.dropper; import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.arena.ArenaPlayerRegistry; import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode; import net.knarcraft.minigames.arena.dropper.DropperArenaGroup; -import net.knarcraft.minigames.arena.dropper.DropperArenaPlayerRegistry; import net.knarcraft.minigames.arena.dropper.DropperArenaSession; import net.knarcraft.minigames.config.DropperConfiguration; +import net.knarcraft.minigames.config.Message; import net.knarcraft.minigames.util.PlayerTeleporter; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -23,7 +24,7 @@ public class JoinDropperArenaCommand implements CommandExecutor { public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] arguments) { if (!(commandSender instanceof Player player)) { - commandSender.sendMessage("This command must be used by a player"); + commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage()); return false; } @@ -33,20 +34,20 @@ public class JoinDropperArenaCommand implements CommandExecutor { // Disallow joining if the player is already in a mini-game arena if (MiniGames.getInstance().getSession(player.getUniqueId()) != null) { - commandSender.sendMessage("You are already playing a mini-game!"); + commandSender.sendMessage(Message.ERROR_ALREADY_PLAYING.getMessage()); return false; } // Make sure the arena exists DropperArena specifiedArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]); if (specifiedArena == null) { - commandSender.sendMessage("Unable to find the specified dropper arena."); + commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage()); return false; } // Deny vehicles as allowing this is tricky, and will cause problems in some cases if (player.isInsideVehicle() || !player.getPassengers().isEmpty()) { - commandSender.sendMessage("You cannot join an arena while inside a vehicle or carrying a passenger."); + commandSender.sendMessage(Message.ERROR_JOIN_IN_VEHICLE_OR_PASSENGER.getMessage()); return false; } @@ -80,25 +81,25 @@ public class JoinDropperArenaCommand implements CommandExecutor { if (MiniGames.getInstance().getDropperConfiguration().mustDoNormalModeFirst() && gameMode != DropperArenaGameMode.DEFAULT && specifiedArena.getData().hasNotCompleted(DropperArenaGameMode.DEFAULT, player)) { - player.sendMessage("You must complete this arena in normal mode first!"); + player.sendMessage(Message.ERROR_NORMAL_MODE_REQUIRED.getMessage()); return false; } // Register the player's session DropperArenaSession newSession = new DropperArenaSession(specifiedArena, player, gameMode); - DropperArenaPlayerRegistry playerRegistry = MiniGames.getInstance().getDropperArenaPlayerRegistry(); + ArenaPlayerRegistry playerRegistry = MiniGames.getInstance().getDropperArenaPlayerRegistry(); playerRegistry.registerPlayer(player.getUniqueId(), newSession); // Try to teleport the player to the arena boolean teleported = PlayerTeleporter.teleportPlayer(player, specifiedArena.getSpawnLocation(), false, false); if (!teleported) { - player.sendMessage("Unable to teleport you to the dropper arena. Make sure you're not in a vehicle," + - "and not carrying a passenger!"); + player.sendMessage(Message.ERROR_ARENA_TELEPORT_FAILED.getMessage()); newSession.triggerQuit(false); return false; } else { // Make sure to update the state again in the air to remove a potential swimming state newSession.getEntryState().setArenaState(); + player.sendMessage(Message.SUCCESS_ARENA_JOINED.getMessage()); return true; } } @@ -118,14 +119,14 @@ public class JoinDropperArenaCommand implements CommandExecutor { // Require that players beat all arenas in the group in the normal game-mode before trying challenge modes if (configuration.mustDoNormalModeFirst() && arenaGameMode != DropperArenaGameMode.DEFAULT && !arenaGroup.hasBeatenAll(DropperArenaGameMode.DEFAULT, player)) { - player.sendMessage("You have not yet beaten all arenas in this group!"); + player.sendMessage(Message.ERROR_GROUP_NORMAL_MODE_REQUIRED.getMessage()); return false; } // Require that the player has beaten the previous arena on the same game-mode before trying this one if (configuration.mustDoGroupedInSequence() && arenaGroup.cannotPlay(arenaGameMode, player, dropperArena.getArenaId())) { - player.sendMessage("You have not yet beaten the previous arena!"); + player.sendMessage(Message.ERROR_PREVIOUS_ARENA_REQUIRED.getMessage()); return false; } diff --git a/src/main/java/net/knarcraft/minigames/command/dropper/ListDropperArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/ListDropperArenaCommand.java index 16e6487..36f6f52 100644 --- a/src/main/java/net/knarcraft/minigames/command/dropper/ListDropperArenaCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/dropper/ListDropperArenaCommand.java @@ -1,5 +1,6 @@ package net.knarcraft.minigames.command.dropper; +import net.knarcraft.minigames.config.Message; import net.knarcraft.minigames.util.TabCompleteHelper; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -18,10 +19,11 @@ public class ListDropperArenaCommand implements TabExecutor { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] arguments) { - sender.sendMessage("Dropper arenas:"); + StringBuilder builder = new StringBuilder(Message.SUCCESS_DROPPER_ARENAS_LIST.getMessage()); for (String arenaName : TabCompleteHelper.getDropperArenas()) { - sender.sendMessage(arenaName); + builder.append("\n").append(arenaName); } + sender.sendMessage(builder.toString()); return true; } diff --git a/src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaCommand.java index 861a537..0d3be62 100644 --- a/src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaCommand.java @@ -2,6 +2,7 @@ package net.knarcraft.minigames.command.dropper; import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.dropper.DropperArena; +import net.knarcraft.minigames.config.Message; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -23,13 +24,13 @@ public class RemoveDropperArenaCommand implements CommandExecutor { // Get the specified arena DropperArena targetArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]); if (targetArena == null) { - commandSender.sendMessage("Unable to find the specified arena"); + commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage()); return false; } // Remove the arena MiniGames.getInstance().getDropperArenaHandler().removeArena(targetArena); - commandSender.sendMessage("The specified arena has been successfully removed"); + commandSender.sendMessage(Message.SUCCESS_ARENA_REMOVED.getMessage()); return true; } diff --git a/src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaTabCompleter.java b/src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaTabCompleter.java index b12234f..1143e3e 100644 --- a/src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaTabCompleter.java +++ b/src/main/java/net/knarcraft/minigames/command/dropper/RemoveDropperArenaTabCompleter.java @@ -10,6 +10,8 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains; + /** * The tab-completer for the remove arena command */ @@ -20,7 +22,7 @@ public class RemoveDropperArenaTabCompleter implements TabCompleter { public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] arguments) { if (arguments.length == 1) { - return TabCompleteHelper.getDropperArenas(); + return filterMatchingContains(TabCompleteHelper.getDropperArenas(), arguments[0]); } else { return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/CreateParkourArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/CreateParkourArenaCommand.java index 0cb281c..08be9ad 100644 --- a/src/main/java/net/knarcraft/minigames/command/parkour/CreateParkourArenaCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/parkour/CreateParkourArenaCommand.java @@ -3,6 +3,7 @@ package net.knarcraft.minigames.command.parkour; import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler; +import net.knarcraft.minigames.config.Message; import net.knarcraft.minigames.util.StringSanitizer; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -19,7 +20,7 @@ public class CreateParkourArenaCommand implements CommandExecutor { public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] arguments) { if (!(commandSender instanceof Player player)) { - commandSender.sendMessage("This command must be used by a player"); + commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage()); return false; } @@ -40,13 +41,13 @@ public class CreateParkourArenaCommand implements CommandExecutor { ParkourArena existingArena = arenaHandler.getArena(arenaName); if (existingArena != null) { - commandSender.sendMessage("There already exists a parkour arena with that name!"); + commandSender.sendMessage(Message.ERROR_ARENA_NAME_COLLISION.getMessage()); return false; } ParkourArena arena = new ParkourArena(arenaName, player.getLocation(), arenaHandler); arenaHandler.addArena(arena); - commandSender.sendMessage("The arena was successfully created!"); + commandSender.sendMessage(Message.SUCCESS_ARENA_CREATED.getMessage()); return true; } diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaCommand.java index 8c563cb..8eb128e 100644 --- a/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaCommand.java @@ -3,6 +3,8 @@ package net.knarcraft.minigames.command.parkour; import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty; +import net.knarcraft.minigames.config.Message; +import net.knarcraft.minigames.container.PlaceholderContainer; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.command.Command; @@ -29,7 +31,7 @@ public class EditParkourArenaCommand implements CommandExecutor { public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] arguments) { if (!(commandSender instanceof Player player)) { - commandSender.sendMessage("This command must be used by a player"); + commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage()); return false; } @@ -39,29 +41,29 @@ public class EditParkourArenaCommand implements CommandExecutor { ParkourArena specifiedArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]); if (specifiedArena == null) { - commandSender.sendMessage("Unable to find the specified dropper arena."); + commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage()); return false; } ParkourArenaEditableProperty editableProperty = ParkourArenaEditableProperty.getFromArgumentString(arguments[1]); if (editableProperty == null) { - commandSender.sendMessage("Unknown property specified."); + commandSender.sendMessage(Message.ERROR_UNKNOWN_PROPERTY.getMessage()); return false; } - String currentValueFormat = "Current value of %s is: %s"; - if (arguments.length < 3) { // Print the current value of the property String value = editableProperty.getCurrentValueAsString(specifiedArena); - commandSender.sendMessage(String.format(currentValueFormat, editableProperty.getArgumentString(), value)); + commandSender.sendMessage(Message.SUCCESS_CURRENT_VALUE.getMessage(new PlaceholderContainer().add( + "{property}", editableProperty.getArgumentString()).add("{value}", value))); return true; } else { boolean successful = changeValue(specifiedArena, editableProperty, arguments[2], player); if (successful) { - player.sendMessage(String.format("Property %s changed to: %s", editableProperty, arguments[2])); + player.sendMessage(Message.SUCCESS_PROPERTY_CHANGED.getMessage("{property}", + editableProperty.getArgumentString())); } else { - player.sendMessage("Unable to change the property. Make sure your input is valid!"); + player.sendMessage(Message.ERROR_PROPERTY_INPUT_INVALID.getMessage()); } return successful; } diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaTabCompleter.java b/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaTabCompleter.java index a9270e7..3a0a7f0 100644 --- a/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaTabCompleter.java +++ b/src/main/java/net/knarcraft/minigames/command/parkour/EditParkourArenaTabCompleter.java @@ -1,5 +1,6 @@ package net.knarcraft.minigames.command.parkour; +import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty; import net.knarcraft.minigames.util.TabCompleteHelper; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -10,6 +11,8 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains; + /** * The tab-completer for the edit arena command */ @@ -17,14 +20,18 @@ public class EditParkourArenaTabCompleter implements TabCompleter { @Override public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, - @NotNull String label, @NotNull String[] args) { - if (args.length == 1) { - return TabCompleteHelper.getParkourArenas(); - } else if (args.length == 2) { - return TabCompleteHelper.getParkourArenaProperties(); - } else if (args.length == 3) { - //TODO: Tab-complete possible values for the given property - return null; + @NotNull String label, @NotNull String[] arguments) { + if (arguments.length == 1) { + return filterMatchingContains(TabCompleteHelper.getParkourArenas(), arguments[0]); + } else if (arguments.length == 2) { + return filterMatchingContains(TabCompleteHelper.getParkourArenaProperties(), arguments[1]); + } else if (arguments.length == 3) { + ParkourArenaEditableProperty property = ParkourArenaEditableProperty.getFromArgumentString(arguments[1]); + if (property == null) { + return new ArrayList<>(); + } + return filterMatchingContains(TabCompleteHelper.getTabCompleteSuggestions(property.getPropertyType()), + arguments[2]); } else { return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/JoinParkourArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/JoinParkourArenaCommand.java index 2174511..0067375 100644 --- a/src/main/java/net/knarcraft/minigames/command/parkour/JoinParkourArenaCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/parkour/JoinParkourArenaCommand.java @@ -1,11 +1,12 @@ package net.knarcraft.minigames.command.parkour; import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.arena.ArenaPlayerRegistry; import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode; import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup; -import net.knarcraft.minigames.arena.parkour.ParkourArenaPlayerRegistry; import net.knarcraft.minigames.arena.parkour.ParkourArenaSession; +import net.knarcraft.minigames.config.Message; import net.knarcraft.minigames.config.ParkourConfiguration; import net.knarcraft.minigames.util.PlayerTeleporter; import org.bukkit.command.Command; @@ -23,7 +24,7 @@ public class JoinParkourArenaCommand implements CommandExecutor { public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] arguments) { if (!(commandSender instanceof Player player)) { - commandSender.sendMessage("This command must be used by a player"); + commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage()); return false; } @@ -33,20 +34,20 @@ public class JoinParkourArenaCommand implements CommandExecutor { // Disallow joining if the player is already in a mini-game arena if (MiniGames.getInstance().getSession(player.getUniqueId()) != null) { - commandSender.sendMessage("You are already playing a mini-game!"); + commandSender.sendMessage(Message.ERROR_ALREADY_PLAYING.getMessage()); return false; } // Make sure the arena exists ParkourArena specifiedArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]); if (specifiedArena == null) { - commandSender.sendMessage("Unable to find the specified parkour arena."); + commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage()); return false; } // Deny vehicles as allowing this is tricky, and will cause problems in some cases if (player.isInsideVehicle() || !player.getPassengers().isEmpty()) { - commandSender.sendMessage("You cannot join an arena while inside a vehicle or carrying a passenger."); + commandSender.sendMessage(Message.ERROR_JOIN_IN_VEHICLE_OR_PASSENGER.getMessage()); return false; } @@ -78,19 +79,19 @@ public class JoinParkourArenaCommand implements CommandExecutor { // Register the player's session ParkourArenaSession newSession = new ParkourArenaSession(specifiedArena, player, gameMode); - ParkourArenaPlayerRegistry playerRegistry = MiniGames.getInstance().getParkourArenaPlayerRegistry(); + ArenaPlayerRegistry playerRegistry = MiniGames.getInstance().getParkourArenaPlayerRegistry(); playerRegistry.registerPlayer(player.getUniqueId(), newSession); // Try to teleport the player to the arena boolean teleported = PlayerTeleporter.teleportPlayer(player, specifiedArena.getSpawnLocation(), false, false); if (!teleported) { - player.sendMessage("Unable to teleport you to the parkour arena. Make sure you're not in a vehicle," + - "and not carrying a passenger!"); + player.sendMessage(Message.ERROR_ARENA_TELEPORT_FAILED.getMessage()); newSession.triggerQuit(false); return false; } else { // Make sure to update the state again in the air to remove a potential swimming state newSession.getEntryState().setArenaState(); + player.sendMessage(Message.SUCCESS_ARENA_JOINED.getMessage()); return true; } } @@ -111,7 +112,7 @@ public class JoinParkourArenaCommand implements CommandExecutor { // Require that the player has beaten the previous arena on the same game-mode before trying this one if (configuration.mustDoGroupedInSequence() && arenaGroup.cannotPlay(arenaGameMode, player, parkourArena.getArenaId())) { - player.sendMessage("You have not yet beaten the previous arena!"); + player.sendMessage(Message.ERROR_PREVIOUS_ARENA_REQUIRED.getMessage()); return false; } diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/ListParkourArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/ListParkourArenaCommand.java index 212f5db..884f9b3 100644 --- a/src/main/java/net/knarcraft/minigames/command/parkour/ListParkourArenaCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/parkour/ListParkourArenaCommand.java @@ -1,5 +1,6 @@ package net.knarcraft.minigames.command.parkour; +import net.knarcraft.minigames.config.Message; import net.knarcraft.minigames.util.TabCompleteHelper; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -18,10 +19,11 @@ public class ListParkourArenaCommand implements TabExecutor { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] arguments) { - sender.sendMessage("Parkour arenas:"); + StringBuilder builder = new StringBuilder(Message.SUCCESS_PARKOUR_ARENAS_LIST.getMessage()); for (String arenaName : TabCompleteHelper.getParkourArenas()) { - sender.sendMessage(arenaName); + builder.append("\n").append(arenaName); } + sender.sendMessage(builder.toString()); return true; } diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupListCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupListCommand.java index 7dd31dd..8e69d65 100644 --- a/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupListCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupListCommand.java @@ -4,6 +4,7 @@ import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup; import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler; +import net.knarcraft.minigames.config.Message; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; @@ -15,6 +16,8 @@ import java.util.List; import java.util.Set; import java.util.UUID; +import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains; + /** * The command for listing groups and the stages within */ @@ -59,17 +62,17 @@ public class ParkourGroupListCommand implements TabExecutor { @NotNull String groupName) { ParkourArenaGroup arenaGroup = arenaHandler.getGroup(groupName); if (arenaGroup == null) { - sender.sendMessage("Unable to find the specified group!"); + sender.sendMessage(Message.ERROR_GROUP_NOT_FOUND.getMessage()); return false; } // Send a list of all stages (arenas in the group) - StringBuilder builder = new StringBuilder(groupName).append("'s stages:").append("\n"); + StringBuilder builder = new StringBuilder(Message.SUCCESS_GROUP_STAGES.getMessage("{group}", groupName)); int counter = 1; for (UUID arenaId : arenaGroup.getArenas()) { ParkourArena arena = arenaHandler.getArena(arenaId); if (arena != null) { - builder.append(counter++).append(". ").append(arena.getArenaName()).append("\n"); + builder.append("\n").append(counter++).append(". ").append(arena.getArenaName()); } } sender.sendMessage(builder.toString()); @@ -86,7 +89,7 @@ public class ParkourGroupListCommand implements TabExecutor { for (ParkourArenaGroup group : arenaGroups) { groupNames.add(group.getGroupName()); } - return groupNames; + return filterMatchingContains(groupNames, arguments[0]); } else { return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSetCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSetCommand.java index 14a66e2..7a60fae 100644 --- a/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSetCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSetCommand.java @@ -4,6 +4,7 @@ import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup; import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler; +import net.knarcraft.minigames.config.Message; import net.knarcraft.minigames.util.StringSanitizer; import net.knarcraft.minigames.util.TabCompleteHelper; import org.bukkit.command.Command; @@ -15,6 +16,8 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains; + /** * The command for setting the group of an arena */ @@ -31,7 +34,7 @@ public class ParkourGroupSetCommand implements TabExecutor { ParkourArena specifiedArena = arenaHandler.getArena(arguments[0]); if (specifiedArena == null) { - commandSender.sendMessage("Unable to find the specified parkour arena."); + commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage()); return false; } @@ -53,7 +56,7 @@ public class ParkourGroupSetCommand implements TabExecutor { arenaHandler.setGroup(specifiedArena.getArenaId(), arenaGroup); - commandSender.sendMessage("The arena's group has been updated"); + commandSender.sendMessage(Message.SUCCESS_ARENA_GROUP_UPDATED.getMessage()); return true; } @@ -62,7 +65,7 @@ public class ParkourGroupSetCommand implements TabExecutor { public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] arguments) { if (arguments.length == 1) { - return TabCompleteHelper.getParkourArenas(); + return filterMatchingContains(TabCompleteHelper.getParkourArenas(), arguments[0]); } else if (arguments.length == 2) { List possibleValues = new ArrayList<>(); possibleValues.add("none"); @@ -70,7 +73,7 @@ public class ParkourGroupSetCommand implements TabExecutor { for (ParkourArenaGroup group : MiniGames.getInstance().getParkourArenaHandler().getAllGroups()) { possibleValues.add(group.getGroupName()); } - return possibleValues; + return filterMatchingContains(possibleValues, arguments[1]); } else { return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSwapCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSwapCommand.java index 66a8174..a0c9784 100644 --- a/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSwapCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/parkour/ParkourGroupSwapCommand.java @@ -4,6 +4,7 @@ import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup; import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler; +import net.knarcraft.minigames.config.Message; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabExecutor; @@ -14,6 +15,8 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; +import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains; + /** * The command for swapping the order of two arenas in a group */ @@ -30,26 +33,26 @@ public class ParkourGroupSwapCommand implements TabExecutor { ParkourArena arena1 = arenaHandler.getArena(arguments[0]); if (arena1 == null) { - commandSender.sendMessage("Unable to find the first specified parkour arena."); + commandSender.sendMessage(Message.ERROR_ARENA_1_NOT_FOUND.getMessage()); return false; } ParkourArena arena2 = arenaHandler.getArena(arguments[1]); if (arena2 == null) { - commandSender.sendMessage("Unable to find the second specified parkour arena."); + commandSender.sendMessage(Message.ERROR_ARENA_2_NOT_FOUND.getMessage()); return false; } ParkourArenaGroup arena1Group = arenaHandler.getGroup(arena1.getArenaId()); ParkourArenaGroup arena2Group = arenaHandler.getGroup(arena2.getArenaId()); if (arena1Group == null || !arena1Group.equals(arena2Group)) { - commandSender.sendMessage("You cannot swap arenas in different groups!"); + commandSender.sendMessage(Message.ERROR_SWAP_DIFFERENT_GROUPS.getMessage()); return false; } arena1Group.swapArenas(arena1Group.getArenas().indexOf(arena1.getArenaId()), arena1Group.getArenas().indexOf(arena2.getArenaId())); - commandSender.sendMessage("The arenas have been swapped!"); + commandSender.sendMessage(Message.SUCCESS_ARENAS_SWAPPED.getMessage()); return true; } @@ -63,9 +66,9 @@ public class ParkourGroupSwapCommand implements TabExecutor { for (ParkourArena parkourArena : arenaHandler.getArenasInAGroup()) { arenaNames.add(parkourArena.getArenaName()); } - return arenaNames; + return filterMatchingContains(arenaNames, arguments[0]); } else if (arguments.length == 2) { - return getArenaNamesInSameGroup(arguments[0]); + return filterMatchingContains(getArenaNamesInSameGroup(arguments[0]), arguments[1]); } else { return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaCommand.java b/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaCommand.java index 7aaa7c8..dc4ee3e 100644 --- a/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaCommand.java +++ b/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaCommand.java @@ -2,6 +2,7 @@ package net.knarcraft.minigames.command.parkour; import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.parkour.ParkourArena; +import net.knarcraft.minigames.config.Message; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -23,13 +24,13 @@ public class RemoveParkourArenaCommand implements CommandExecutor { // Get the specified arena ParkourArena targetArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]); if (targetArena == null) { - commandSender.sendMessage("Unable to find the specified arena"); + commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage()); return false; } // Remove the arena MiniGames.getInstance().getParkourArenaHandler().removeArena(targetArena); - commandSender.sendMessage("The specified arena has been successfully removed"); + commandSender.sendMessage(Message.SUCCESS_ARENA_REMOVED.getMessage()); return true; } diff --git a/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaTabCompleter.java b/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaTabCompleter.java index 9605a81..87c0787 100644 --- a/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaTabCompleter.java +++ b/src/main/java/net/knarcraft/minigames/command/parkour/RemoveParkourArenaTabCompleter.java @@ -10,6 +10,8 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains; + /** * The tab-completer for the remove arena command */ @@ -20,7 +22,7 @@ public class RemoveParkourArenaTabCompleter implements TabCompleter { public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] arguments) { if (arguments.length == 1) { - return TabCompleteHelper.getParkourArenas(); + return filterMatchingContains(TabCompleteHelper.getParkourArenas(), arguments[0]); } else { return new ArrayList<>(); } diff --git a/src/main/java/net/knarcraft/minigames/config/Message.java b/src/main/java/net/knarcraft/minigames/config/Message.java new file mode 100644 index 0000000..d9cebf9 --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/config/Message.java @@ -0,0 +1,300 @@ +package net.knarcraft.minigames.config; + +import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.container.PlaceholderContainer; +import net.knarcraft.minigames.util.ColorHelper; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +/** + * A message which ca be displayed to the user + */ +public enum Message { + + /* ************** * + * Error messages * + * ************** */ + + /** + * The message displayed if saving arena groups fails + */ + ERROR_CANNOT_SAVE_ARENA_GROUPS("&cUnable to save current arena groups! Data loss can occur!"), + + /** + * The message displayed if an un-parse-able message is given by a user + */ + ERROR_MATERIAL_NOT_PARSE_ABLE("&cUnable to parse material: {material}"), + + /** + * The message displayed if a player tries to be teleported to/from an arena with a passenger + */ + ERROR_TELEPORT_WITH_PASSENGER("&cYou cannot be teleported with a passenger!"), + + /** + * The message displayed if a player tries to be teleported to/from an arena with a vehicle + */ + ERROR_TELEPORT_IN_VEHICLE("&cYou cannot be teleported while in a vehicle"), + + /** + * The message displayed if an arena cannot be loaded + */ + ERROR_ARENA_NOT_LOADED("&cCould not load the arena at configuration section {section}. Please check " + + "the {file} storage file for issues."), + + /** + * The message displayed if an arena's data cannot be loaded + */ + ERROR_ARENA_DATA_NOT_LOADED("&cUnable to load arena data for dropper arena: {arena}"), + + /** + * The message displayed if the user specifies an unrecognized arena + */ + ERROR_ARENA_NOT_FOUND("&cUnable to find the specified arena."), + + /** + * The message displayed if the user specifies an unrecognized group + */ + ERROR_GROUP_NOT_FOUND("&cUnable to find the specified group!"), + + /** + * The message displayed if the console tries to execute a player-only command + */ + ERROR_PLAYER_ONLY("&cThis command must be used by a player"), + + /** + * The message displayed if the name of an arena is duplicated + */ + ERROR_ARENA_NAME_COLLISION("&cThere already exists an arena with that name!"), + + /** + * The message displayed if the player is required to win on the default difficulty first + */ + ERROR_NORMAL_MODE_REQUIRED("&cYou must complete this arena in normal mode first!"), + + /** + * The message displayed if the player is required to win on the default difficulty for all arenas in the group first + */ + ERROR_GROUP_NORMAL_MODE_REQUIRED("&cYou have not yet beaten the default game-mode for all arenas in this group!"), + + /** + * The message displayed if the player is required to beat the previous arena in the group + */ + ERROR_PREVIOUS_ARENA_REQUIRED("&cYou have not yet beaten the previous arena!"), + + /** + * The message displayed if player teleportation failed for some reason + */ + ERROR_ARENA_TELEPORT_FAILED("&cUnable to teleport you to the arena."), + + /** + * The message displayed if the player tries to quit the arena while not in an arena + */ + ERROR_NOT_IN_ARENA("&cYou are not in a mini-games arena!"), + + /** + * The message displayed if the player tries to join an arena while already playing + * + *

This should in theory be impossible, as players cannot use any commands except /miniGamesLeave while playing + * in an arena.

+ */ + ERROR_ALREADY_PLAYING("&cYou are already playing a mini-game!"), + + /** + * The message displayed if a player tries to join an arena with a passenger or riding a vehicle + */ + ERROR_JOIN_IN_VEHICLE_OR_PASSENGER("&cYou cannot join an arena while inside a vehicle or carrying a passenger."), + + /** + * The message displayed if the player tries to change an unrecognized arena property + */ + ERROR_UNKNOWN_PROPERTY("&cUnknown property specified."), + + /** + * The message displayed if the given input to /dEdit or /pEdit's value is invalid + */ + ERROR_PROPERTY_INPUT_INVALID("&cUnable to change the property. Make sure your input is valid!"), + + /** + * The message displayed if the first arena specified in /dgSwap or /pgSwap is invalid + */ + ERROR_ARENA_1_NOT_FOUND("&cUnable to find the first specified arena."), + + /** + * The message displayed if the second arena specified in /dgSwap or /pgSwap is invalid + */ + ERROR_ARENA_2_NOT_FOUND("&cUnable to find the second specified dropper arena."), + + /** + * The message displayed if the two groups specified for /dgSwap or /pgSwap are in different arenas + */ + ERROR_SWAP_DIFFERENT_GROUPS("&cYou cannot swap arenas in different groups!"), + + /** + * The message displayed if a player tries to use any command other than /mLeave while in an arena + */ + ERROR_ILLEGAL_COMMAND("&cYou cannot use that command while in an arena!"), + + /* **************** * + * Success messages * + * **************** */ + + /** + * The message displayed if an arena's group has been changed + */ + SUCCESS_ARENA_GROUP_UPDATED("&aThe arena's group has been updated"), + + /** + * The message displayed if the MiniGames plugin is reloaded + */ + SUCCESS_PLUGIN_RELOADED("&aPlugin reloaded!"), + + /** + * The message displayed if a new arena has been created + */ + SUCCESS_ARENA_CREATED("&aThe arena was successfully created!"), + + /** + * The message displayed if a player clears/wins an arena for the first time + */ + SUCCESS_ARENA_FIRST_CLEAR("&aYou cleared the arena!"), + + /** + * The message displayed when a player wins an arena + */ + SUCCESS_ARENA_WIN("&aYou won!"), + + /** + * The message displayed when a player quits an arena + */ + SUCCESS_ARENA_QUIT("&aYou quit the arena!"), + + /** + * The message used to display the current value of an arena property + */ + SUCCESS_CURRENT_VALUE("&aCurrent value of {property} is: {value}"), + + /** + * The message used to announce that an arena property has been changed + */ + SUCCESS_PROPERTY_CHANGED("&aProperty {property} successfully changed"), + + /** + * The message displayed when two arenas' order in a group have been swapped + */ + SUCCESS_ARENAS_SWAPPED("&aThe arenas have been swapped!"), + + /** + * The message displayed when an arena has been removed + */ + SUCCESS_ARENA_REMOVED("&aThe specified arena has been successfully removed"), + + /** + * The header displayed before listing all dropper arenas + */ + SUCCESS_DROPPER_ARENAS_LIST("&aDropper arenas:&r"), + + /** + * The header displayed before listing all parkour arenas + */ + SUCCESS_PARKOUR_ARENAS_LIST("&aParkour arenas:&r"), + + /** + * The message displayed when a player reaches a new checkpoint in a parkour arena + */ + SUCCESS_CHECKPOINT_REACHED("&aCheckpoint reached!"), + + /** + * The header displayed before listing all arenas (stages) in a group + */ + SUCCESS_GROUP_STAGES("&a{group}'s stages:&r"), + + /** + * The message displayed when a new record has been achieved + */ + SUCCESS_RECORD_ACHIEVED("&aYou just set a {recordInfo} on the {gameMode} game-mode!"), + + /** + * The partial message used to describe that the player achieved a world record + */ + RECORD_ACHIEVED_GLOBAL("new {recordType} record"), + + /** + * The partial message used to describe that the player achieved a personal best record + */ + RECORD_ACHIEVED_PERSONAL("personal {recordType} record"), + + /** + * The message displayed when a player joins an arena + */ + SUCCESS_ARENA_JOINED("&aYou joined the arena."), + ; + + private final @NotNull String defaultMessage; + + /** + * Instantiates a new message + * + * @param defaultMessage

The default value of the message

+ */ + Message(@NotNull String defaultMessage) { + this.defaultMessage = defaultMessage; + } + + /** + * Gets the message this enum represents + * + * @return

The formatted message

+ */ + public @NotNull String getMessage() { + return formatMessage(this.defaultMessage); + } + + /** + * Gets the message this enum represents + * + * @param placeholder

The placeholder to replace

+ * @param replacement

The replacement to use

+ * @return

The formatted message

+ */ + public @NotNull String getMessage(@NotNull String placeholder, @NotNull String replacement) { + return formatMessage(this.defaultMessage.replace(placeholder, replacement)); + } + + /** + * Gets the message this enum represents, intended for display within another message + * + * @param placeholder

The placeholder to replace

+ * @param replacement

The replacement to use

+ * @return

The formatted message

+ */ + public @NotNull String getPartialMessage(@NotNull String placeholder, @NotNull String replacement) { + return ColorHelper.translateAllColorCodes(this.defaultMessage.replace(placeholder, replacement)); + } + + /** + * Gets the message this enum represents + * + * @param placeholders

The placeholder -> replacement map specifying necessary replacements

+ * @return

The formatted message

+ */ + public @NotNull String getMessage(@NotNull PlaceholderContainer placeholders) { + String replaced = this.defaultMessage; + for (Map.Entry entry : placeholders.getPlaceholders().entrySet()) { + replaced = replaced.replace(entry.getKey(), entry.getValue()); + } + return formatMessage(replaced); + } + + /** + * Gets the formatted version of the given message + * + * @param message

The message to format

+ * @return

The formatted message

+ */ + private @NotNull String formatMessage(@NotNull String message) { + String prefix = MiniGames.getInstance().getDescription().getPrefix(); + return ColorHelper.translateAllColorCodes("#546EED[&r&l" + prefix + "#546EED]&r " + message); + } + +} diff --git a/src/main/java/net/knarcraft/minigames/container/PlaceholderContainer.java b/src/main/java/net/knarcraft/minigames/container/PlaceholderContainer.java new file mode 100644 index 0000000..ca88158 --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/container/PlaceholderContainer.java @@ -0,0 +1,41 @@ +package net.knarcraft.minigames.container; + +import java.util.HashMap; +import java.util.Map; + +/** + * A container for keeping track of several placeholder to value mappings + */ +public class PlaceholderContainer { + + private final Map placeholders; + + /** + * Instantiates a new placeholder container + */ + public PlaceholderContainer() { + this.placeholders = new HashMap<>(); + } + + /** + * Gets all placeholders + * + * @return

All placeholders

+ */ + public Map getPlaceholders() { + return new HashMap<>(this.placeholders); + } + + /** + * Adds a new placeholder + * + * @param placeholder

The placeholder to register

+ * @param value

The value of the placeholder

+ * @return

This object

+ */ + public PlaceholderContainer add(String placeholder, String value) { + this.placeholders.put(placeholder, value); + return this; + } + +} diff --git a/src/main/java/net/knarcraft/minigames/listener/CommandListener.java b/src/main/java/net/knarcraft/minigames/listener/CommandListener.java index 05391d8..112231d 100644 --- a/src/main/java/net/knarcraft/minigames/listener/CommandListener.java +++ b/src/main/java/net/knarcraft/minigames/listener/CommandListener.java @@ -2,6 +2,7 @@ package net.knarcraft.minigames.listener; import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.ArenaSession; +import net.knarcraft.minigames.config.Message; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -40,7 +41,7 @@ public class CommandListener implements Listener { } } - player.sendMessage("You cannot use that command while in an arena!"); + player.sendMessage(Message.ERROR_ILLEGAL_COMMAND.getMessage()); event.setCancelled(true); } diff --git a/src/main/java/net/knarcraft/minigames/listener/MoveListener.java b/src/main/java/net/knarcraft/minigames/listener/MoveListener.java index c9c2120..42f7630 100644 --- a/src/main/java/net/knarcraft/minigames/listener/MoveListener.java +++ b/src/main/java/net/knarcraft/minigames/listener/MoveListener.java @@ -8,6 +8,7 @@ import net.knarcraft.minigames.arena.dropper.DropperArenaSession; import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArenaSession; import net.knarcraft.minigames.config.DropperConfiguration; +import net.knarcraft.minigames.config.Message; import net.knarcraft.minigames.config.ParkourConfiguration; import net.knarcraft.minigames.config.SharedConfiguration; import org.bukkit.Location; @@ -80,19 +81,30 @@ public class MoveListener implements Listener { List checkpoints = arena.getCheckpoints(); for (Location checkpoint : checkpoints) { Location previousCheckpoint = arenaSession.getRegisteredCheckpoint(); - if (checkpoint.getBlock().equals(event.getTo().getBlock()) && !checkpoint.equals(previousCheckpoint)) { - if (parkourConfiguration.enforceCheckpointOrder()) { - int checkpointIndex = checkpoints.indexOf(checkpoint); - int previousIndex = previousCheckpoint == null ? -1 : checkpoints.indexOf(previousCheckpoint); - if (checkpointIndex - previousIndex != 1) { - continue; - } - } - arenaSession.registerCheckpoint(checkpoint.clone()); - event.getPlayer().sendMessage("Checkpoint reached!"); + // Skip if checkpoint has not been reached + if (!checkpoint.getBlock().equals(event.getTo().getBlock())) { + continue; + } + + // If the checkpoint is the same as the previously reached one, abort + if (previousCheckpoint != null && checkpoint.getBlock().equals(previousCheckpoint.getBlock())) { return; } + + // If not the correct checkpoint according to the enforced order, abort + if (parkourConfiguration.enforceCheckpointOrder()) { + int checkpointIndex = checkpoints.indexOf(checkpoint); + int previousIndex = previousCheckpoint == null ? -1 : checkpoints.indexOf(previousCheckpoint); + if (checkpointIndex - previousIndex != 1) { + return; + } + } + + // Register the checkpoint + arenaSession.registerCheckpoint(checkpoint.clone()); + event.getPlayer().sendMessage(Message.SUCCESS_CHECKPOINT_REACHED.getMessage()); + return; } } diff --git a/src/main/java/net/knarcraft/minigames/listener/PlayerLeaveListener.java b/src/main/java/net/knarcraft/minigames/listener/PlayerLeaveListener.java deleted file mode 100644 index 7ca6239..0000000 --- a/src/main/java/net/knarcraft/minigames/listener/PlayerLeaveListener.java +++ /dev/null @@ -1,79 +0,0 @@ -package net.knarcraft.minigames.listener; - -import net.knarcraft.minigames.MiniGames; -import net.knarcraft.minigames.arena.ArenaSession; -import net.knarcraft.minigames.arena.parkour.ParkourArenaSession; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.event.player.PlayerTeleportEvent; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.logging.Level; - -/** - * A listener for players leaving the server or the arena - */ -public class PlayerLeaveListener implements Listener { - - private final Map leftSessions = new HashMap<>(); - - @EventHandler - public void onPlayerLeave(PlayerQuitEvent event) { - Player player = event.getPlayer(); - - ArenaSession arenaSession = MiniGames.getInstance().getSession(event.getPlayer().getUniqueId()); - if (arenaSession == null) { - return; - } - - MiniGames.log(Level.WARNING, "Found player " + player.getUniqueId() + - " leaving in the middle of a session!"); - leftSessions.put(player.getUniqueId(), arenaSession); - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - UUID playerId = event.getPlayer().getUniqueId(); - // Force the player to quit from the session once they re-join - if (leftSessions.containsKey(playerId)) { - MiniGames.log(Level.WARNING, "Found un-exited dropper session!"); - Bukkit.getScheduler().runTaskLater(MiniGames.getInstance(), () -> { - leftSessions.get(playerId).triggerQuit(false); - MiniGames.log(Level.WARNING, "Triggered a quit!"); - leftSessions.remove(playerId); - }, 80); - } - } - - @EventHandler - public void onPlayerTeleport(PlayerTeleportEvent event) { - Location targetLocation = event.getTo(); - if (targetLocation == null || event.isCancelled()) { - return; - } - - ArenaSession arenaSession = MiniGames.getInstance().getSession(event.getPlayer().getUniqueId()); - if (arenaSession == null) { - return; - } - - if (targetLocation.equals(arenaSession.getArena().getSpawnLocation())) { - return; - } - - if (arenaSession instanceof ParkourArenaSession parkourArenaSession && - targetLocation.equals(parkourArenaSession.getRegisteredCheckpoint())) { - return; - } - - arenaSession.triggerQuit(false); - } - -} diff --git a/src/main/java/net/knarcraft/minigames/listener/PlayerStateChangeListener.java b/src/main/java/net/knarcraft/minigames/listener/PlayerStateChangeListener.java new file mode 100644 index 0000000..3c5e48f --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/listener/PlayerStateChangeListener.java @@ -0,0 +1,92 @@ +package net.knarcraft.minigames.listener; + +import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.arena.ArenaPlayerRegistry; +import net.knarcraft.minigames.arena.ArenaSession; +import net.knarcraft.minigames.arena.PlayerEntryState; +import net.knarcraft.minigames.arena.parkour.ParkourArenaSession; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.spigotmc.event.player.PlayerSpawnLocationEvent; + +import java.util.logging.Level; + +/** + * A listener for players leaving/joining the server, or leaving the server unexpectedly + */ +public class PlayerStateChangeListener implements Listener { + + @EventHandler + public void onPlayerSpawn(PlayerSpawnLocationEvent event) { + Player player = event.getPlayer(); + + // Restore any lingering arena states + Location restoreLocation; + restoreLocation = restoreStateIfNecessary(player, MiniGames.getInstance().getDropperArenaPlayerRegistry()); + if (restoreLocation != null) { + event.setSpawnLocation(restoreLocation); + } + restoreLocation = restoreStateIfNecessary(player, MiniGames.getInstance().getParkourArenaPlayerRegistry()); + if (restoreLocation != null) { + event.setSpawnLocation(restoreLocation); + } + } + + /** + * Prevent the player from teleporting away from an arena for any reason + * + * @param event

The triggered teleport event

+ */ + @EventHandler(ignoreCancelled = true) + public void onPlayerTeleport(PlayerTeleportEvent event) { + Location targetLocation = event.getTo(); + if (targetLocation == null) { + return; + } + + // Ignore if not in an arena session + ArenaSession arenaSession = MiniGames.getInstance().getSession(event.getPlayer().getUniqueId()); + if (arenaSession == null) { + return; + } + + // If teleported to the arena's spawn, it's fine + if (targetLocation.equals(arenaSession.getArena().getSpawnLocation())) { + return; + } + + // If teleported to the arena's checkpoint, it's fine + if (arenaSession instanceof ParkourArenaSession parkourArenaSession && + targetLocation.equals(parkourArenaSession.getRegisteredCheckpoint())) { + return; + } + + event.setCancelled(true); + } + + /** + * Restores the state of the given player if a lingering session is found in the given player registry + * + * @param player

The player whose state should be checked

+ * @param playerRegistry

The registry to check for a lingering state

+ * @return

The location the player should spawn in, or null if not restored

+ */ + private Location restoreStateIfNecessary(Player player, ArenaPlayerRegistry playerRegistry) { + PlayerEntryState entryState = playerRegistry.getEntryState(player.getUniqueId()); + if (entryState != null) { + MiniGames.log(Level.INFO, "Found existing state for joining player " + player + + ". Attempting to restore the player's state."); + playerRegistry.removePlayer(player.getUniqueId(), false); + + Bukkit.getScheduler().runTaskLater(MiniGames.getInstance(), () -> entryState.restore(player), 1); + return entryState.getEntryLocation(); + } else { + return null; + } + } + +} diff --git a/src/main/java/net/knarcraft/minigames/util/ArenaStorageHelper.java b/src/main/java/net/knarcraft/minigames/util/ArenaStorageHelper.java index c64ad1d..9abfd2d 100644 --- a/src/main/java/net/knarcraft/minigames/util/ArenaStorageHelper.java +++ b/src/main/java/net/knarcraft/minigames/util/ArenaStorageHelper.java @@ -1,18 +1,68 @@ package net.knarcraft.minigames.util; import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.arena.PlayerEntryState; +import org.bukkit.configuration.file.YamlConfiguration; import org.jetbrains.annotations.NotNull; import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.logging.Level; +/** + * A helper class for dealing with arena storage + */ public final class ArenaStorageHelper { private ArenaStorageHelper() { } + /** + * Stores the given entry states to disk + * + * @param key

The key specifying the correct entry state file

+ * @param entryStates

The entry states to save

+ */ + public static void storeArenaPlayerEntryStates(String key, Set entryStates) { + YamlConfiguration configuration = new YamlConfiguration(); + configuration.set(key, new ArrayList<>(entryStates)); + + try { + configuration.save(new File(MiniGames.getInstance().getDataFolder(), key + "EntryStates.yml")); + } catch (IOException e) { + MiniGames.log(Level.SEVERE, "Unable to save entry states to disk"); + } + } + + /** + * Gets saved entry states from disk + * + * @param key

The key specifying the correct entry state file

+ * @return

The previously saved entry states

+ */ + public static Set getArenaPlayerEntryStates(String key) { + File entryStateFile = new File(MiniGames.getInstance().getDataFolder(), key + "EntryStates.yml"); + if (!entryStateFile.exists()) { + return new HashSet<>(); + } + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(entryStateFile); + Set output = new HashSet<>(); + + List entries = configuration.getList(key, new ArrayList<>()); + for (Object entry : entries) { + if (entry instanceof PlayerEntryState entryState) { + output.add(entryState); + } + } + return output; + } + /** * Gets the file used to store the given arena id's data * @@ -20,7 +70,7 @@ public final class ArenaStorageHelper { * @param arenaId

The id of the arena to get a data file for

* @return

The file the arena's data is/should be stored in

*/ - static @NotNull File getArenaDataFile(File root, @NotNull UUID arenaId) { + public static @NotNull File getArenaDataFile(File root, @NotNull UUID arenaId) { File arenaDataFile = new File(root, arenaId + ".yml"); if (!root.exists() && !root.mkdirs()) { MiniGames.log(Level.SEVERE, "Unable to create the arena data directories"); diff --git a/src/main/java/net/knarcraft/minigames/util/ColorHelper.java b/src/main/java/net/knarcraft/minigames/util/ColorHelper.java new file mode 100644 index 0000000..4b79d19 --- /dev/null +++ b/src/main/java/net/knarcraft/minigames/util/ColorHelper.java @@ -0,0 +1,29 @@ +package net.knarcraft.minigames.util; + +import net.md_5.bungee.api.ChatColor; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A helper class for converting colors + */ +public class ColorHelper { + + /** + * Translates all found color codes to formatting in a string + * + * @param message

The string to search for color codes

+ * @return

The message with color codes translated

+ */ + public static String translateAllColorCodes(String message) { + message = ChatColor.translateAlternateColorCodes('&', message); + Pattern pattern = Pattern.compile("&?(#[a-fA-F0-9]{6})"); + Matcher matcher = pattern.matcher(message); + while (matcher.find()) { + message = message.replace(matcher.group(), "" + ChatColor.of(matcher.group(1))); + } + return message; + } + +} diff --git a/src/main/java/net/knarcraft/minigames/util/DropperArenaStorageHelper.java b/src/main/java/net/knarcraft/minigames/util/DropperArenaStorageHelper.java index b158669..524b736 100644 --- a/src/main/java/net/knarcraft/minigames/util/DropperArenaStorageHelper.java +++ b/src/main/java/net/knarcraft/minigames/util/DropperArenaStorageHelper.java @@ -9,6 +9,8 @@ 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.config.Message; +import net.knarcraft.minigames.container.PlaceholderContainer; import net.knarcraft.minigames.container.SerializableMaterial; import net.knarcraft.minigames.container.SerializableUUID; import org.bukkit.Location; @@ -159,8 +161,8 @@ public final class DropperArenaStorageHelper { 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."); + MiniGames.log(Level.SEVERE, Message.ERROR_ARENA_NOT_LOADED.getMessage(new PlaceholderContainer().add( + "{section}", configurationSection.getName()).add("{file}", "dropper_arenas"))); return null; } if (winBlockType == null) { @@ -170,7 +172,8 @@ public final class DropperArenaStorageHelper { // 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); + MiniGames.log(Level.SEVERE, Message.ERROR_ARENA_DATA_NOT_LOADED.getMessage("{arena}", + arenaId.toString())); arenaData = getEmptyDropperData(arenaId); } diff --git a/src/main/java/net/knarcraft/minigames/util/MaterialHelper.java b/src/main/java/net/knarcraft/minigames/util/MaterialHelper.java index 821ba9a..2a50b5a 100644 --- a/src/main/java/net/knarcraft/minigames/util/MaterialHelper.java +++ b/src/main/java/net/knarcraft/minigames/util/MaterialHelper.java @@ -1,6 +1,7 @@ package net.knarcraft.minigames.util; import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.config.Message; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; @@ -43,7 +44,7 @@ public final class MaterialHelper { if (matched != null) { parsedMaterials.add(matched); } else { - MiniGames.log(Level.WARNING, "Unable to parse: " + string); + MiniGames.log(Level.WARNING, Message.ERROR_MATERIAL_NOT_PARSE_ABLE.getMessage("{material}", string)); } } return parsedMaterials; diff --git a/src/main/java/net/knarcraft/minigames/util/ParkourArenaStorageHelper.java b/src/main/java/net/knarcraft/minigames/util/ParkourArenaStorageHelper.java index a1d2aff..375ab4b 100644 --- a/src/main/java/net/knarcraft/minigames/util/ParkourArenaStorageHelper.java +++ b/src/main/java/net/knarcraft/minigames/util/ParkourArenaStorageHelper.java @@ -9,6 +9,8 @@ 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.config.Message; +import net.knarcraft.minigames.container.PlaceholderContainer; import net.knarcraft.minigames.container.SerializableMaterial; import net.knarcraft.minigames.container.SerializableUUID; import org.bukkit.Location; @@ -164,8 +166,8 @@ public final class ParkourArenaStorageHelper { // 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."); + MiniGames.log(Level.SEVERE, Message.ERROR_ARENA_NOT_LOADED.getMessage(new PlaceholderContainer().add( + "{section}", configurationSection.getName()).add("{file}", "parkour_arena"))); return null; } @@ -177,7 +179,8 @@ public final class ParkourArenaStorageHelper { // 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); + MiniGames.log(Level.SEVERE, Message.ERROR_ARENA_DATA_NOT_LOADED.getMessage("{arena}", + arenaId.toString())); arenaData = getEmptyParkourData(arenaId); } diff --git a/src/main/java/net/knarcraft/minigames/util/PlayerTeleporter.java b/src/main/java/net/knarcraft/minigames/util/PlayerTeleporter.java index 4265ec5..974a042 100644 --- a/src/main/java/net/knarcraft/minigames/util/PlayerTeleporter.java +++ b/src/main/java/net/knarcraft/minigames/util/PlayerTeleporter.java @@ -1,6 +1,7 @@ package net.knarcraft.minigames.util; import net.knarcraft.minigames.MiniGames; +import net.knarcraft.minigames.config.Message; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Entity; @@ -35,7 +36,7 @@ public final class PlayerTeleporter { passenger.teleport(location); } } else { - player.sendMessage("You cannot be teleported with a passenger!"); + player.sendMessage(Message.ERROR_TELEPORT_WITH_PASSENGER.getMessage()); return false; } } @@ -45,16 +46,16 @@ public final class PlayerTeleporter { player.eject(); vehicle.teleport(location); } else { - player.sendMessage("You cannot be teleported while in a vehicle"); + player.sendMessage(Message.ERROR_TELEPORT_IN_VEHICLE.getMessage()); return false; } } - //Stop the existing player velocity to prevent unevenness between players + // Stop the existing player velocity to prevent unevenness between players player.setVelocity(new Vector(0, 0, 0)); player.setInvulnerable(true); player.teleport(location); player.setVelocity(new Vector(0, 0, 0)); - //When teleporting a player out of the arena, sometimes the move listener is slow to react, giving the player + // When teleporting a player out of the arena, sometimes the move listener is slow to react, giving the player // lethal velocity, and causing damage. That's why the player is given 5 ticks of invulnerability if (!immediately) { Bukkit.getScheduler().runTaskLater(MiniGames.getInstance(), () -> player.setInvulnerable(false), 5); diff --git a/src/main/java/net/knarcraft/minigames/util/TabCompleteHelper.java b/src/main/java/net/knarcraft/minigames/util/TabCompleteHelper.java index d537525..fee509a 100644 --- a/src/main/java/net/knarcraft/minigames/util/TabCompleteHelper.java +++ b/src/main/java/net/knarcraft/minigames/util/TabCompleteHelper.java @@ -3,18 +3,24 @@ package net.knarcraft.minigames.util; import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.arena.Arena; import net.knarcraft.minigames.arena.ArenaHandler; +import net.knarcraft.minigames.arena.EditablePropertyType; import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty; import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty; +import org.bukkit.Material; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * A helper-class for common tab-completions */ public final class TabCompleteHelper { + private static Map> tabCompleteSuggestions; + private TabCompleteHelper() { } @@ -76,4 +82,137 @@ public final class TabCompleteHelper { return arenaProperties; } + /** + * Finds tab complete values that contain the typed text + * + * @param values

The values to filter

+ * @param typedText

The text the player has started typing

+ * @return

The given string values that contain the player's typed text

+ */ + public static List filterMatchingContains(@NotNull List values, @NotNull String typedText) { + List configValues = new ArrayList<>(); + for (String value : values) { + if (value.toLowerCase().contains(typedText.toLowerCase())) { + configValues.add(value); + } + } + return configValues; + } + + /** + * Gets tab-complete suggestions for the given property type + * + * @param propertyType

The property type to get suggestions for

+ * @return

The suggestions produced

+ */ + public static List getTabCompleteSuggestions(EditablePropertyType propertyType) { + if (tabCompleteSuggestions == null) { + tabCompleteSuggestions = new HashMap<>(); + tabCompleteSuggestions.put(EditablePropertyType.LOCATION, getLocationSuggestions()); + tabCompleteSuggestions.put(EditablePropertyType.ARENA_NAME, getNameSuggestions()); + tabCompleteSuggestions.put(EditablePropertyType.HORIZONTAL_VELOCITY, getHorizontalVelocitySuggestions()); + tabCompleteSuggestions.put(EditablePropertyType.VERTICAL_VELOCITY, getVerticalVelocitySuggestions()); + tabCompleteSuggestions.put(EditablePropertyType.BLOCK_TYPE, getBlockTypeSuggestions()); + tabCompleteSuggestions.put(EditablePropertyType.CHECKPOINT_CLEAR, getCheckpointClearSuggestions()); + tabCompleteSuggestions.put(EditablePropertyType.MATERIAL_LIST, getMaterialListSuggestions()); + } + + return tabCompleteSuggestions.get(propertyType); + } + + /** + * Gets suggestions for a list of materials + * + * @return

A list of suggestions

+ */ + private static List getMaterialListSuggestions() { + List suggestions = new ArrayList<>(); + suggestions.add("LAVA,MAGMA_BLOCK"); + suggestions.add("WATER,MAGMA_BLOCK,LAVA,+BUTTONS,+CORALS"); + return suggestions; + } + + /** + * Gets suggestions for checkpointClear + * + * @return

A list of suggestions

+ */ + private static List getCheckpointClearSuggestions() { + List suggestions = new ArrayList<>(); + suggestions.add("true"); + return suggestions; + } + + /** + * Gets suggestions for a block material + * + * @return

A list of suggestions

+ */ + private static List getBlockTypeSuggestions() { + List materials = new ArrayList<>(); + for (Material material : Material.values()) { + if (material.isBlock()) { + materials.add(material.name()); + } + } + return materials; + } + + /** + * Gets suggestions for a vertical velocity + * + * @return

A list of suggestions

+ */ + private static List getVerticalVelocitySuggestions() { + List velocities = new ArrayList<>(); + velocities.add("0.01"); + velocities.add("0.5"); + velocities.add("1"); + velocities.add("3"); + velocities.add("10"); + velocities.add("30"); + velocities.add("75"); + return velocities; + } + + /** + * Gets suggestions for a horizontal velocity + * + * @return

A list of suggestions

+ */ + private static List getHorizontalVelocitySuggestions() { + List velocities = new ArrayList<>(); + velocities.add("0.01"); + velocities.add("0.1"); + velocities.add("0.5"); + velocities.add("1"); + return velocities; + } + + /** + * Gets suggestions for an arena name + * + * @return

A list of suggestions

+ */ + private static List getNameSuggestions() { + List locations = new ArrayList<>(); + locations.add("DropperArena1"); + locations.add("DropperArena2"); + locations.add("ParkourArena1"); + locations.add("ParkourArena2"); + return locations; + } + + /** + * Gets suggestions for a location + * + * @return

A list of suggestions

+ */ + private static List getLocationSuggestions() { + List locations = new ArrayList<>(); + locations.add("here"); + locations.add("x,y,z"); + return locations; + } + } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 917919f..c548273 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,4 +1,5 @@ name: MiniGames +prefix: MiniGames version: '${project.version}' main: net.knarcraft.minigames.MiniGames api-version: 1.19