Merge pull request #26 from SunNetservers/dev

Improves messages, tab-completion
This commit is contained in:
Kristian Knarvik 2023-04-26 15:48:03 +00:00 committed by GitHub
commit 701cdd81eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 1618 additions and 692 deletions

View File

@ -1,19 +1,23 @@
package net.knarcraft.minigames; package net.knarcraft.minigames;
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
import net.knarcraft.minigames.arena.ArenaSession; 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.DropperArenaData;
import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode; import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup; import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
import net.knarcraft.minigames.arena.dropper.DropperArenaHandler; import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
import net.knarcraft.minigames.arena.dropper.DropperArenaPlayerRegistry; import net.knarcraft.minigames.arena.dropper.DropperArenaPlayerRegistry;
import net.knarcraft.minigames.arena.dropper.DropperArenaRecordsRegistry; 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.ParkourArenaData;
import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode; import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode;
import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup; import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler; import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
import net.knarcraft.minigames.arena.parkour.ParkourArenaPlayerRegistry; import net.knarcraft.minigames.arena.parkour.ParkourArenaPlayerRegistry;
import net.knarcraft.minigames.arena.parkour.ParkourArenaRecordsRegistry; 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.IntegerRecord;
import net.knarcraft.minigames.arena.record.LongRecord; import net.knarcraft.minigames.arena.record.LongRecord;
import net.knarcraft.minigames.command.LeaveArenaCommand; 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.CommandListener;
import net.knarcraft.minigames.listener.DamageListener; import net.knarcraft.minigames.listener.DamageListener;
import net.knarcraft.minigames.listener.MoveListener; 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.DropperRecordExpansion;
import net.knarcraft.minigames.placeholder.ParkourRecordExpansion; import net.knarcraft.minigames.placeholder.ParkourRecordExpansion;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -56,7 +60,6 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand; import org.bukkit.command.PluginCommand;
import org.bukkit.command.TabCompleter; import org.bukkit.command.TabCompleter;
import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -76,11 +79,11 @@ public final class MiniGames extends JavaPlugin {
private DropperConfiguration dropperConfiguration; private DropperConfiguration dropperConfiguration;
private ParkourConfiguration parkourConfiguration; private ParkourConfiguration parkourConfiguration;
private DropperArenaHandler dropperArenaHandler; private DropperArenaHandler dropperArenaHandler;
private DropperArenaPlayerRegistry dropperArenaPlayerRegistry; private ArenaPlayerRegistry<DropperArena> dropperArenaPlayerRegistry;
private DropperRecordExpansion dropperRecordExpansion; private DropperRecordExpansion dropperRecordExpansion;
private ParkourRecordExpansion parkourRecordExpansion; private ParkourRecordExpansion parkourRecordExpansion;
private ParkourArenaHandler parkourArenaHandler; private ParkourArenaHandler parkourArenaHandler;
private ParkourArenaPlayerRegistry parkourArenaPlayerRegistry; private ArenaPlayerRegistry<ParkourArena> parkourArenaPlayerRegistry;
/** /**
* Gets an instance of this plugin * Gets an instance of this plugin
@ -114,7 +117,7 @@ public final class MiniGames extends JavaPlugin {
* *
* @return <p>A dropper arena player registry</p> * @return <p>A dropper arena player registry</p>
*/ */
public DropperArenaPlayerRegistry getDropperArenaPlayerRegistry() { public ArenaPlayerRegistry<DropperArena> getDropperArenaPlayerRegistry() {
return this.dropperArenaPlayerRegistry; return this.dropperArenaPlayerRegistry;
} }
@ -123,7 +126,7 @@ public final class MiniGames extends JavaPlugin {
* *
* @return <p>A parkour arena player registry</p> * @return <p>A parkour arena player registry</p>
*/ */
public ParkourArenaPlayerRegistry getParkourArenaPlayerRegistry() { public ArenaPlayerRegistry<ParkourArena> getParkourArenaPlayerRegistry() {
return this.parkourArenaPlayerRegistry; return this.parkourArenaPlayerRegistry;
} }
@ -163,7 +166,7 @@ public final class MiniGames extends JavaPlugin {
* @return <p>The player's current session, or null if not found</p> * @return <p>The player's current session, or null if not found</p>
*/ */
public @Nullable ArenaSession getSession(@NotNull UUID playerId) { public @Nullable ArenaSession getSession(@NotNull UUID playerId) {
DropperArenaSession dropperArenaSession = dropperArenaPlayerRegistry.getArenaSession(playerId); ArenaSession dropperArenaSession = dropperArenaPlayerRegistry.getArenaSession(playerId);
if (dropperArenaSession != null) { if (dropperArenaSession != null) {
return dropperArenaSession; return dropperArenaSession;
} }
@ -216,6 +219,8 @@ public final class MiniGames extends JavaPlugin {
ConfigurationSerialization.registerClass(ParkourArenaData.class); ConfigurationSerialization.registerClass(ParkourArenaData.class);
ConfigurationSerialization.registerClass(ParkourArenaGroup.class); ConfigurationSerialization.registerClass(ParkourArenaGroup.class);
ConfigurationSerialization.registerClass(ParkourArenaGameMode.class); ConfigurationSerialization.registerClass(ParkourArenaGameMode.class);
ConfigurationSerialization.registerClass(DropperPlayerEntryState.class);
ConfigurationSerialization.registerClass(ParkourPlayerEntryState.class);
} }
@Override @Override
@ -239,7 +244,7 @@ public final class MiniGames extends JavaPlugin {
PluginManager pluginManager = getServer().getPluginManager(); PluginManager pluginManager = getServer().getPluginManager();
pluginManager.registerEvents(new DamageListener(), this); pluginManager.registerEvents(new DamageListener(), this);
pluginManager.registerEvents(new MoveListener(this.dropperConfiguration, this.parkourConfiguration), 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); pluginManager.registerEvents(new CommandListener(), this);
registerCommand("miniGamesReload", new ReloadCommand(), null); registerCommand("miniGamesReload", new ReloadCommand(), null);
@ -277,12 +282,12 @@ public final class MiniGames extends JavaPlugin {
@Override @Override
public void onDisable() { public void onDisable() {
// Throw out currently playing players before exiting // Kill all sessions before exiting
for (Player player : getServer().getOnlinePlayers()) { for (DropperArena arena : dropperArenaHandler.getArenas().values()) {
ArenaSession session = getSession(player.getUniqueId()); dropperArenaPlayerRegistry.removeForArena(arena, true);
if (session != null) { }
session.triggerQuit(true); for (ParkourArena arena : parkourArenaHandler.getArenas().values()) {
} parkourArenaPlayerRegistry.removeForArena(arena, true);
} }
} }

View File

@ -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 <K> <p>The type of arena stored</p>
*/
public abstract class AbstractArenaPlayerRegistry<K extends Arena> implements ArenaPlayerRegistry<K> {
private final Map<UUID, ArenaSession> arenaPlayers = new HashMap<>();
private final Map<UUID, PlayerEntryState> 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<UUID, ArenaSession> 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 <p>A unique key used for entry state storage</p>
*/
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<PlayerEntryState> 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.");
}
}
}

View File

@ -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 <p>The arena that's being played in</p>
* @param player <p>The player playing the arena</p>
* @param gameMode <p>The game-mode</p>
*/
public 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 <p>The result of the record</p>
* @param type <p>The type of record set (time or deaths)</p>
*/
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 <p>The string representation</p>
*/
protected abstract String getGameModeString();
/**
* Removes this session from current sessions
*/
protected abstract void removeSession();
}

View File

@ -1,5 +1,8 @@
package net.knarcraft.minigames.arena; 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.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -7,12 +10,17 @@ import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull; 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 * An abstract representation of a player's entry state
*/ */
public abstract class AbstractPlayerEntryState implements PlayerEntryState { public abstract class AbstractPlayerEntryState implements PlayerEntryState {
protected final Player player; protected final UUID playerId;
private final boolean makePlayerInvisible; private final boolean makePlayerInvisible;
private final Location entryLocation; private final Location entryLocation;
private final boolean originalIsFlying; private final boolean originalIsFlying;
@ -29,7 +37,7 @@ public abstract class AbstractPlayerEntryState implements PlayerEntryState {
* @param makePlayerInvisible <p>Whether players should be made invisible while in the arena</p> * @param makePlayerInvisible <p>Whether players should be made invisible while in the arena</p>
*/ */
public AbstractPlayerEntryState(@NotNull Player player, boolean makePlayerInvisible) { public AbstractPlayerEntryState(@NotNull Player player, boolean makePlayerInvisible) {
this.player = player; this.playerId = player.getUniqueId();
this.makePlayerInvisible = makePlayerInvisible; this.makePlayerInvisible = makePlayerInvisible;
this.entryLocation = player.getLocation().clone(); this.entryLocation = player.getLocation().clone();
this.originalIsFlying = player.isFlying(); this.originalIsFlying = player.isFlying();
@ -40,24 +48,71 @@ public abstract class AbstractPlayerEntryState implements PlayerEntryState {
this.originalCollideAble = player.isCollidable(); this.originalCollideAble = player.isCollidable();
} }
/**
* Instantiates a new abstract player entry state
*
* @param playerId <p>The id of the player whose state this should keep track of</p>
* @param makePlayerInvisible <p>Whether players should be made invisible while in the arena</p>
* @param entryLocation <p>The location the player entered from</p>
* @param originalIsFlying <p>Whether the player was flying before entering the arena</p>
* @param originalGameMode <p>The game-mode of the player before entering the arena</p>
* @param originalAllowFlight <p>Whether the player was allowed flight before entering the arena</p>
* @param originalInvulnerable <p>Whether the player was invulnerable before entering the arena</p>
* @param originalIsSwimming <p>Whether the player was swimming before entering the arena</p>
* @param originalCollideAble <p>Whether the player was collide-able before entering the arena</p>
*/
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 @Override
public void setArenaState() { public void setArenaState() {
Player player = getPlayer();
if (player == null) {
return;
}
if (this.makePlayerInvisible) { if (this.makePlayerInvisible) {
this.player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY,
PotionEffect.INFINITE_DURATION, 3)); PotionEffect.INFINITE_DURATION, 3));
} }
} }
@Override @Override
public void restore() { public boolean restore() {
this.player.setFlying(this.originalIsFlying); Player player = getPlayer();
this.player.setGameMode(this.originalGameMode); if (player == null) {
this.player.setAllowFlight(this.originalAllowFlight); return false;
this.player.setInvulnerable(this.originalInvulnerable); }
this.player.setSwimming(this.originalIsSwimming); restore(player);
this.player.setCollidable(this.originalCollideAble); 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) { 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; return this.entryLocation;
} }
/**
* Gets the player this entry state belongs to
*
* @return <p>The player, or null if not currently online</p>
*/
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<String, Object> serialize() {
Map<String, Object> 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;
}
} }

View File

@ -3,6 +3,7 @@ package net.knarcraft.minigames.arena;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID; import java.util.UUID;
@ -83,4 +84,11 @@ public interface Arena {
*/ */
@NotNull Location getSpawnLocation(); @NotNull Location getSpawnLocation();
/**
* Gets this arena's exit location
*
* @return <p>This arena's exit location, or null if no such location is set.</p>
*/
@Nullable Location getExitLocation();
} }

View File

@ -170,7 +170,7 @@ public abstract class ArenaHandler<K extends Arena, S extends ArenaGroup<K, S>>
*/ */
public void removeArena(@NotNull K arena) { public void removeArena(@NotNull K arena) {
UUID arenaId = arena.getArenaId(); UUID arenaId = arena.getArenaId();
this.playerRegistry.removeForArena(arena); this.playerRegistry.removeForArena(arena, false);
this.arenas.remove(arenaId); this.arenas.remove(arenaId);
this.arenaNameLookup.remove(arena.getArenaNameSanitized()); this.arenaNameLookup.remove(arena.getArenaNameSanitized());
this.arenaGroups.remove(arenaId); this.arenaGroups.remove(arenaId);

View File

@ -1,5 +1,10 @@
package net.knarcraft.minigames.arena; 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 * A registry keeping track of all player sessions for some arenas
* *
@ -7,11 +12,44 @@ package net.knarcraft.minigames.arena;
*/ */
public interface ArenaPlayerRegistry<K extends Arena> { public interface ArenaPlayerRegistry<K extends Arena> {
/**
* Gets the current entry state for the given player
*
* @param playerId <p>The id of the player to get an entry state for</p>
* @return <p>The entry state of the player, or null if not found</p>
*/
@Nullable PlayerEntryState getEntryState(@NotNull UUID playerId);
/**
* Registers that the given player has started playing the given dropper arena session
*
* @param playerId <p>The id of the player that started playing</p>
* @param arenaSession <p>The arena session to register</p>
*/
void registerPlayer(@NotNull UUID playerId, @NotNull ArenaSession arenaSession);
/**
* Removes this player from players currently playing
*
* @param playerId <p>The id of the player to remove</p>
* @param restoreState <p>Whether to restore the state of the player as part of the removal</p>
*/
boolean removePlayer(@NotNull UUID playerId, boolean restoreState);
/**
* Gets the player's active dropper arena session
*
* @param playerId <p>The id of the player to get arena for</p>
* @return <p>The player's active arena session, or null if not currently playing</p>
*/
@Nullable ArenaSession getArenaSession(@NotNull UUID playerId);
/** /**
* Removes all active sessions for the given arena * Removes all active sessions for the given arena
* *
* @param arena <p>The arena to remove sessions for</p> * @param arena <p>The arena to remove sessions for</p>
* @param immediately <p>Whether to immediately teleport the player</p>
*/ */
void removeForArena(K arena); void removeForArena(K arena, boolean immediately);
} }

View File

@ -1,6 +1,5 @@
package net.knarcraft.minigames.arena; package net.knarcraft.minigames.arena;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
@ -8,13 +7,6 @@ import org.jetbrains.annotations.NotNull;
*/ */
public interface ArenaSession { public interface ArenaSession {
/**
* Gets the game-mode the player is playing in this session
*
* @return <p>The game-mode for this session</p>
*/
@NotNull ArenaGameMode getGameMode();
/** /**
* Gets the state of the player when they joined the session * Gets the state of the player when they joined the session
* *
@ -46,11 +38,4 @@ public interface ArenaSession {
*/ */
@NotNull Arena getArena(); @NotNull Arena getArena();
/**
* Gets the player playing in this session
*
* @return <p>This session's player</p>
*/
@NotNull Player getPlayer();
} }

View File

@ -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
}

View File

@ -1,11 +1,15 @@
package net.knarcraft.minigames.arena; package net.knarcraft.minigames.arena;
import org.bukkit.Location; 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 * 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 * 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 * Restores the stored state for the stored player
*/ */
void restore(); boolean restore();
/**
* Restores the stored state for the given player
*
* @param player <p>A player object that's refers to the same player as the stored player</p>
*/
void restore(Player player);
/**
* Gets the id of the player this state belongs to
*
* @return <p>The player the state belongs to</p>
*/
UUID getPlayerId();
/** /**
* Gets the location the player entered from * Gets the location the player entered from

View File

@ -150,11 +150,7 @@ public class DropperArena implements Arena {
return this.spawnLocation.clone(); return this.spawnLocation.clone();
} }
/** @Override
* Gets this arena's exit location
*
* @return <p>This arena's exit location, or null if no such location is set.</p>
*/
public @Nullable Location getExitLocation() { public @Nullable Location getExitLocation() {
return this.exitLocation != null ? this.exitLocation.clone() : null; return this.exitLocation != null ? this.exitLocation.clone() : null;
} }

View File

@ -1,5 +1,6 @@
package net.knarcraft.minigames.arena.dropper; package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.arena.EditablePropertyType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -13,45 +14,62 @@ public enum DropperArenaEditableProperty {
/** /**
* The name of the arena * The name of the arena
*/ */
NAME("name", DropperArena::getArenaName), NAME("name", DropperArena::getArenaName, EditablePropertyType.ARENA_NAME),
/** /**
* The arena's spawn location * 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 * 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 * 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 * 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 * 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 @NotNull String argumentString;
private final Function<DropperArena, String> currentValueProvider; private final Function<DropperArena, String> currentValueProvider;
private final EditablePropertyType propertyType;
/** /**
* Instantiates a new arena editable property * Instantiates a new arena editable property
* *
* @param argumentString <p>The argument string used to specify this property</p> * @param argumentString <p>The argument string used to specify this property</p>
*/ */
DropperArenaEditableProperty(@NotNull String argumentString, Function<DropperArena, String> currentValueProvider) { DropperArenaEditableProperty(@NotNull String argumentString, Function<DropperArena, String> currentValueProvider,
EditablePropertyType propertyType) {
this.argumentString = argumentString; this.argumentString = argumentString;
this.currentValueProvider = currentValueProvider; this.currentValueProvider = currentValueProvider;
this.propertyType = propertyType;
}
/**
* Gets the type of property this editable property represents
*
* @return <p>The type of this property</p>
*/
public EditablePropertyType getPropertyType() {
return this.propertyType;
} }
/** /**

View File

@ -2,6 +2,8 @@ package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaHandler; 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 net.knarcraft.minigames.util.DropperArenaStorageHelper;
import java.io.IOException; import java.io.IOException;
@ -22,7 +24,7 @@ public class DropperArenaHandler extends ArenaHandler<DropperArena, DropperArena
* *
* @param playerRegistry <p>The registry keeping track of player sessions</p> * @param playerRegistry <p>The registry keeping track of player sessions</p>
*/ */
public DropperArenaHandler(DropperArenaPlayerRegistry playerRegistry) { public DropperArenaHandler(ArenaPlayerRegistry<DropperArena> playerRegistry) {
super(playerRegistry); super(playerRegistry);
} }
@ -31,8 +33,7 @@ public class DropperArenaHandler extends ArenaHandler<DropperArena, DropperArena
try { try {
DropperArenaStorageHelper.saveDropperArenaGroups(new HashSet<>(this.arenaGroups.values())); DropperArenaStorageHelper.saveDropperArenaGroups(new HashSet<>(this.arenaGroups.values()));
} catch (IOException e) { } catch (IOException e) {
MiniGames.log(Level.SEVERE, "Unable to save current arena groups! " + MiniGames.log(Level.SEVERE, Message.ERROR_CANNOT_SAVE_ARENA_GROUPS.getMessage());
"Data loss can occur!");
MiniGames.log(Level.SEVERE, e.getMessage()); MiniGames.log(Level.SEVERE, e.getMessage());
} }
} }

View File

@ -1,62 +1,15 @@
package net.knarcraft.minigames.arena.dropper; package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.arena.ArenaPlayerRegistry; import net.knarcraft.minigames.arena.AbstractArenaPlayerRegistry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/** /**
* A registry to keep track of which players are playing in which arenas * A registry to keep track of which players are playing in which arenas
*/ */
public class DropperArenaPlayerRegistry implements ArenaPlayerRegistry<DropperArena> { public class DropperArenaPlayerRegistry extends AbstractArenaPlayerRegistry<DropperArena> {
private final Map<UUID, DropperArenaSession> arenaPlayers = new HashMap<>(); @Override
protected String getEntryStateStorageKey() {
/** return "dropper";
* Registers that the given player has started playing the given dropper arena session
*
* @param playerId <p>The id of the player that started playing</p>
* @param arena <p>The arena session to register</p>
*/
public void registerPlayer(@NotNull UUID playerId, @NotNull DropperArenaSession arena) {
this.arenaPlayers.put(playerId, arena);
}
/**
* Removes this player from players currently playing
*
* @param playerId <p>The id of the player to remove</p>
*/
public boolean removePlayer(@NotNull UUID playerId) {
return this.arenaPlayers.remove(playerId) != null;
}
/**
* Gets the player's active dropper arena session
*
* @param playerId <p>The id of the player to get arena for</p>
* @return <p>The player's active arena session, or null if not currently playing</p>
*/
public @Nullable DropperArenaSession getArenaSession(@NotNull UUID playerId) {
return this.arenaPlayers.getOrDefault(playerId, null);
}
/**
* Removes all active sessions for the given arena
*
* @param arena <p>The arena to remove sessions for</p>
*/
public void removeForArena(DropperArena arena) {
for (Map.Entry<UUID, DropperArenaSession> entry : this.arenaPlayers.entrySet()) {
if (entry.getValue().getArena() == arena) {
// Kick the player gracefully
entry.getValue().triggerQuit(false);
this.arenaPlayers.remove(entry.getKey());
}
}
} }
} }

View File

@ -1,13 +1,11 @@
package net.knarcraft.minigames.arena.dropper; package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaRecordsRegistry; import net.knarcraft.minigames.arena.AbstractArenaSession;
import net.knarcraft.minigames.arena.ArenaSession;
import net.knarcraft.minigames.arena.PlayerEntryState; import net.knarcraft.minigames.arena.PlayerEntryState;
import net.knarcraft.minigames.config.DropperConfiguration; 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 net.knarcraft.minigames.util.PlayerTeleporter;
import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; 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 * 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 DropperArena arena;
private final @NotNull Player player; private final @NotNull Player player;
private final @NotNull DropperArenaGameMode gameMode; private final @NotNull DropperArenaGameMode gameMode;
private int deaths;
private final long startTime;
private final PlayerEntryState entryState;
/** /**
* Instantiates a new dropper arena session * Instantiates a new dropper arena session
@ -34,173 +29,19 @@ public class DropperArenaSession implements ArenaSession {
*/ */
public DropperArenaSession(@NotNull DropperArena dropperArena, @NotNull Player player, public DropperArenaSession(@NotNull DropperArena dropperArena, @NotNull Player player,
@NotNull DropperArenaGameMode gameMode) { @NotNull DropperArenaGameMode gameMode) {
super(dropperArena, player, gameMode);
this.arena = dropperArena; this.arena = dropperArena;
this.player = player; this.player = player;
this.gameMode = gameMode; this.gameMode = gameMode;
this.deaths = 0;
this.startTime = System.currentTimeMillis();
DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration(); DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration();
boolean makeInvisible = configuration.makePlayersInvisible(); boolean makeInvisible = configuration.makePlayersInvisible();
boolean disableCollision = configuration.disableHitCollision(); boolean disableCollision = configuration.disableHitCollision();
this.entryState = new DropperPlayerEntryState(player, gameMode, makeInvisible, disableCollision, this.entryState = new DropperPlayerEntryState(player, gameMode, makeInvisible, disableCollision,
dropperArena.getPlayerHorizontalVelocity()); dropperArena.getPlayerHorizontalVelocity());
// Make the player fly to improve mobility in the air
this.entryState.setArenaState(); this.entryState.setArenaState();
} }
/**
* Gets the game-mode the player is playing in this session
*
* @return <p>The game-mode for this session</p>
*/
public @NotNull DropperArenaGameMode getGameMode() {
return this.gameMode;
}
/**
* Gets the state of the player when they joined the session
*
* @return <p>The player's entry state</p>
*/
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 <p>Whether to to the teleportation immediately, not using any timers</p>
*/
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 <p>The result of the record</p>
* @param type <p>The type of record set (time or deaths)</p>
*/
private void announceRecord(@NotNull RecordResult recordResult, @NotNull String type) {
if (recordResult == RecordResult.NONE) {
return;
}
// Gets a string representation of the played game-mode
String gameModeString = switch (this.gameMode) {
case DEFAULT -> "default";
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 <p>Whether to to the teleportation immediately, not using any timers</p>
*/
public void triggerQuit(boolean immediately) {
// Stop this session
stopSession();
// Teleport the player out of the arena
teleportToExit(immediately);
player.sendMessage("You quit the arena!");
}
/**
* Stops this session, and disables flight mode
*/
private void stopSession() {
// Remove this session from game sessions to stop listeners from fiddling more with the player
removeSession();
// Remove flight mode
entryState.restore();
}
/**
* Gets the arena this session is being played in
*
* @return <p>The session's arena</p>
*/
public @NotNull DropperArena getArena() {
return this.arena;
}
/** /**
* Gets the player playing in this session * Gets the player playing in this session
* *
@ -210,4 +51,74 @@ public class DropperArenaSession implements ArenaSession {
return this.player; return this.player;
} }
/**
* Gets the game-mode the player is playing in this session
*
* @return <p>The game-mode for this session</p>
*/
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";
};
}
} }

View File

@ -1,10 +1,15 @@
package net.knarcraft.minigames.arena.dropper; package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.arena.AbstractPlayerEntryState; import net.knarcraft.minigames.arena.AbstractPlayerEntryState;
import net.knarcraft.minigames.container.SerializableUUID;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.UUID;
/** /**
* The state of a player before entering a dropper arena * The state of a player before entering a dropper arena
*/ */
@ -29,29 +34,106 @@ public class DropperPlayerEntryState extends AbstractPlayerEntryState {
this.horizontalVelocity = horizontalVelocity; this.horizontalVelocity = horizontalVelocity;
} }
/**
* Instantiates a new parkour player entry state
*
* @param playerId <p>The id of the player whose state this should keep track of</p>
* @param makePlayerInvisible <p>Whether players should be made invisible while in the arena</p>
* @param entryLocation <p>The location the player entered from</p>
* @param originalIsFlying <p>Whether the player was flying before entering the arena</p>
* @param originalGameMode <p>The game-mode of the player before entering the arena</p>
* @param originalAllowFlight <p>Whether the player was allowed flight before entering the arena</p>
* @param originalInvulnerable <p>Whether the player was invulnerable before entering the arena</p>
* @param originalIsSwimming <p>Whether the player was swimming before entering the arena</p>
* @param originalCollideAble <p>Whether the player was collide-able before entering the arena</p>
*/
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 @Override
public void setArenaState() { public void setArenaState() {
super.setArenaState(); super.setArenaState();
this.player.setAllowFlight(true); Player player = getPlayer();
this.player.setFlying(true); if (player == null) {
this.player.setGameMode(GameMode.ADVENTURE); return;
this.player.setSwimming(false); }
player.setAllowFlight(true);
player.setFlying(true);
player.setGameMode(GameMode.ADVENTURE);
player.setSwimming(false);
if (this.disableHitCollision) { 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 playing on the inverted game-mode, negate the horizontal velocity to swap the controls
if (this.arenaGameMode == DropperArenaGameMode.INVERTED) { if (this.arenaGameMode == DropperArenaGameMode.INVERTED) {
this.player.setFlySpeed(-this.horizontalVelocity); player.setFlySpeed(-this.horizontalVelocity);
} else { } else {
this.player.setFlySpeed(this.horizontalVelocity); player.setFlySpeed(this.horizontalVelocity);
} }
} }
@Override @Override
public void restore() { public boolean restore() {
super.restore(); Player player = getPlayer();
this.player.setFlySpeed(this.originalFlySpeed); 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<String, Object> serialize() {
Map<String, Object> 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 <p>The data to deserialize</p>
*/
@SuppressWarnings("unused")
public static DropperPlayerEntryState deserialize(Map<String, Object> 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);
} }
} }

View File

@ -162,11 +162,7 @@ public class ParkourArena implements Arena {
return this.spawnLocation; return this.spawnLocation;
} }
/** @Override
* Gets this arena's exit location
*
* @return <p>This arena's exit location, or null if no such location is set.</p>
*/
public @Nullable Location getExitLocation() { public @Nullable Location getExitLocation() {
return this.exitLocation; return this.exitLocation;
} }

View File

@ -1,5 +1,6 @@
package net.knarcraft.minigames.arena.parkour; package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.arena.EditablePropertyType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -13,22 +14,25 @@ public enum ParkourArenaEditableProperty {
/** /**
* The name of the arena * The name of the arena
*/ */
NAME("name", ParkourArena::getArenaName), NAME("name", ParkourArena::getArenaName, EditablePropertyType.ARENA_NAME),
/** /**
* The arena's spawn location * 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 * 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 * 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) * The arena's win location (overrides the win block type)
@ -39,35 +43,50 @@ public enum ParkourArenaEditableProperty {
} else { } else {
return "null"; return "null";
} }
}), }, EditablePropertyType.LOCATION),
/** /**
* The arena's check points. Specifically used for adding. * 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. * 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 * 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 @NotNull String argumentString;
private final Function<ParkourArena, String> currentValueProvider; private final Function<ParkourArena, String> currentValueProvider;
private final EditablePropertyType propertyType;
/** /**
* Instantiates a new arena editable property * Instantiates a new arena editable property
* *
* @param argumentString <p>The argument string used to specify this property</p> * @param argumentString <p>The argument string used to specify this property</p>
*/ */
ParkourArenaEditableProperty(@NotNull String argumentString, Function<ParkourArena, String> currentValueProvider) { ParkourArenaEditableProperty(@NotNull String argumentString, Function<ParkourArena, String> currentValueProvider,
EditablePropertyType propertyType) {
this.argumentString = argumentString; this.argumentString = argumentString;
this.currentValueProvider = currentValueProvider; this.currentValueProvider = currentValueProvider;
this.propertyType = propertyType;
}
/**
* Gets the type of property this editable property represents
*
* @return <p>The type of this property</p>
*/
public EditablePropertyType getPropertyType() {
return this.propertyType;
} }
/** /**

View File

@ -2,6 +2,8 @@ package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaHandler; 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 net.knarcraft.minigames.util.ParkourArenaStorageHelper;
import java.io.IOException; import java.io.IOException;
@ -22,7 +24,7 @@ public class ParkourArenaHandler extends ArenaHandler<ParkourArena, ParkourArena
* *
* @param playerRegistry <p>The registry keeping track of player sessions</p> * @param playerRegistry <p>The registry keeping track of player sessions</p>
*/ */
public ParkourArenaHandler(ParkourArenaPlayerRegistry playerRegistry) { public ParkourArenaHandler(ArenaPlayerRegistry<ParkourArena> playerRegistry) {
super(playerRegistry); super(playerRegistry);
} }
@ -31,8 +33,7 @@ public class ParkourArenaHandler extends ArenaHandler<ParkourArena, ParkourArena
try { try {
ParkourArenaStorageHelper.saveParkourArenaGroups(new HashSet<>(this.arenaGroups.values())); ParkourArenaStorageHelper.saveParkourArenaGroups(new HashSet<>(this.arenaGroups.values()));
} catch (IOException e) { } catch (IOException e) {
MiniGames.log(Level.SEVERE, "Unable to save current arena groups! " + MiniGames.log(Level.SEVERE, Message.ERROR_CANNOT_SAVE_ARENA_GROUPS.getMessage());
"Data loss can occur!");
MiniGames.log(Level.SEVERE, e.getMessage()); MiniGames.log(Level.SEVERE, e.getMessage());
} }
} }

View File

@ -1,62 +1,15 @@
package net.knarcraft.minigames.arena.parkour; package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.arena.ArenaPlayerRegistry; import net.knarcraft.minigames.arena.AbstractArenaPlayerRegistry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/** /**
* A registry to keep track of which players are playing in which arenas * A registry to keep track of which players are playing in which arenas
*/ */
public class ParkourArenaPlayerRegistry implements ArenaPlayerRegistry<ParkourArena> { public class ParkourArenaPlayerRegistry extends AbstractArenaPlayerRegistry<ParkourArena> {
private final Map<UUID, ParkourArenaSession> arenaPlayers = new HashMap<>(); @Override
protected String getEntryStateStorageKey() {
/** return "parkour";
* Registers that the given player has started playing the given parkour arena session
*
* @param playerId <p>The id of the player that started playing</p>
* @param arena <p>The arena session to register</p>
*/
public void registerPlayer(@NotNull UUID playerId, @NotNull ParkourArenaSession arena) {
this.arenaPlayers.put(playerId, arena);
}
/**
* Removes this player from players currently playing
*
* @param playerId <p>The id of the player to remove</p>
*/
public boolean removePlayer(@NotNull UUID playerId) {
return this.arenaPlayers.remove(playerId) != null;
}
/**
* Gets the player's active parkour arena session
*
* @param playerId <p>The id of the player to get arena for</p>
* @return <p>The player's active arena session, or null if not currently playing</p>
*/
public @Nullable ParkourArenaSession getArenaSession(@NotNull UUID playerId) {
return this.arenaPlayers.getOrDefault(playerId, null);
}
/**
* Removes all active sessions for the given arena
*
* @param arena <p>The arena to remove sessions for</p>
*/
public void removeForArena(ParkourArena arena) {
for (Map.Entry<UUID, ParkourArenaSession> entry : this.arenaPlayers.entrySet()) {
if (entry.getValue().getArena() == arena) {
// Kick the player gracefully
entry.getValue().triggerQuit(false);
this.arenaPlayers.remove(entry.getKey());
}
}
} }
} }

View File

@ -1,12 +1,10 @@
package net.knarcraft.minigames.arena.parkour; package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaGameMode; import net.knarcraft.minigames.arena.AbstractArenaSession;
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
import net.knarcraft.minigames.arena.ArenaSession;
import net.knarcraft.minigames.arena.PlayerEntryState; import net.knarcraft.minigames.arena.PlayerEntryState;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.config.ParkourConfiguration; import net.knarcraft.minigames.config.ParkourConfiguration;
import net.knarcraft.minigames.property.RecordResult;
import net.knarcraft.minigames.util.PlayerTeleporter; import net.knarcraft.minigames.util.PlayerTeleporter;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; 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 * 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 ParkourArena arena;
private final @NotNull Player player; private final @NotNull Player player;
private final @NotNull ParkourArenaGameMode gameMode; private final @NotNull ParkourArenaGameMode gameMode;
private int deaths;
private final long startTime;
private final PlayerEntryState entryState;
private Location reachedCheckpoint = null; private Location reachedCheckpoint = null;
/** /**
@ -37,33 +32,17 @@ public class ParkourArenaSession implements ArenaSession {
*/ */
public ParkourArenaSession(@NotNull ParkourArena parkourArena, @NotNull Player player, public ParkourArenaSession(@NotNull ParkourArena parkourArena, @NotNull Player player,
@NotNull ParkourArenaGameMode gameMode) { @NotNull ParkourArenaGameMode gameMode) {
super(parkourArena, player, gameMode);
this.arena = parkourArena; this.arena = parkourArena;
this.player = player; this.player = player;
this.gameMode = gameMode; this.gameMode = gameMode;
this.deaths = 0;
this.startTime = System.currentTimeMillis();
ParkourConfiguration configuration = MiniGames.getInstance().getParkourConfiguration(); ParkourConfiguration configuration = MiniGames.getInstance().getParkourConfiguration();
boolean makeInvisible = configuration.makePlayersInvisible(); boolean makeInvisible = configuration.makePlayersInvisible();
this.entryState = new ParkourPlayerEntryState(player, makeInvisible); this.entryState = new ParkourPlayerEntryState(player, makeInvisible);
// Make the player fly to improve mobility in the air
this.entryState.setArenaState(); this.entryState.setArenaState();
} }
@Override
public @NotNull ArenaGameMode getGameMode() {
return this.gameMode;
}
/**
* Gets the state of the player when they joined the session
*
* @return <p>The player's entry state</p>
*/
public @NotNull PlayerEntryState getEntryState() {
return this.entryState;
}
/** /**
* Registers the checkpoint this session's player has reached * Registers the checkpoint this session's player has reached
* *
@ -82,12 +61,15 @@ public class ParkourArenaSession implements ArenaSession {
return this.reachedCheckpoint; return this.reachedCheckpoint;
} }
/** @Override
* Triggers a win for the player playing in this session public @NotNull PlayerEntryState getEntryState() {
*/ return this.entryState;
}
@Override
public void triggerWin() { public void triggerWin() {
// Stop this session // Stop this session
stopSession(); removeSession();
// Check for, and display, records // Check for, and display, records
MiniGames miniGames = MiniGames.getInstance(); MiniGames miniGames = MiniGames.getInstance();
@ -99,78 +81,15 @@ public class ParkourArenaSession implements ArenaSession {
// Mark the arena as cleared // Mark the arena as cleared
if (this.arena.getData().setCompleted(this.gameMode, this.player)) { 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 // Teleport the player out of the arena
teleportToExit(false); teleportToExit(false);
} }
/** @Override
* 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 <p>The result of the record</p>
* @param type <p>The type of record set (time or deaths)</p>
*/
private void announceRecord(@NotNull RecordResult recordResult, @NotNull String type) {
if (recordResult == RecordResult.NONE) {
return;
}
// Gets a string representation of the played game-mode
String gameModeString = switch (this.gameMode) {
case DEFAULT -> "default";
};
String recordString = "You just set a %s on the %s game-mode!";
recordString = switch (recordResult) {
case WORLD_RECORD -> String.format(recordString, "new %s record", gameModeString);
case PERSONAL_BEST -> String.format(recordString, "personal %s record", gameModeString);
default -> throw new IllegalStateException("Unexpected value: " + recordResult);
};
player.sendMessage(String.format(recordString, type));
}
/**
* Triggers a loss for the player playing in this session
*/
public void triggerLoss() { public void triggerLoss() {
this.deaths++; this.deaths++;
//Teleport the player back to the top //Teleport the player back to the top
@ -179,45 +98,27 @@ public class ParkourArenaSession implements ArenaSession {
this.entryState.setArenaState(); this.entryState.setArenaState();
} }
/** @Override
* Triggers a quit for the player playing in this session
*/
public void triggerQuit(boolean immediately) {
// Stop this session
stopSession();
// Teleport the player out of the arena
teleportToExit(immediately);
player.sendMessage("You quit the arena!");
}
/**
* Stops this session, and disables flight mode
*/
private void stopSession() {
// Remove this session from game sessions to stop listeners from fiddling more with the player
removeSession();
// Remove flight mode
entryState.restore();
}
/**
* Gets the arena this session is being played in
*
* @return <p>The session's arena</p>
*/
public @NotNull ParkourArena getArena() { public @NotNull ParkourArena getArena() {
return this.arena; return this.arena;
} }
/** @Override
* Gets the player playing in this session protected void removeSession() {
* // Remove this session for game sessions to stop listeners from fiddling more with the player
* @return <p>This session's player</p> boolean removedSession = MiniGames.getInstance().getParkourArenaPlayerRegistry().removePlayer(
*/ player.getUniqueId(), true);
public @NotNull Player getPlayer() { if (!removedSession) {
return this.player; 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";
};
} }
} }

View File

@ -1,10 +1,15 @@
package net.knarcraft.minigames.arena.parkour; package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.arena.AbstractPlayerEntryState; import net.knarcraft.minigames.arena.AbstractPlayerEntryState;
import net.knarcraft.minigames.container.SerializableUUID;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.UUID;
/** /**
* The state of a player before entering a parkour arena * The state of a player before entering a parkour arena
*/ */
@ -19,14 +24,60 @@ public class ParkourPlayerEntryState extends AbstractPlayerEntryState {
super(player, makePlayerInvisible); super(player, makePlayerInvisible);
} }
/**
* Instantiates a new parkour player entry state
*
* @param playerId <p>The id of the player whose state this should keep track of</p>
* @param makePlayerInvisible <p>Whether players should be made invisible while in the arena</p>
* @param entryLocation <p>The location the player entered from</p>
* @param originalIsFlying <p>Whether the player was flying before entering the arena</p>
* @param originalGameMode <p>The game-mode of the player before entering the arena</p>
* @param originalAllowFlight <p>Whether the player was allowed flight before entering the arena</p>
* @param originalInvulnerable <p>Whether the player was invulnerable before entering the arena</p>
* @param originalIsSwimming <p>Whether the player was swimming before entering the arena</p>
* @param originalCollideAble <p>Whether the player was collide-able before entering the arena</p>
*/
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 @Override
public void setArenaState() { public void setArenaState() {
super.setArenaState(); super.setArenaState();
this.player.setAllowFlight(false); Player player = getPlayer();
this.player.setFlying(false); if (player == null) {
this.player.setGameMode(GameMode.ADVENTURE); return;
this.player.setSwimming(false); }
this.player.setCollidable(false); player.setAllowFlight(false);
player.setFlying(false);
player.setGameMode(GameMode.ADVENTURE);
player.setSwimming(false);
player.setCollidable(false);
}
/**
* Deserializes a ParkourPlayerEntryState from the given data
*
* @return <p>The data to deserialize</p>
*/
@SuppressWarnings("unused")
public static ParkourPlayerEntryState deserialize(Map<String, Object> 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);
} }
} }

View File

@ -11,6 +11,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
/** /**
* An abstract class for an arena joining tab-completer * An abstract class for an arena joining tab-completer
*/ */
@ -34,13 +36,13 @@ public abstract class JoinArenaTabCompleter implements TabCompleter {
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String label, @NotNull String[] arguments) { @NotNull String label, @NotNull String[] arguments) {
if (arguments.length == 1) { if (arguments.length == 1) {
return arenaNameSupplier.get(); return filterMatchingContains(arenaNameSupplier.get(), arguments[0]);
} else if (arguments.length == 2) { } else if (arguments.length == 2) {
List<String> gameModes = new ArrayList<>(); List<String> gameModes = new ArrayList<>();
for (ArenaGameMode gameMode : gameMode.getValues()) { for (ArenaGameMode gameMode : gameMode.getValues()) {
gameModes.add(gameMode.name().toLowerCase()); gameModes.add(gameMode.name().toLowerCase());
} }
return gameModes; return filterMatchingContains(gameModes, arguments[1]);
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -2,6 +2,7 @@ package net.knarcraft.minigames.command;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaSession; import net.knarcraft.minigames.arena.ArenaSession;
import net.knarcraft.minigames.config.Message;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@ -21,13 +22,13 @@ public class LeaveArenaCommand implements TabExecutor {
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] strings) { @NotNull String[] strings) {
if (!(commandSender instanceof Player player)) { if (!(commandSender instanceof Player player)) {
commandSender.sendMessage("This command must be used by a player"); commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
return false; return false;
} }
ArenaSession existingSession = MiniGames.getInstance().getSession(player.getUniqueId()); ArenaSession existingSession = MiniGames.getInstance().getSession(player.getUniqueId());
if (existingSession == null) { if (existingSession == null) {
commandSender.sendMessage("You are not in a mini-games arena!"); commandSender.sendMessage(Message.ERROR_NOT_IN_ARENA.getMessage());
return false; return false;
} }

View File

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

View File

@ -3,6 +3,7 @@ package net.knarcraft.minigames.command.dropper;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArena;
import net.knarcraft.minigames.arena.dropper.DropperArenaHandler; import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.util.StringSanitizer; import net.knarcraft.minigames.util.StringSanitizer;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; 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, public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) { @NotNull String[] arguments) {
if (!(commandSender instanceof Player player)) { if (!(commandSender instanceof Player player)) {
commandSender.sendMessage("This command must be used by a player"); commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
return false; return false;
} }
@ -40,13 +41,13 @@ public class CreateDropperArenaCommand implements CommandExecutor {
DropperArena existingArena = arenaHandler.getArena(arenaName); DropperArena existingArena = arenaHandler.getArena(arenaName);
if (existingArena != null) { if (existingArena != null) {
commandSender.sendMessage("There already exists a dropper arena with that name!"); commandSender.sendMessage(Message.ERROR_ARENA_NAME_COLLISION.getMessage());
return false; return false;
} }
DropperArena arena = new DropperArena(arenaName, player.getLocation(), arenaHandler); DropperArena arena = new DropperArena(arenaName, player.getLocation(), arenaHandler);
arenaHandler.addArena(arena); arenaHandler.addArena(arena);
commandSender.sendMessage("The arena was successfully created!"); commandSender.sendMessage(Message.SUCCESS_ARENA_CREATED.getMessage());
return true; return true;
} }

View File

@ -4,6 +4,7 @@ import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArena;
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup; import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
import net.knarcraft.minigames.arena.dropper.DropperArenaHandler; import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
import net.knarcraft.minigames.config.Message;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@ -14,6 +15,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
/** /**
* The command for listing groups and the stages within * The command for listing groups and the stages within
*/ */
@ -58,12 +61,12 @@ public class DropperGroupListCommand implements TabExecutor {
@NotNull String groupName) { @NotNull String groupName) {
DropperArenaGroup arenaGroup = arenaHandler.getGroup(groupName); DropperArenaGroup arenaGroup = arenaHandler.getGroup(groupName);
if (arenaGroup == null) { if (arenaGroup == null) {
sender.sendMessage("Unable to find the specified group!"); sender.sendMessage(Message.ERROR_GROUP_NOT_FOUND.getMessage());
return false; return false;
} }
// Send a list of all stages (arenas in the group) // 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; int counter = 1;
for (UUID arenaId : arenaGroup.getArenas()) { for (UUID arenaId : arenaGroup.getArenas()) {
DropperArena arena = arenaHandler.getArena(arenaId); DropperArena arena = arenaHandler.getArena(arenaId);
@ -84,7 +87,7 @@ public class DropperGroupListCommand implements TabExecutor {
for (DropperArenaGroup group : MiniGames.getInstance().getDropperArenaHandler().getAllGroups()) { for (DropperArenaGroup group : MiniGames.getInstance().getDropperArenaHandler().getAllGroups()) {
groupNames.add(group.getGroupName()); groupNames.add(group.getGroupName());
} }
return groupNames; return filterMatchingContains(groupNames, arguments[0]);
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -4,6 +4,7 @@ import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArena;
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup; import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
import net.knarcraft.minigames.arena.dropper.DropperArenaHandler; import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.util.StringSanitizer; import net.knarcraft.minigames.util.StringSanitizer;
import net.knarcraft.minigames.util.TabCompleteHelper; import net.knarcraft.minigames.util.TabCompleteHelper;
import org.bukkit.command.Command; import org.bukkit.command.Command;
@ -15,6 +16,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
/** /**
* The command for setting the group of an arena * The command for setting the group of an arena
*/ */
@ -31,7 +34,7 @@ public class DropperGroupSetCommand implements TabExecutor {
DropperArena specifiedArena = arenaHandler.getArena(arguments[0]); DropperArena specifiedArena = arenaHandler.getArena(arguments[0]);
if (specifiedArena == null) { if (specifiedArena == null) {
commandSender.sendMessage("Unable to find the specified dropper arena."); commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
return false; return false;
} }
@ -53,7 +56,7 @@ public class DropperGroupSetCommand implements TabExecutor {
arenaHandler.setGroup(specifiedArena.getArenaId(), arenaGroup); arenaHandler.setGroup(specifiedArena.getArenaId(), arenaGroup);
commandSender.sendMessage("The arena's group has been updated"); commandSender.sendMessage(Message.SUCCESS_ARENA_GROUP_UPDATED.getMessage());
return true; return true;
} }
@ -62,7 +65,7 @@ public class DropperGroupSetCommand implements TabExecutor {
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) { @NotNull String[] arguments) {
if (arguments.length == 1) { if (arguments.length == 1) {
return TabCompleteHelper.getDropperArenas(); return filterMatchingContains(TabCompleteHelper.getDropperArenas(), arguments[0]);
} else if (arguments.length == 2) { } else if (arguments.length == 2) {
List<String> possibleValues = new ArrayList<>(); List<String> possibleValues = new ArrayList<>();
possibleValues.add("none"); possibleValues.add("none");
@ -70,7 +73,7 @@ public class DropperGroupSetCommand implements TabExecutor {
for (DropperArenaGroup group : MiniGames.getInstance().getDropperArenaHandler().getAllGroups()) { for (DropperArenaGroup group : MiniGames.getInstance().getDropperArenaHandler().getAllGroups()) {
possibleValues.add(group.getGroupName()); possibleValues.add(group.getGroupName());
} }
return possibleValues; return filterMatchingContains(possibleValues, arguments[1]);
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -4,6 +4,7 @@ import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArena;
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup; import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
import net.knarcraft.minigames.arena.dropper.DropperArenaHandler; import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
import net.knarcraft.minigames.config.Message;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@ -14,6 +15,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
/** /**
* The command for swapping the order of two arenas in a group * 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]); DropperArena arena1 = arenaHandler.getArena(arguments[0]);
if (arena1 == null) { if (arena1 == null) {
commandSender.sendMessage("Unable to find the first specified dropper arena."); commandSender.sendMessage(Message.ERROR_ARENA_1_NOT_FOUND.getMessage());
return false; return false;
} }
DropperArena arena2 = arenaHandler.getArena(arguments[1]); DropperArena arena2 = arenaHandler.getArena(arguments[1]);
if (arena2 == null) { if (arena2 == null) {
commandSender.sendMessage("Unable to find the second specified dropper arena."); commandSender.sendMessage(Message.ERROR_ARENA_2_NOT_FOUND.getMessage());
return false; return false;
} }
DropperArenaGroup arena1Group = arenaHandler.getGroup(arena1.getArenaId()); DropperArenaGroup arena1Group = arenaHandler.getGroup(arena1.getArenaId());
DropperArenaGroup arena2Group = arenaHandler.getGroup(arena2.getArenaId()); DropperArenaGroup arena2Group = arenaHandler.getGroup(arena2.getArenaId());
if (arena1Group == null || !arena1Group.equals(arena2Group)) { 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; return false;
} }
arena1Group.swapArenas(arena1Group.getArenas().indexOf(arena1.getArenaId()), arena1Group.swapArenas(arena1Group.getArenas().indexOf(arena1.getArenaId()),
arena1Group.getArenas().indexOf(arena2.getArenaId())); arena1Group.getArenas().indexOf(arena2.getArenaId()));
commandSender.sendMessage("The arenas have been swapped!"); commandSender.sendMessage(Message.SUCCESS_ARENAS_SWAPPED.getMessage());
return true; return true;
} }
@ -63,9 +66,9 @@ public class DropperGroupSwapCommand implements TabExecutor {
for (DropperArena dropperArena : arenaHandler.getArenasInAGroup()) { for (DropperArena dropperArena : arenaHandler.getArenasInAGroup()) {
arenaNames.add(dropperArena.getArenaName()); arenaNames.add(dropperArena.getArenaName());
} }
return arenaNames; return filterMatchingContains(arenaNames, arguments[0]);
} else if (arguments.length == 2) { } else if (arguments.length == 2) {
return getArenaNamesInSameGroup(arguments[0]); return filterMatchingContains(getArenaNamesInSameGroup(arguments[0]), arguments[1]);
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -4,6 +4,8 @@ import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArena;
import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty; import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty;
import net.knarcraft.minigames.config.DropperConfiguration; 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.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.command.Command; 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, public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) { @NotNull String[] arguments) {
if (!(commandSender instanceof Player player)) { if (!(commandSender instanceof Player player)) {
commandSender.sendMessage("This command must be used by a player"); commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
return false; return false;
} }
@ -42,29 +44,29 @@ public class EditDropperArenaCommand implements CommandExecutor {
DropperArena specifiedArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]); DropperArena specifiedArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]);
if (specifiedArena == null) { if (specifiedArena == null) {
commandSender.sendMessage("Unable to find the specified dropper arena."); commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
return false; return false;
} }
DropperArenaEditableProperty editableProperty = DropperArenaEditableProperty.getFromArgumentString(arguments[1]); DropperArenaEditableProperty editableProperty = DropperArenaEditableProperty.getFromArgumentString(arguments[1]);
if (editableProperty == null) { if (editableProperty == null) {
commandSender.sendMessage("Unknown property specified."); commandSender.sendMessage(Message.ERROR_UNKNOWN_PROPERTY.getMessage());
return false; return false;
} }
String currentValueFormat = "Current value of %s is: %s";
if (arguments.length < 3) { if (arguments.length < 3) {
// Print the current value of the property // Print the current value of the property
String value = editableProperty.getCurrentValueAsString(specifiedArena); 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; return true;
} else { } else {
boolean successful = changeValue(specifiedArena, editableProperty, arguments[2], player); boolean successful = changeValue(specifiedArena, editableProperty, arguments[2], player);
if (successful) { 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 { } else {
player.sendMessage("Unable to change the property. Make sure your input is valid!"); player.sendMessage(Message.ERROR_PROPERTY_INPUT_INVALID.getMessage());
} }
return successful; return successful;
} }

View File

@ -1,5 +1,6 @@
package net.knarcraft.minigames.command.dropper; package net.knarcraft.minigames.command.dropper;
import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty;
import net.knarcraft.minigames.util.TabCompleteHelper; import net.knarcraft.minigames.util.TabCompleteHelper;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -10,6 +11,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
/** /**
* The tab-completer for the edit arena command * The tab-completer for the edit arena command
*/ */
@ -17,14 +20,18 @@ public class EditDropperArenaTabCompleter implements TabCompleter {
@Override @Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String label, @NotNull String[] args) { @NotNull String label, @NotNull String[] arguments) {
if (args.length == 1) { if (arguments.length == 1) {
return TabCompleteHelper.getDropperArenas(); return filterMatchingContains(TabCompleteHelper.getDropperArenas(), arguments[0]);
} else if (args.length == 2) { } else if (arguments.length == 2) {
return TabCompleteHelper.getDropperArenaProperties(); return filterMatchingContains(TabCompleteHelper.getDropperArenaProperties(), arguments[1]);
} else if (args.length == 3) { } else if (arguments.length == 3) {
//TODO: Tab-complete possible values for the given property DropperArenaEditableProperty property = DropperArenaEditableProperty.getFromArgumentString(arguments[1]);
return null; if (property == null) {
return new ArrayList<>();
}
return filterMatchingContains(TabCompleteHelper.getTabCompleteSuggestions(property.getPropertyType()),
arguments[2]);
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -1,12 +1,13 @@
package net.knarcraft.minigames.command.dropper; package net.knarcraft.minigames.command.dropper;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArena;
import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode; import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup; 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.arena.dropper.DropperArenaSession;
import net.knarcraft.minigames.config.DropperConfiguration; import net.knarcraft.minigames.config.DropperConfiguration;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.util.PlayerTeleporter; import net.knarcraft.minigames.util.PlayerTeleporter;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; 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, public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) { @NotNull String[] arguments) {
if (!(commandSender instanceof Player player)) { if (!(commandSender instanceof Player player)) {
commandSender.sendMessage("This command must be used by a player"); commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
return false; return false;
} }
@ -33,20 +34,20 @@ public class JoinDropperArenaCommand implements CommandExecutor {
// Disallow joining if the player is already in a mini-game arena // Disallow joining if the player is already in a mini-game arena
if (MiniGames.getInstance().getSession(player.getUniqueId()) != null) { 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; return false;
} }
// Make sure the arena exists // Make sure the arena exists
DropperArena specifiedArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]); DropperArena specifiedArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]);
if (specifiedArena == null) { if (specifiedArena == null) {
commandSender.sendMessage("Unable to find the specified dropper arena."); commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
return false; return false;
} }
// Deny vehicles as allowing this is tricky, and will cause problems in some cases // Deny vehicles as allowing this is tricky, and will cause problems in some cases
if (player.isInsideVehicle() || !player.getPassengers().isEmpty()) { 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; return false;
} }
@ -80,25 +81,25 @@ public class JoinDropperArenaCommand implements CommandExecutor {
if (MiniGames.getInstance().getDropperConfiguration().mustDoNormalModeFirst() && if (MiniGames.getInstance().getDropperConfiguration().mustDoNormalModeFirst() &&
gameMode != DropperArenaGameMode.DEFAULT && gameMode != DropperArenaGameMode.DEFAULT &&
specifiedArena.getData().hasNotCompleted(DropperArenaGameMode.DEFAULT, player)) { 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; return false;
} }
// Register the player's session // Register the player's session
DropperArenaSession newSession = new DropperArenaSession(specifiedArena, player, gameMode); DropperArenaSession newSession = new DropperArenaSession(specifiedArena, player, gameMode);
DropperArenaPlayerRegistry playerRegistry = MiniGames.getInstance().getDropperArenaPlayerRegistry(); ArenaPlayerRegistry<DropperArena> playerRegistry = MiniGames.getInstance().getDropperArenaPlayerRegistry();
playerRegistry.registerPlayer(player.getUniqueId(), newSession); playerRegistry.registerPlayer(player.getUniqueId(), newSession);
// Try to teleport the player to the arena // Try to teleport the player to the arena
boolean teleported = PlayerTeleporter.teleportPlayer(player, specifiedArena.getSpawnLocation(), false, false); boolean teleported = PlayerTeleporter.teleportPlayer(player, specifiedArena.getSpawnLocation(), false, false);
if (!teleported) { if (!teleported) {
player.sendMessage("Unable to teleport you to the dropper arena. Make sure you're not in a vehicle," + player.sendMessage(Message.ERROR_ARENA_TELEPORT_FAILED.getMessage());
"and not carrying a passenger!");
newSession.triggerQuit(false); newSession.triggerQuit(false);
return false; return false;
} else { } else {
// Make sure to update the state again in the air to remove a potential swimming state // Make sure to update the state again in the air to remove a potential swimming state
newSession.getEntryState().setArenaState(); newSession.getEntryState().setArenaState();
player.sendMessage(Message.SUCCESS_ARENA_JOINED.getMessage());
return true; 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 // Require that players beat all arenas in the group in the normal game-mode before trying challenge modes
if (configuration.mustDoNormalModeFirst() && arenaGameMode != DropperArenaGameMode.DEFAULT && if (configuration.mustDoNormalModeFirst() && arenaGameMode != DropperArenaGameMode.DEFAULT &&
!arenaGroup.hasBeatenAll(DropperArenaGameMode.DEFAULT, player)) { !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; return false;
} }
// Require that the player has beaten the previous arena on the same game-mode before trying this one // Require that the player has beaten the previous arena on the same game-mode before trying this one
if (configuration.mustDoGroupedInSequence() && if (configuration.mustDoGroupedInSequence() &&
arenaGroup.cannotPlay(arenaGameMode, player, dropperArena.getArenaId())) { 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; return false;
} }

View File

@ -1,5 +1,6 @@
package net.knarcraft.minigames.command.dropper; package net.knarcraft.minigames.command.dropper;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.util.TabCompleteHelper; import net.knarcraft.minigames.util.TabCompleteHelper;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -18,10 +19,11 @@ public class ListDropperArenaCommand implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) { @NotNull String[] arguments) {
sender.sendMessage("Dropper arenas:"); StringBuilder builder = new StringBuilder(Message.SUCCESS_DROPPER_ARENAS_LIST.getMessage());
for (String arenaName : TabCompleteHelper.getDropperArenas()) { for (String arenaName : TabCompleteHelper.getDropperArenas()) {
sender.sendMessage(arenaName); builder.append("\n").append(arenaName);
} }
sender.sendMessage(builder.toString());
return true; return true;
} }

View File

@ -2,6 +2,7 @@ package net.knarcraft.minigames.command.dropper;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.dropper.DropperArena; import net.knarcraft.minigames.arena.dropper.DropperArena;
import net.knarcraft.minigames.config.Message;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -23,13 +24,13 @@ public class RemoveDropperArenaCommand implements CommandExecutor {
// Get the specified arena // Get the specified arena
DropperArena targetArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]); DropperArena targetArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]);
if (targetArena == null) { if (targetArena == null) {
commandSender.sendMessage("Unable to find the specified arena"); commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
return false; return false;
} }
// Remove the arena // Remove the arena
MiniGames.getInstance().getDropperArenaHandler().removeArena(targetArena); MiniGames.getInstance().getDropperArenaHandler().removeArena(targetArena);
commandSender.sendMessage("The specified arena has been successfully removed"); commandSender.sendMessage(Message.SUCCESS_ARENA_REMOVED.getMessage());
return true; return true;
} }

View File

@ -10,6 +10,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
/** /**
* The tab-completer for the remove arena command * The tab-completer for the remove arena command
*/ */
@ -20,7 +22,7 @@ public class RemoveDropperArenaTabCompleter implements TabCompleter {
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) { @NotNull String[] arguments) {
if (arguments.length == 1) { if (arguments.length == 1) {
return TabCompleteHelper.getDropperArenas(); return filterMatchingContains(TabCompleteHelper.getDropperArenas(), arguments[0]);
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -3,6 +3,7 @@ package net.knarcraft.minigames.command.parkour;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArena;
import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler; import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.util.StringSanitizer; import net.knarcraft.minigames.util.StringSanitizer;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; 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, public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) { @NotNull String[] arguments) {
if (!(commandSender instanceof Player player)) { if (!(commandSender instanceof Player player)) {
commandSender.sendMessage("This command must be used by a player"); commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
return false; return false;
} }
@ -40,13 +41,13 @@ public class CreateParkourArenaCommand implements CommandExecutor {
ParkourArena existingArena = arenaHandler.getArena(arenaName); ParkourArena existingArena = arenaHandler.getArena(arenaName);
if (existingArena != null) { if (existingArena != null) {
commandSender.sendMessage("There already exists a parkour arena with that name!"); commandSender.sendMessage(Message.ERROR_ARENA_NAME_COLLISION.getMessage());
return false; return false;
} }
ParkourArena arena = new ParkourArena(arenaName, player.getLocation(), arenaHandler); ParkourArena arena = new ParkourArena(arenaName, player.getLocation(), arenaHandler);
arenaHandler.addArena(arena); arenaHandler.addArena(arena);
commandSender.sendMessage("The arena was successfully created!"); commandSender.sendMessage(Message.SUCCESS_ARENA_CREATED.getMessage());
return true; return true;
} }

View File

@ -3,6 +3,8 @@ package net.knarcraft.minigames.command.parkour;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArena;
import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty; 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.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.command.Command; 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, public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) { @NotNull String[] arguments) {
if (!(commandSender instanceof Player player)) { if (!(commandSender instanceof Player player)) {
commandSender.sendMessage("This command must be used by a player"); commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
return false; return false;
} }
@ -39,29 +41,29 @@ public class EditParkourArenaCommand implements CommandExecutor {
ParkourArena specifiedArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]); ParkourArena specifiedArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]);
if (specifiedArena == null) { if (specifiedArena == null) {
commandSender.sendMessage("Unable to find the specified dropper arena."); commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
return false; return false;
} }
ParkourArenaEditableProperty editableProperty = ParkourArenaEditableProperty.getFromArgumentString(arguments[1]); ParkourArenaEditableProperty editableProperty = ParkourArenaEditableProperty.getFromArgumentString(arguments[1]);
if (editableProperty == null) { if (editableProperty == null) {
commandSender.sendMessage("Unknown property specified."); commandSender.sendMessage(Message.ERROR_UNKNOWN_PROPERTY.getMessage());
return false; return false;
} }
String currentValueFormat = "Current value of %s is: %s";
if (arguments.length < 3) { if (arguments.length < 3) {
// Print the current value of the property // Print the current value of the property
String value = editableProperty.getCurrentValueAsString(specifiedArena); 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; return true;
} else { } else {
boolean successful = changeValue(specifiedArena, editableProperty, arguments[2], player); boolean successful = changeValue(specifiedArena, editableProperty, arguments[2], player);
if (successful) { 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 { } else {
player.sendMessage("Unable to change the property. Make sure your input is valid!"); player.sendMessage(Message.ERROR_PROPERTY_INPUT_INVALID.getMessage());
} }
return successful; return successful;
} }

View File

@ -1,5 +1,6 @@
package net.knarcraft.minigames.command.parkour; package net.knarcraft.minigames.command.parkour;
import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty;
import net.knarcraft.minigames.util.TabCompleteHelper; import net.knarcraft.minigames.util.TabCompleteHelper;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -10,6 +11,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
/** /**
* The tab-completer for the edit arena command * The tab-completer for the edit arena command
*/ */
@ -17,14 +20,18 @@ public class EditParkourArenaTabCompleter implements TabCompleter {
@Override @Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String label, @NotNull String[] args) { @NotNull String label, @NotNull String[] arguments) {
if (args.length == 1) { if (arguments.length == 1) {
return TabCompleteHelper.getParkourArenas(); return filterMatchingContains(TabCompleteHelper.getParkourArenas(), arguments[0]);
} else if (args.length == 2) { } else if (arguments.length == 2) {
return TabCompleteHelper.getParkourArenaProperties(); return filterMatchingContains(TabCompleteHelper.getParkourArenaProperties(), arguments[1]);
} else if (args.length == 3) { } else if (arguments.length == 3) {
//TODO: Tab-complete possible values for the given property ParkourArenaEditableProperty property = ParkourArenaEditableProperty.getFromArgumentString(arguments[1]);
return null; if (property == null) {
return new ArrayList<>();
}
return filterMatchingContains(TabCompleteHelper.getTabCompleteSuggestions(property.getPropertyType()),
arguments[2]);
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -1,11 +1,12 @@
package net.knarcraft.minigames.command.parkour; package net.knarcraft.minigames.command.parkour;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArena;
import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode; import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode;
import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup; 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.arena.parkour.ParkourArenaSession;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.config.ParkourConfiguration; import net.knarcraft.minigames.config.ParkourConfiguration;
import net.knarcraft.minigames.util.PlayerTeleporter; import net.knarcraft.minigames.util.PlayerTeleporter;
import org.bukkit.command.Command; 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, public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) { @NotNull String[] arguments) {
if (!(commandSender instanceof Player player)) { if (!(commandSender instanceof Player player)) {
commandSender.sendMessage("This command must be used by a player"); commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
return false; return false;
} }
@ -33,20 +34,20 @@ public class JoinParkourArenaCommand implements CommandExecutor {
// Disallow joining if the player is already in a mini-game arena // Disallow joining if the player is already in a mini-game arena
if (MiniGames.getInstance().getSession(player.getUniqueId()) != null) { 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; return false;
} }
// Make sure the arena exists // Make sure the arena exists
ParkourArena specifiedArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]); ParkourArena specifiedArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]);
if (specifiedArena == null) { if (specifiedArena == null) {
commandSender.sendMessage("Unable to find the specified parkour arena."); commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
return false; return false;
} }
// Deny vehicles as allowing this is tricky, and will cause problems in some cases // Deny vehicles as allowing this is tricky, and will cause problems in some cases
if (player.isInsideVehicle() || !player.getPassengers().isEmpty()) { 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; return false;
} }
@ -78,19 +79,19 @@ public class JoinParkourArenaCommand implements CommandExecutor {
// Register the player's session // Register the player's session
ParkourArenaSession newSession = new ParkourArenaSession(specifiedArena, player, gameMode); ParkourArenaSession newSession = new ParkourArenaSession(specifiedArena, player, gameMode);
ParkourArenaPlayerRegistry playerRegistry = MiniGames.getInstance().getParkourArenaPlayerRegistry(); ArenaPlayerRegistry<ParkourArena> playerRegistry = MiniGames.getInstance().getParkourArenaPlayerRegistry();
playerRegistry.registerPlayer(player.getUniqueId(), newSession); playerRegistry.registerPlayer(player.getUniqueId(), newSession);
// Try to teleport the player to the arena // Try to teleport the player to the arena
boolean teleported = PlayerTeleporter.teleportPlayer(player, specifiedArena.getSpawnLocation(), false, false); boolean teleported = PlayerTeleporter.teleportPlayer(player, specifiedArena.getSpawnLocation(), false, false);
if (!teleported) { if (!teleported) {
player.sendMessage("Unable to teleport you to the parkour arena. Make sure you're not in a vehicle," + player.sendMessage(Message.ERROR_ARENA_TELEPORT_FAILED.getMessage());
"and not carrying a passenger!");
newSession.triggerQuit(false); newSession.triggerQuit(false);
return false; return false;
} else { } else {
// Make sure to update the state again in the air to remove a potential swimming state // Make sure to update the state again in the air to remove a potential swimming state
newSession.getEntryState().setArenaState(); newSession.getEntryState().setArenaState();
player.sendMessage(Message.SUCCESS_ARENA_JOINED.getMessage());
return true; 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 // Require that the player has beaten the previous arena on the same game-mode before trying this one
if (configuration.mustDoGroupedInSequence() && if (configuration.mustDoGroupedInSequence() &&
arenaGroup.cannotPlay(arenaGameMode, player, parkourArena.getArenaId())) { 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; return false;
} }

View File

@ -1,5 +1,6 @@
package net.knarcraft.minigames.command.parkour; package net.knarcraft.minigames.command.parkour;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.util.TabCompleteHelper; import net.knarcraft.minigames.util.TabCompleteHelper;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -18,10 +19,11 @@ public class ListParkourArenaCommand implements TabExecutor {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] arguments) { @NotNull String[] arguments) {
sender.sendMessage("Parkour arenas:"); StringBuilder builder = new StringBuilder(Message.SUCCESS_PARKOUR_ARENAS_LIST.getMessage());
for (String arenaName : TabCompleteHelper.getParkourArenas()) { for (String arenaName : TabCompleteHelper.getParkourArenas()) {
sender.sendMessage(arenaName); builder.append("\n").append(arenaName);
} }
sender.sendMessage(builder.toString());
return true; return true;
} }

View File

@ -4,6 +4,7 @@ import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArena;
import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup; import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler; import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
import net.knarcraft.minigames.config.Message;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@ -15,6 +16,8 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
/** /**
* The command for listing groups and the stages within * The command for listing groups and the stages within
*/ */
@ -59,17 +62,17 @@ public class ParkourGroupListCommand implements TabExecutor {
@NotNull String groupName) { @NotNull String groupName) {
ParkourArenaGroup arenaGroup = arenaHandler.getGroup(groupName); ParkourArenaGroup arenaGroup = arenaHandler.getGroup(groupName);
if (arenaGroup == null) { if (arenaGroup == null) {
sender.sendMessage("Unable to find the specified group!"); sender.sendMessage(Message.ERROR_GROUP_NOT_FOUND.getMessage());
return false; return false;
} }
// Send a list of all stages (arenas in the group) // 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; int counter = 1;
for (UUID arenaId : arenaGroup.getArenas()) { for (UUID arenaId : arenaGroup.getArenas()) {
ParkourArena arena = arenaHandler.getArena(arenaId); ParkourArena arena = arenaHandler.getArena(arenaId);
if (arena != null) { 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()); sender.sendMessage(builder.toString());
@ -86,7 +89,7 @@ public class ParkourGroupListCommand implements TabExecutor {
for (ParkourArenaGroup group : arenaGroups) { for (ParkourArenaGroup group : arenaGroups) {
groupNames.add(group.getGroupName()); groupNames.add(group.getGroupName());
} }
return groupNames; return filterMatchingContains(groupNames, arguments[0]);
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -4,6 +4,7 @@ import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArena;
import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup; import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler; import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.util.StringSanitizer; import net.knarcraft.minigames.util.StringSanitizer;
import net.knarcraft.minigames.util.TabCompleteHelper; import net.knarcraft.minigames.util.TabCompleteHelper;
import org.bukkit.command.Command; import org.bukkit.command.Command;
@ -15,6 +16,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
/** /**
* The command for setting the group of an arena * The command for setting the group of an arena
*/ */
@ -31,7 +34,7 @@ public class ParkourGroupSetCommand implements TabExecutor {
ParkourArena specifiedArena = arenaHandler.getArena(arguments[0]); ParkourArena specifiedArena = arenaHandler.getArena(arguments[0]);
if (specifiedArena == null) { if (specifiedArena == null) {
commandSender.sendMessage("Unable to find the specified parkour arena."); commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
return false; return false;
} }
@ -53,7 +56,7 @@ public class ParkourGroupSetCommand implements TabExecutor {
arenaHandler.setGroup(specifiedArena.getArenaId(), arenaGroup); arenaHandler.setGroup(specifiedArena.getArenaId(), arenaGroup);
commandSender.sendMessage("The arena's group has been updated"); commandSender.sendMessage(Message.SUCCESS_ARENA_GROUP_UPDATED.getMessage());
return true; return true;
} }
@ -62,7 +65,7 @@ public class ParkourGroupSetCommand implements TabExecutor {
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) { @NotNull String[] arguments) {
if (arguments.length == 1) { if (arguments.length == 1) {
return TabCompleteHelper.getParkourArenas(); return filterMatchingContains(TabCompleteHelper.getParkourArenas(), arguments[0]);
} else if (arguments.length == 2) { } else if (arguments.length == 2) {
List<String> possibleValues = new ArrayList<>(); List<String> possibleValues = new ArrayList<>();
possibleValues.add("none"); possibleValues.add("none");
@ -70,7 +73,7 @@ public class ParkourGroupSetCommand implements TabExecutor {
for (ParkourArenaGroup group : MiniGames.getInstance().getParkourArenaHandler().getAllGroups()) { for (ParkourArenaGroup group : MiniGames.getInstance().getParkourArenaHandler().getAllGroups()) {
possibleValues.add(group.getGroupName()); possibleValues.add(group.getGroupName());
} }
return possibleValues; return filterMatchingContains(possibleValues, arguments[1]);
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -4,6 +4,7 @@ import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArena;
import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup; import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler; import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
import net.knarcraft.minigames.config.Message;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
@ -14,6 +15,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
/** /**
* The command for swapping the order of two arenas in a group * 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]); ParkourArena arena1 = arenaHandler.getArena(arguments[0]);
if (arena1 == null) { if (arena1 == null) {
commandSender.sendMessage("Unable to find the first specified parkour arena."); commandSender.sendMessage(Message.ERROR_ARENA_1_NOT_FOUND.getMessage());
return false; return false;
} }
ParkourArena arena2 = arenaHandler.getArena(arguments[1]); ParkourArena arena2 = arenaHandler.getArena(arguments[1]);
if (arena2 == null) { if (arena2 == null) {
commandSender.sendMessage("Unable to find the second specified parkour arena."); commandSender.sendMessage(Message.ERROR_ARENA_2_NOT_FOUND.getMessage());
return false; return false;
} }
ParkourArenaGroup arena1Group = arenaHandler.getGroup(arena1.getArenaId()); ParkourArenaGroup arena1Group = arenaHandler.getGroup(arena1.getArenaId());
ParkourArenaGroup arena2Group = arenaHandler.getGroup(arena2.getArenaId()); ParkourArenaGroup arena2Group = arenaHandler.getGroup(arena2.getArenaId());
if (arena1Group == null || !arena1Group.equals(arena2Group)) { 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; return false;
} }
arena1Group.swapArenas(arena1Group.getArenas().indexOf(arena1.getArenaId()), arena1Group.swapArenas(arena1Group.getArenas().indexOf(arena1.getArenaId()),
arena1Group.getArenas().indexOf(arena2.getArenaId())); arena1Group.getArenas().indexOf(arena2.getArenaId()));
commandSender.sendMessage("The arenas have been swapped!"); commandSender.sendMessage(Message.SUCCESS_ARENAS_SWAPPED.getMessage());
return true; return true;
} }
@ -63,9 +66,9 @@ public class ParkourGroupSwapCommand implements TabExecutor {
for (ParkourArena parkourArena : arenaHandler.getArenasInAGroup()) { for (ParkourArena parkourArena : arenaHandler.getArenasInAGroup()) {
arenaNames.add(parkourArena.getArenaName()); arenaNames.add(parkourArena.getArenaName());
} }
return arenaNames; return filterMatchingContains(arenaNames, arguments[0]);
} else if (arguments.length == 2) { } else if (arguments.length == 2) {
return getArenaNamesInSameGroup(arguments[0]); return filterMatchingContains(getArenaNamesInSameGroup(arguments[0]), arguments[1]);
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -2,6 +2,7 @@ package net.knarcraft.minigames.command.parkour;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArena;
import net.knarcraft.minigames.config.Message;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -23,13 +24,13 @@ public class RemoveParkourArenaCommand implements CommandExecutor {
// Get the specified arena // Get the specified arena
ParkourArena targetArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]); ParkourArena targetArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]);
if (targetArena == null) { if (targetArena == null) {
commandSender.sendMessage("Unable to find the specified arena"); commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
return false; return false;
} }
// Remove the arena // Remove the arena
MiniGames.getInstance().getParkourArenaHandler().removeArena(targetArena); MiniGames.getInstance().getParkourArenaHandler().removeArena(targetArena);
commandSender.sendMessage("The specified arena has been successfully removed"); commandSender.sendMessage(Message.SUCCESS_ARENA_REMOVED.getMessage());
return true; return true;
} }

View File

@ -10,6 +10,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
/** /**
* The tab-completer for the remove arena command * The tab-completer for the remove arena command
*/ */
@ -20,7 +22,7 @@ public class RemoveParkourArenaTabCompleter implements TabCompleter {
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] arguments) { @NotNull String[] arguments) {
if (arguments.length == 1) { if (arguments.length == 1) {
return TabCompleteHelper.getParkourArenas(); return filterMatchingContains(TabCompleteHelper.getParkourArenas(), arguments[0]);
} else { } else {
return new ArrayList<>(); return new ArrayList<>();
} }

View File

@ -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
*
* <p>This should in theory be impossible, as players cannot use any commands except /miniGamesLeave while playing
* in an arena.</p>
*/
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 <p>The default value of the message</p>
*/
Message(@NotNull String defaultMessage) {
this.defaultMessage = defaultMessage;
}
/**
* Gets the message this enum represents
*
* @return <p>The formatted message</p>
*/
public @NotNull String getMessage() {
return formatMessage(this.defaultMessage);
}
/**
* Gets the message this enum represents
*
* @param placeholder <p>The placeholder to replace</p>
* @param replacement <p>The replacement to use</p>
* @return <p>The formatted message</p>
*/
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 <p>The placeholder to replace</p>
* @param replacement <p>The replacement to use</p>
* @return <p>The formatted message</p>
*/
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 <p>The placeholder -> replacement map specifying necessary replacements</p>
* @return <p>The formatted message</p>
*/
public @NotNull String getMessage(@NotNull PlaceholderContainer placeholders) {
String replaced = this.defaultMessage;
for (Map.Entry<String, String> entry : placeholders.getPlaceholders().entrySet()) {
replaced = replaced.replace(entry.getKey(), entry.getValue());
}
return formatMessage(replaced);
}
/**
* Gets the formatted version of the given message
*
* @param message <p>The message to format</p>
* @return <p>The formatted message</p>
*/
private @NotNull String formatMessage(@NotNull String message) {
String prefix = MiniGames.getInstance().getDescription().getPrefix();
return ColorHelper.translateAllColorCodes("#546EED[&r&l" + prefix + "#546EED]&r " + message);
}
}

View File

@ -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<String, String> placeholders;
/**
* Instantiates a new placeholder container
*/
public PlaceholderContainer() {
this.placeholders = new HashMap<>();
}
/**
* Gets all placeholders
*
* @return <p>All placeholders</p>
*/
public Map<String, String> getPlaceholders() {
return new HashMap<>(this.placeholders);
}
/**
* Adds a new placeholder
*
* @param placeholder <p>The placeholder to register</p>
* @param value <p>The value of the placeholder</p>
* @return <p>This object</p>
*/
public PlaceholderContainer add(String placeholder, String value) {
this.placeholders.put(placeholder, value);
return this;
}
}

View File

@ -2,6 +2,7 @@ package net.knarcraft.minigames.listener;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaSession; import net.knarcraft.minigames.arena.ArenaSession;
import net.knarcraft.minigames.config.Message;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@ -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); event.setCancelled(true);
} }

View File

@ -8,6 +8,7 @@ import net.knarcraft.minigames.arena.dropper.DropperArenaSession;
import net.knarcraft.minigames.arena.parkour.ParkourArena; import net.knarcraft.minigames.arena.parkour.ParkourArena;
import net.knarcraft.minigames.arena.parkour.ParkourArenaSession; import net.knarcraft.minigames.arena.parkour.ParkourArenaSession;
import net.knarcraft.minigames.config.DropperConfiguration; import net.knarcraft.minigames.config.DropperConfiguration;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.config.ParkourConfiguration; import net.knarcraft.minigames.config.ParkourConfiguration;
import net.knarcraft.minigames.config.SharedConfiguration; import net.knarcraft.minigames.config.SharedConfiguration;
import org.bukkit.Location; import org.bukkit.Location;
@ -80,19 +81,30 @@ public class MoveListener implements Listener {
List<Location> checkpoints = arena.getCheckpoints(); List<Location> checkpoints = arena.getCheckpoints();
for (Location checkpoint : checkpoints) { for (Location checkpoint : checkpoints) {
Location previousCheckpoint = arenaSession.getRegisteredCheckpoint(); 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()); // Skip if checkpoint has not been reached
event.getPlayer().sendMessage("Checkpoint 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; 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;
} }
} }

View File

@ -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<UUID, ArenaSession> 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);
}
}

View File

@ -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 <p>The triggered teleport event</p>
*/
@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 <p>The player whose state should be checked</p>
* @param playerRegistry <p>The registry to check for a lingering state</p>
* @return <p>The location the player should spawn in, or null if not restored</p>
*/
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;
}
}
}

View File

@ -1,18 +1,68 @@
package net.knarcraft.minigames.util; package net.knarcraft.minigames.util;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.PlayerEntryState;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; 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.UUID;
import java.util.logging.Level; import java.util.logging.Level;
/**
* A helper class for dealing with arena storage
*/
public final class ArenaStorageHelper { public final class ArenaStorageHelper {
private ArenaStorageHelper() { private ArenaStorageHelper() {
} }
/**
* Stores the given entry states to disk
*
* @param key <p>The key specifying the correct entry state file</p>
* @param entryStates <p>The entry states to save</p>
*/
public static void storeArenaPlayerEntryStates(String key, Set<PlayerEntryState> 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 <p>The key specifying the correct entry state file</p>
* @return <p>The previously saved entry states</p>
*/
public static Set<PlayerEntryState> 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<PlayerEntryState> 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 * Gets the file used to store the given arena id's data
* *
@ -20,7 +70,7 @@ public final class ArenaStorageHelper {
* @param arenaId <p>The id of the arena to get a data file for</p> * @param arenaId <p>The id of the arena to get a data file for</p>
* @return <p>The file the arena's data is/should be stored in</p> * @return <p>The file the arena's data is/should be stored in</p>
*/ */
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"); File arenaDataFile = new File(root, arenaId + ".yml");
if (!root.exists() && !root.mkdirs()) { if (!root.exists() && !root.mkdirs()) {
MiniGames.log(Level.SEVERE, "Unable to create the arena data directories"); MiniGames.log(Level.SEVERE, "Unable to create the arena data directories");

View File

@ -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 <p>The string to search for color codes</p>
* @return <p>The message with color codes translated</p>
*/
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;
}
}

View File

@ -9,6 +9,8 @@ import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup; import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
import net.knarcraft.minigames.arena.dropper.DropperArenaRecordsRegistry; import net.knarcraft.minigames.arena.dropper.DropperArenaRecordsRegistry;
import net.knarcraft.minigames.arena.dropper.DropperArenaStorageKey; 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.SerializableMaterial;
import net.knarcraft.minigames.container.SerializableUUID; import net.knarcraft.minigames.container.SerializableUUID;
import org.bukkit.Location; import org.bukkit.Location;
@ -159,8 +161,8 @@ public final class DropperArenaStorageHelper {
DropperArenaStorageKey.WIN_BLOCK_TYPE.getKey()); DropperArenaStorageKey.WIN_BLOCK_TYPE.getKey());
if (arenaName == null || spawnLocation == null) { if (arenaName == null || spawnLocation == null) {
MiniGames.log(Level.SEVERE, "Could not load the arena at configuration " + MiniGames.log(Level.SEVERE, Message.ERROR_ARENA_NOT_LOADED.getMessage(new PlaceholderContainer().add(
"section " + configurationSection.getName() + ". Please check the dropper_arenas storage file for issues."); "{section}", configurationSection.getName()).add("{file}", "dropper_arenas")));
return null; return null;
} }
if (winBlockType == null) { if (winBlockType == null) {
@ -170,7 +172,8 @@ public final class DropperArenaStorageHelper {
// Generate new, empty arena data if not available // Generate new, empty arena data if not available
DropperArenaData arenaData = loadDropperArenaData(arenaId); DropperArenaData arenaData = loadDropperArenaData(arenaId);
if (arenaData == null) { 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); arenaData = getEmptyDropperData(arenaId);
} }

View File

@ -1,6 +1,7 @@
package net.knarcraft.minigames.util; package net.knarcraft.minigames.util;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.config.Message;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
@ -43,7 +44,7 @@ public final class MaterialHelper {
if (matched != null) { if (matched != null) {
parsedMaterials.add(matched); parsedMaterials.add(matched);
} else { } else {
MiniGames.log(Level.WARNING, "Unable to parse: " + string); MiniGames.log(Level.WARNING, Message.ERROR_MATERIAL_NOT_PARSE_ABLE.getMessage("{material}", string));
} }
} }
return parsedMaterials; return parsedMaterials;

View File

@ -9,6 +9,8 @@ import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode;
import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup; import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
import net.knarcraft.minigames.arena.parkour.ParkourArenaRecordsRegistry; import net.knarcraft.minigames.arena.parkour.ParkourArenaRecordsRegistry;
import net.knarcraft.minigames.arena.parkour.ParkourArenaStorageKey; 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.SerializableMaterial;
import net.knarcraft.minigames.container.SerializableUUID; import net.knarcraft.minigames.container.SerializableUUID;
import org.bukkit.Location; import org.bukkit.Location;
@ -164,8 +166,8 @@ public final class ParkourArenaStorageHelper {
// The arena name and spawn location must be present // The arena name and spawn location must be present
if (arenaName == null || spawnLocation == null) { if (arenaName == null || spawnLocation == null) {
MiniGames.log(Level.SEVERE, "Could not load the arena at configuration " + MiniGames.log(Level.SEVERE, Message.ERROR_ARENA_NOT_LOADED.getMessage(new PlaceholderContainer().add(
"section " + configurationSection.getName() + ". Please check the parkour_arenas storage file for issues."); "{section}", configurationSection.getName()).add("{file}", "parkour_arena")));
return null; return null;
} }
@ -177,7 +179,8 @@ public final class ParkourArenaStorageHelper {
// Generate new, empty arena data if not available // Generate new, empty arena data if not available
ParkourArenaData arenaData = loadParkourArenaData(arenaId); ParkourArenaData arenaData = loadParkourArenaData(arenaId);
if (arenaData == null) { 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); arenaData = getEmptyParkourData(arenaId);
} }

View File

@ -1,6 +1,7 @@
package net.knarcraft.minigames.util; package net.knarcraft.minigames.util;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.config.Message;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
@ -35,7 +36,7 @@ public final class PlayerTeleporter {
passenger.teleport(location); passenger.teleport(location);
} }
} else { } else {
player.sendMessage("You cannot be teleported with a passenger!"); player.sendMessage(Message.ERROR_TELEPORT_WITH_PASSENGER.getMessage());
return false; return false;
} }
} }
@ -45,16 +46,16 @@ public final class PlayerTeleporter {
player.eject(); player.eject();
vehicle.teleport(location); vehicle.teleport(location);
} else { } else {
player.sendMessage("You cannot be teleported while in a vehicle"); player.sendMessage(Message.ERROR_TELEPORT_IN_VEHICLE.getMessage());
return false; 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.setVelocity(new Vector(0, 0, 0));
player.setInvulnerable(true); player.setInvulnerable(true);
player.teleport(location); player.teleport(location);
player.setVelocity(new Vector(0, 0, 0)); 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 // lethal velocity, and causing damage. That's why the player is given 5 ticks of invulnerability
if (!immediately) { if (!immediately) {
Bukkit.getScheduler().runTaskLater(MiniGames.getInstance(), () -> player.setInvulnerable(false), 5); Bukkit.getScheduler().runTaskLater(MiniGames.getInstance(), () -> player.setInvulnerable(false), 5);

View File

@ -3,18 +3,24 @@ package net.knarcraft.minigames.util;
import net.knarcraft.minigames.MiniGames; import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.Arena; import net.knarcraft.minigames.arena.Arena;
import net.knarcraft.minigames.arena.ArenaHandler; import net.knarcraft.minigames.arena.ArenaHandler;
import net.knarcraft.minigames.arena.EditablePropertyType;
import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty; import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty;
import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty; import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty;
import org.bukkit.Material;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* A helper-class for common tab-completions * A helper-class for common tab-completions
*/ */
public final class TabCompleteHelper { public final class TabCompleteHelper {
private static Map<EditablePropertyType, List<String>> tabCompleteSuggestions;
private TabCompleteHelper() { private TabCompleteHelper() {
} }
@ -76,4 +82,137 @@ public final class TabCompleteHelper {
return arenaProperties; return arenaProperties;
} }
/**
* Finds tab complete values that contain the typed text
*
* @param values <p>The values to filter</p>
* @param typedText <p>The text the player has started typing</p>
* @return <p>The given string values that contain the player's typed text</p>
*/
public static List<String> filterMatchingContains(@NotNull List<String> values, @NotNull String typedText) {
List<String> 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 <p>The property type to get suggestions for</p>
* @return <p>The suggestions produced</p>
*/
public static List<String> 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 <p>A list of suggestions</p>
*/
private static List<String> getMaterialListSuggestions() {
List<String> suggestions = new ArrayList<>();
suggestions.add("LAVA,MAGMA_BLOCK");
suggestions.add("WATER,MAGMA_BLOCK,LAVA,+BUTTONS,+CORALS");
return suggestions;
}
/**
* Gets suggestions for checkpointClear
*
* @return <p>A list of suggestions</p>
*/
private static List<String> getCheckpointClearSuggestions() {
List<String> suggestions = new ArrayList<>();
suggestions.add("true");
return suggestions;
}
/**
* Gets suggestions for a block material
*
* @return <p>A list of suggestions</p>
*/
private static List<String> getBlockTypeSuggestions() {
List<String> materials = new ArrayList<>();
for (Material material : Material.values()) {
if (material.isBlock()) {
materials.add(material.name());
}
}
return materials;
}
/**
* Gets suggestions for a vertical velocity
*
* @return <p>A list of suggestions</p>
*/
private static List<String> getVerticalVelocitySuggestions() {
List<String> 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 <p>A list of suggestions</p>
*/
private static List<String> getHorizontalVelocitySuggestions() {
List<String> 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 <p>A list of suggestions</p>
*/
private static List<String> getNameSuggestions() {
List<String> locations = new ArrayList<>();
locations.add("DropperArena1");
locations.add("DropperArena2");
locations.add("ParkourArena1");
locations.add("ParkourArena2");
return locations;
}
/**
* Gets suggestions for a location
*
* @return <p>A list of suggestions</p>
*/
private static List<String> getLocationSuggestions() {
List<String> locations = new ArrayList<>();
locations.add("here");
locations.add("x,y,z");
return locations;
}
} }

View File

@ -1,4 +1,5 @@
name: MiniGames name: MiniGames
prefix: MiniGames
version: '${project.version}' version: '${project.version}'
main: net.knarcraft.minigames.MiniGames main: net.knarcraft.minigames.MiniGames
api-version: 1.19 api-version: 1.19