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

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;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.container.SerializableUUID;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@ -7,12 +10,17 @@ import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
/**
* An abstract representation of a player's entry state
*/
public abstract class AbstractPlayerEntryState implements PlayerEntryState {
protected final Player player;
protected final UUID playerId;
private final boolean makePlayerInvisible;
private final Location entryLocation;
private final boolean originalIsFlying;
@ -29,7 +37,7 @@ public abstract class AbstractPlayerEntryState implements PlayerEntryState {
* @param makePlayerInvisible <p>Whether players should be made invisible while in the arena</p>
*/
public AbstractPlayerEntryState(@NotNull Player player, boolean makePlayerInvisible) {
this.player = player;
this.playerId = player.getUniqueId();
this.makePlayerInvisible = makePlayerInvisible;
this.entryLocation = player.getLocation().clone();
this.originalIsFlying = player.isFlying();
@ -40,24 +48,71 @@ public abstract class AbstractPlayerEntryState implements PlayerEntryState {
this.originalCollideAble = player.isCollidable();
}
/**
* Instantiates a new abstract player entry state
*
* @param playerId <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
public void setArenaState() {
Player player = getPlayer();
if (player == null) {
return;
}
if (this.makePlayerInvisible) {
this.player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY,
player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY,
PotionEffect.INFINITE_DURATION, 3));
}
}
@Override
public void restore() {
this.player.setFlying(this.originalIsFlying);
this.player.setGameMode(this.originalGameMode);
this.player.setAllowFlight(this.originalAllowFlight);
this.player.setInvulnerable(this.originalInvulnerable);
this.player.setSwimming(this.originalIsSwimming);
this.player.setCollidable(this.originalCollideAble);
public boolean restore() {
Player player = getPlayer();
if (player == null) {
return false;
}
restore(player);
return true;
}
@Override
public void restore(@NotNull Player player) {
player.setFlying(this.originalIsFlying);
player.setGameMode(this.originalGameMode);
player.setAllowFlight(this.originalAllowFlight);
player.setInvulnerable(this.originalInvulnerable);
player.setSwimming(this.originalIsSwimming);
player.setCollidable(this.originalCollideAble);
if (this.makePlayerInvisible) {
this.player.removePotionEffect(PotionEffectType.INVISIBILITY);
player.removePotionEffect(PotionEffectType.INVISIBILITY);
}
}
@ -66,4 +121,34 @@ public abstract class AbstractPlayerEntryState implements PlayerEntryState {
return this.entryLocation;
}
/**
* Gets the player this entry state belongs to
*
* @return <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.block.Block;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
@ -83,4 +84,11 @@ public interface Arena {
*/
@NotNull Location getSpawnLocation();
/**
* Gets this arena's exit location
*
* @return <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) {
UUID arenaId = arena.getArenaId();
this.playerRegistry.removeForArena(arena);
this.playerRegistry.removeForArena(arena, false);
this.arenas.remove(arenaId);
this.arenaNameLookup.remove(arena.getArenaNameSanitized());
this.arenaGroups.remove(arenaId);

View File

@ -1,5 +1,10 @@
package net.knarcraft.minigames.arena;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
/**
* A registry keeping track of all player sessions for some arenas
*
@ -7,11 +12,44 @@ package net.knarcraft.minigames.arena;
*/
public interface ArenaPlayerRegistry<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
*
* @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;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
@ -8,13 +7,6 @@ import org.jetbrains.annotations.NotNull;
*/
public interface ArenaSession {
/**
* Gets the game-mode the player is playing in this session
*
* @return <p>The game-mode for this session</p>
*/
@NotNull ArenaGameMode getGameMode();
/**
* Gets the state of the player when they joined the session
*
@ -46,11 +38,4 @@ public interface ArenaSession {
*/
@NotNull Arena getArena();
/**
* Gets the player playing in this session
*
* @return <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;
import org.bukkit.Location;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.entity.Player;
import java.util.UUID;
/**
* The stored state of a player
*/
public interface PlayerEntryState {
public interface PlayerEntryState extends ConfigurationSerializable {
/**
* Sets the state of the stored player to the state used by the arena
@ -15,7 +19,21 @@ public interface PlayerEntryState {
/**
* Restores the stored state for the stored player
*/
void restore();
boolean restore();
/**
* Restores the stored state for the given player
*
* @param player <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

View File

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

View File

@ -1,5 +1,6 @@
package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.arena.EditablePropertyType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -13,45 +14,62 @@ public enum DropperArenaEditableProperty {
/**
* The name of the arena
*/
NAME("name", DropperArena::getArenaName),
NAME("name", DropperArena::getArenaName, EditablePropertyType.ARENA_NAME),
/**
* The arena's spawn location
*/
SPAWN_LOCATION("spawnLocation", (arena) -> String.valueOf(arena.getSpawnLocation())),
SPAWN_LOCATION("spawnLocation", (arena) -> String.valueOf(arena.getSpawnLocation()),
EditablePropertyType.LOCATION),
/**
* The arena's exit location
*/
EXIT_LOCATION("exitLocation", (arena) -> String.valueOf(arena.getExitLocation())),
EXIT_LOCATION("exitLocation", (arena) -> String.valueOf(arena.getExitLocation()),
EditablePropertyType.LOCATION),
/**
* The arena's vertical velocity
*/
VERTICAL_VELOCITY("verticalVelocity", (arena) -> String.valueOf(arena.getPlayerVerticalVelocity())),
VERTICAL_VELOCITY("verticalVelocity", (arena) -> String.valueOf(arena.getPlayerVerticalVelocity()),
EditablePropertyType.VERTICAL_VELOCITY),
/**
* The arena's horizontal velocity
*/
HORIZONTAL_VELOCITY("horizontalVelocity", (arena) -> String.valueOf(arena.getPlayerHorizontalVelocity())),
HORIZONTAL_VELOCITY("horizontalVelocity", (arena) -> String.valueOf(arena.getPlayerHorizontalVelocity()),
EditablePropertyType.HORIZONTAL_VELOCITY),
/**
* The arena's win block type
*/
WIN_BLOCK_TYPE("winBlockType", (arena) -> arena.getWinBlockType().toString()),
WIN_BLOCK_TYPE("winBlockType", (arena) -> arena.getWinBlockType().toString(),
EditablePropertyType.BLOCK_TYPE),
;
private final @NotNull String argumentString;
private final Function<DropperArena, String> currentValueProvider;
private final EditablePropertyType propertyType;
/**
* Instantiates a new arena editable property
*
* @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.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.arena.ArenaHandler;
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.util.DropperArenaStorageHelper;
import java.io.IOException;
@ -22,7 +24,7 @@ public class DropperArenaHandler extends ArenaHandler<DropperArena, DropperArena
*
* @param playerRegistry <p>The registry keeping track of player sessions</p>
*/
public DropperArenaHandler(DropperArenaPlayerRegistry playerRegistry) {
public DropperArenaHandler(ArenaPlayerRegistry<DropperArena> playerRegistry) {
super(playerRegistry);
}
@ -31,8 +33,7 @@ public class DropperArenaHandler extends ArenaHandler<DropperArena, DropperArena
try {
DropperArenaStorageHelper.saveDropperArenaGroups(new HashSet<>(this.arenaGroups.values()));
} catch (IOException e) {
MiniGames.log(Level.SEVERE, "Unable to save current arena groups! " +
"Data loss can occur!");
MiniGames.log(Level.SEVERE, Message.ERROR_CANNOT_SAVE_ARENA_GROUPS.getMessage());
MiniGames.log(Level.SEVERE, e.getMessage());
}
}

View File

@ -1,62 +1,15 @@
package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.knarcraft.minigames.arena.AbstractArenaPlayerRegistry;
/**
* A registry to keep track of which players are playing in which arenas
*/
public class DropperArenaPlayerRegistry implements ArenaPlayerRegistry<DropperArena> {
public class DropperArenaPlayerRegistry extends AbstractArenaPlayerRegistry<DropperArena> {
private final Map<UUID, DropperArenaSession> arenaPlayers = new HashMap<>();
/**
* 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());
}
}
@Override
protected String getEntryStateStorageKey() {
return "dropper";
}
}

View File

@ -1,13 +1,11 @@
package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
import net.knarcraft.minigames.arena.ArenaSession;
import net.knarcraft.minigames.arena.AbstractArenaSession;
import net.knarcraft.minigames.arena.PlayerEntryState;
import net.knarcraft.minigames.config.DropperConfiguration;
import net.knarcraft.minigames.property.RecordResult;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.util.PlayerTeleporter;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@ -16,14 +14,11 @@ import java.util.logging.Level;
/**
* A representation of a player's current session in a dropper arena
*/
public class DropperArenaSession implements ArenaSession {
public class DropperArenaSession extends AbstractArenaSession {
private final @NotNull DropperArena arena;
private final @NotNull Player player;
private final @NotNull DropperArenaGameMode gameMode;
private int deaths;
private final long startTime;
private final PlayerEntryState entryState;
/**
* Instantiates a new dropper arena session
@ -34,173 +29,19 @@ public class DropperArenaSession implements ArenaSession {
*/
public DropperArenaSession(@NotNull DropperArena dropperArena, @NotNull Player player,
@NotNull DropperArenaGameMode gameMode) {
super(dropperArena, player, gameMode);
this.arena = dropperArena;
this.player = player;
this.gameMode = gameMode;
this.deaths = 0;
this.startTime = System.currentTimeMillis();
DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration();
boolean makeInvisible = configuration.makePlayersInvisible();
boolean disableCollision = configuration.disableHitCollision();
this.entryState = new DropperPlayerEntryState(player, gameMode, makeInvisible, disableCollision,
dropperArena.getPlayerHorizontalVelocity());
// Make the player fly to improve mobility in the air
this.entryState.setArenaState();
}
/**
* Gets the game-mode the player is playing in this session
*
* @return <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
*
@ -210,4 +51,74 @@ public class DropperArenaSession implements ArenaSession {
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;
import net.knarcraft.minigames.arena.AbstractPlayerEntryState;
import net.knarcraft.minigames.container.SerializableUUID;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.UUID;
/**
* The state of a player before entering a dropper arena
*/
@ -29,29 +34,106 @@ public class DropperPlayerEntryState extends AbstractPlayerEntryState {
this.horizontalVelocity = horizontalVelocity;
}
/**
* Instantiates a new parkour player entry state
*
* @param playerId <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
public void setArenaState() {
super.setArenaState();
this.player.setAllowFlight(true);
this.player.setFlying(true);
this.player.setGameMode(GameMode.ADVENTURE);
this.player.setSwimming(false);
Player player = getPlayer();
if (player == null) {
return;
}
player.setAllowFlight(true);
player.setFlying(true);
player.setGameMode(GameMode.ADVENTURE);
player.setSwimming(false);
if (this.disableHitCollision) {
this.player.setCollidable(false);
player.setCollidable(false);
}
// If playing on the inverted game-mode, negate the horizontal velocity to swap the controls
if (this.arenaGameMode == DropperArenaGameMode.INVERTED) {
this.player.setFlySpeed(-this.horizontalVelocity);
player.setFlySpeed(-this.horizontalVelocity);
} else {
this.player.setFlySpeed(this.horizontalVelocity);
player.setFlySpeed(this.horizontalVelocity);
}
}
@Override
public void restore() {
super.restore();
this.player.setFlySpeed(this.originalFlySpeed);
public boolean restore() {
Player player = getPlayer();
if (player == null) {
return false;
}
this.restore(player);
return true;
}
@Override
public void restore(@NotNull Player player) {
super.restore(player);
player.setFlySpeed(this.originalFlySpeed);
}
@NotNull
@Override
public Map<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;
}
/**
* Gets this arena's exit location
*
* @return <p>This arena's exit location, or null if no such location is set.</p>
*/
@Override
public @Nullable Location getExitLocation() {
return this.exitLocation;
}

View File

@ -1,5 +1,6 @@
package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.arena.EditablePropertyType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -13,22 +14,25 @@ public enum ParkourArenaEditableProperty {
/**
* The name of the arena
*/
NAME("name", ParkourArena::getArenaName),
NAME("name", ParkourArena::getArenaName, EditablePropertyType.ARENA_NAME),
/**
* The arena's spawn location
*/
SPAWN_LOCATION("spawnLocation", (arena) -> String.valueOf(arena.getSpawnLocation())),
SPAWN_LOCATION("spawnLocation", (arena) -> String.valueOf(arena.getSpawnLocation()),
EditablePropertyType.LOCATION),
/**
* The arena's exit location
*/
EXIT_LOCATION("exitLocation", (arena) -> String.valueOf(arena.getExitLocation())),
EXIT_LOCATION("exitLocation", (arena) -> String.valueOf(arena.getExitLocation()),
EditablePropertyType.LOCATION),
/**
* The arena's win block type
*/
WIN_BLOCK_TYPE("winBlockType", (arena) -> arena.getWinBlockType().toString()),
WIN_BLOCK_TYPE("winBlockType", (arena) -> arena.getWinBlockType().toString(),
EditablePropertyType.BLOCK_TYPE),
/**
* The arena's win location (overrides the win block type)
@ -39,35 +43,50 @@ public enum ParkourArenaEditableProperty {
} else {
return "null";
}
}),
}, EditablePropertyType.LOCATION),
/**
* The arena's check points. Specifically used for adding.
*/
CHECKPOINT_ADD("checkpointAdd", (arena) -> String.valueOf(arena.getCheckpoints())),
CHECKPOINT_ADD("checkpointAdd", (arena) -> String.valueOf(arena.getCheckpoints()),
EditablePropertyType.LOCATION),
/**
* The arena's check points. Specifically used for clearing.
*/
CHECKPOINT_CLEAR("checkpointClear", (arena) -> String.valueOf(arena.getCheckpoints())),
CHECKPOINT_CLEAR("checkpointClear", (arena) -> String.valueOf(arena.getCheckpoints()),
EditablePropertyType.CHECKPOINT_CLEAR),
/**
* The blocks constituting the arena's lethal blocks
*/
KILL_PLANE_BLOCKS("killPlaneBlocks", (arena) -> String.valueOf(arena.getKillPlaneBlockNames())),
KILL_PLANE_BLOCKS("killPlaneBlocks", (arena) -> String.valueOf(arena.getKillPlaneBlockNames()),
EditablePropertyType.MATERIAL_LIST),
;
private final @NotNull String argumentString;
private final Function<ParkourArena, String> currentValueProvider;
private final EditablePropertyType propertyType;
/**
* Instantiates a new arena editable property
*
* @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.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.arena.ArenaHandler;
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.util.ParkourArenaStorageHelper;
import java.io.IOException;
@ -22,7 +24,7 @@ public class ParkourArenaHandler extends ArenaHandler<ParkourArena, ParkourArena
*
* @param playerRegistry <p>The registry keeping track of player sessions</p>
*/
public ParkourArenaHandler(ParkourArenaPlayerRegistry playerRegistry) {
public ParkourArenaHandler(ArenaPlayerRegistry<ParkourArena> playerRegistry) {
super(playerRegistry);
}
@ -31,8 +33,7 @@ public class ParkourArenaHandler extends ArenaHandler<ParkourArena, ParkourArena
try {
ParkourArenaStorageHelper.saveParkourArenaGroups(new HashSet<>(this.arenaGroups.values()));
} catch (IOException e) {
MiniGames.log(Level.SEVERE, "Unable to save current arena groups! " +
"Data loss can occur!");
MiniGames.log(Level.SEVERE, Message.ERROR_CANNOT_SAVE_ARENA_GROUPS.getMessage());
MiniGames.log(Level.SEVERE, e.getMessage());
}
}

View File

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

View File

@ -1,12 +1,10 @@
package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaGameMode;
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
import net.knarcraft.minigames.arena.ArenaSession;
import net.knarcraft.minigames.arena.AbstractArenaSession;
import net.knarcraft.minigames.arena.PlayerEntryState;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.config.ParkourConfiguration;
import net.knarcraft.minigames.property.RecordResult;
import net.knarcraft.minigames.util.PlayerTeleporter;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@ -18,14 +16,11 @@ import java.util.logging.Level;
/**
* A representation of a player's current session in a parkour arena
*/
public class ParkourArenaSession implements ArenaSession {
public class ParkourArenaSession extends AbstractArenaSession {
private final @NotNull ParkourArena arena;
private final @NotNull Player player;
private final @NotNull ParkourArenaGameMode gameMode;
private int deaths;
private final long startTime;
private final PlayerEntryState entryState;
private Location reachedCheckpoint = null;
/**
@ -37,33 +32,17 @@ public class ParkourArenaSession implements ArenaSession {
*/
public ParkourArenaSession(@NotNull ParkourArena parkourArena, @NotNull Player player,
@NotNull ParkourArenaGameMode gameMode) {
super(parkourArena, player, gameMode);
this.arena = parkourArena;
this.player = player;
this.gameMode = gameMode;
this.deaths = 0;
this.startTime = System.currentTimeMillis();
ParkourConfiguration configuration = MiniGames.getInstance().getParkourConfiguration();
boolean makeInvisible = configuration.makePlayersInvisible();
this.entryState = new ParkourPlayerEntryState(player, makeInvisible);
// Make the player fly to improve mobility in the air
this.entryState.setArenaState();
}
@Override
public @NotNull ArenaGameMode getGameMode() {
return this.gameMode;
}
/**
* Gets the state of the player when they joined the session
*
* @return <p>The player's entry state</p>
*/
public @NotNull PlayerEntryState getEntryState() {
return this.entryState;
}
/**
* Registers the checkpoint this session's player has reached
*
@ -82,12 +61,15 @@ public class ParkourArenaSession implements ArenaSession {
return this.reachedCheckpoint;
}
/**
* Triggers a win for the player playing in this session
*/
@Override
public @NotNull PlayerEntryState getEntryState() {
return this.entryState;
}
@Override
public void triggerWin() {
// Stop this session
stopSession();
removeSession();
// Check for, and display, records
MiniGames miniGames = MiniGames.getInstance();
@ -99,78 +81,15 @@ public class ParkourArenaSession implements ArenaSession {
// Mark the arena as cleared
if (this.arena.getData().setCompleted(this.gameMode, this.player)) {
this.player.sendMessage("You cleared the arena!");
this.player.sendMessage(Message.SUCCESS_ARENA_FIRST_CLEAR.getMessage());
}
this.player.sendMessage("You won!");
this.player.sendMessage(Message.SUCCESS_ARENA_WIN.getMessage());
// Teleport the player out of the arena
teleportToExit(false);
}
/**
* Teleports the playing player out of the arena
*/
private void teleportToExit(boolean immediately) {
// Teleport the player out of the arena
Location exitLocation;
if (this.arena.getExitLocation() != null) {
exitLocation = this.arena.getExitLocation();
} else {
exitLocation = this.entryState.getEntryLocation();
}
PlayerTeleporter.teleportPlayer(this.player, exitLocation, true, immediately);
}
/**
* Removes this session from current sessions
*/
private void removeSession() {
// Remove this session for game sessions to stop listeners from fiddling more with the player
boolean removedSession = MiniGames.getInstance().getParkourArenaPlayerRegistry().removePlayer(player.getUniqueId());
if (!removedSession) {
MiniGames.log(Level.SEVERE, "Unable to remove parkour arena session for " + player.getName() + ". " +
"This will have unintended consequences.");
}
}
/**
* Registers the player's record if necessary, and prints record information to the player
*/
private void registerRecord() {
ArenaRecordsRegistry recordsRegistry = this.arena.getData().getRecordRegistries().get(this.gameMode);
long timeElapsed = System.currentTimeMillis() - this.startTime;
announceRecord(recordsRegistry.registerTimeRecord(this.player.getUniqueId(), timeElapsed), "time");
announceRecord(recordsRegistry.registerDeathRecord(this.player.getUniqueId(), this.deaths), "least deaths");
}
/**
* Announces a record set by this player
*
* @param recordResult <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
*/
@Override
public void triggerLoss() {
this.deaths++;
//Teleport the player back to the top
@ -179,45 +98,27 @@ public class ParkourArenaSession implements ArenaSession {
this.entryState.setArenaState();
}
/**
* Triggers a quit for the player playing in this session
*/
public void triggerQuit(boolean immediately) {
// Stop this session
stopSession();
// Teleport the player out of the arena
teleportToExit(immediately);
player.sendMessage("You quit the arena!");
}
/**
* Stops this session, and disables flight mode
*/
private void stopSession() {
// Remove this session from game sessions to stop listeners from fiddling more with the player
removeSession();
// Remove flight mode
entryState.restore();
}
/**
* Gets the arena this session is being played in
*
* @return <p>The session's arena</p>
*/
@Override
public @NotNull ParkourArena getArena() {
return this.arena;
}
/**
* Gets the player playing in this session
*
* @return <p>This session's player</p>
*/
public @NotNull Player getPlayer() {
return this.player;
@Override
protected void removeSession() {
// Remove this session for game sessions to stop listeners from fiddling more with the player
boolean removedSession = MiniGames.getInstance().getParkourArenaPlayerRegistry().removePlayer(
player.getUniqueId(), true);
if (!removedSession) {
MiniGames.log(Level.SEVERE, "Unable to remove parkour arena session for " + player.getName() + ". " +
"This will have unintended consequences.");
}
}
@Override
protected String getGameModeString() {
return switch (this.gameMode) {
case DEFAULT -> "default";
};
}
}

View File

@ -1,10 +1,15 @@
package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.arena.AbstractPlayerEntryState;
import net.knarcraft.minigames.container.SerializableUUID;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.UUID;
/**
* The state of a player before entering a parkour arena
*/
@ -19,14 +24,60 @@ public class ParkourPlayerEntryState extends AbstractPlayerEntryState {
super(player, makePlayerInvisible);
}
/**
* Instantiates a new parkour player entry state
*
* @param playerId <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
public void setArenaState() {
super.setArenaState();
this.player.setAllowFlight(false);
this.player.setFlying(false);
this.player.setGameMode(GameMode.ADVENTURE);
this.player.setSwimming(false);
this.player.setCollidable(false);
Player player = getPlayer();
if (player == null) {
return;
}
player.setAllowFlight(false);
player.setFlying(false);
player.setGameMode(GameMode.ADVENTURE);
player.setSwimming(false);
player.setCollidable(false);
}
/**
* Deserializes a ParkourPlayerEntryState from the given data
*
* @return <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.function.Supplier;
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
/**
* An abstract class for an arena joining tab-completer
*/
@ -34,13 +36,13 @@ public abstract class JoinArenaTabCompleter implements TabCompleter {
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String label, @NotNull String[] arguments) {
if (arguments.length == 1) {
return arenaNameSupplier.get();
return filterMatchingContains(arenaNameSupplier.get(), arguments[0]);
} else if (arguments.length == 2) {
List<String> gameModes = new ArrayList<>();
for (ArenaGameMode gameMode : gameMode.getValues()) {
gameModes.add(gameMode.name().toLowerCase());
}
return gameModes;
return filterMatchingContains(gameModes, arguments[1]);
} else {
return new ArrayList<>();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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.arena.ArenaSession;
import net.knarcraft.minigames.config.Message;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@ -40,7 +41,7 @@ public class CommandListener implements Listener {
}
}
player.sendMessage("You cannot use that command while in an arena!");
player.sendMessage(Message.ERROR_ILLEGAL_COMMAND.getMessage());
event.setCancelled(true);
}

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.ParkourArenaSession;
import net.knarcraft.minigames.config.DropperConfiguration;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.config.ParkourConfiguration;
import net.knarcraft.minigames.config.SharedConfiguration;
import org.bukkit.Location;
@ -80,19 +81,30 @@ public class MoveListener implements Listener {
List<Location> checkpoints = arena.getCheckpoints();
for (Location checkpoint : checkpoints) {
Location previousCheckpoint = arenaSession.getRegisteredCheckpoint();
if (checkpoint.getBlock().equals(event.getTo().getBlock()) && !checkpoint.equals(previousCheckpoint)) {
if (parkourConfiguration.enforceCheckpointOrder()) {
int checkpointIndex = checkpoints.indexOf(checkpoint);
int previousIndex = previousCheckpoint == null ? -1 : checkpoints.indexOf(previousCheckpoint);
if (checkpointIndex - previousIndex != 1) {
continue;
}
}
arenaSession.registerCheckpoint(checkpoint.clone());
event.getPlayer().sendMessage("Checkpoint reached!");
// Skip if checkpoint has not been reached
if (!checkpoint.getBlock().equals(event.getTo().getBlock())) {
continue;
}
// If the checkpoint is the same as the previously reached one, abort
if (previousCheckpoint != null && checkpoint.getBlock().equals(previousCheckpoint.getBlock())) {
return;
}
// If not the correct checkpoint according to the enforced order, abort
if (parkourConfiguration.enforceCheckpointOrder()) {
int checkpointIndex = checkpoints.indexOf(checkpoint);
int previousIndex = previousCheckpoint == null ? -1 : checkpoints.indexOf(previousCheckpoint);
if (checkpointIndex - previousIndex != 1) {
return;
}
}
// Register the checkpoint
arenaSession.registerCheckpoint(checkpoint.clone());
event.getPlayer().sendMessage(Message.SUCCESS_CHECKPOINT_REACHED.getMessage());
return;
}
}

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;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.PlayerEntryState;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
/**
* A helper class for dealing with arena storage
*/
public final class ArenaStorageHelper {
private ArenaStorageHelper() {
}
/**
* Stores the given entry states to disk
*
* @param key <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
*
@ -20,7 +70,7 @@ public final class ArenaStorageHelper {
* @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>
*/
static @NotNull File getArenaDataFile(File root, @NotNull UUID arenaId) {
public static @NotNull File getArenaDataFile(File root, @NotNull UUID arenaId) {
File arenaDataFile = new File(root, arenaId + ".yml");
if (!root.exists() && !root.mkdirs()) {
MiniGames.log(Level.SEVERE, "Unable to create the arena data directories");

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

View File

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

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

View File

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

View File

@ -3,18 +3,24 @@ package net.knarcraft.minigames.util;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.Arena;
import net.knarcraft.minigames.arena.ArenaHandler;
import net.knarcraft.minigames.arena.EditablePropertyType;
import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty;
import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty;
import org.bukkit.Material;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A helper-class for common tab-completions
*/
public final class TabCompleteHelper {
private static Map<EditablePropertyType, List<String>> tabCompleteSuggestions;
private TabCompleteHelper() {
}
@ -76,4 +82,137 @@ public final class TabCompleteHelper {
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
prefix: MiniGames
version: '${project.version}'
main: net.knarcraft.minigames.MiniGames
api-version: 1.19