Implements #27 among other things

It was found that the Spigot API's methods for cancelling player collisions won't work without changing the scoreboard. Because of that, the normal option has been disabled. The invisibility option has also been removed, as that's a bad idea if players can still push each-other.
The toggle player option which is implemented in this commit does disable player collision, so that's the only working way right now.

A potential ConcurrentModificationException has been fixed.
The parkourCheckpoint command has been removed, as the functionality is now available through the API.
This commit is contained in:
Kristian Knarvik 2023-05-10 15:14:28 +02:00
parent 00ac0582f4
commit 7848a0a028
23 changed files with 193 additions and 194 deletions

View File

@ -58,7 +58,6 @@ The only permission normal players will need is `minigames.join` which is set to
| /parkourGroupSet | /pgset | \<arena> \<group> | Puts the given arena in the given group. Use "none" to remove an existing group. |
| /parkourGroupList | /pglist | \[group] | Lists groups, or the stages of a group if a group is specified. |
| [/parkourGroupSwap](#droppergroupswap) | /pgswap | \<arena1> \<arena2> | Swaps the two arenas in the group's ordered list. |
| /parkourCheckpoint | /pcheck | | Triggers a teleportation to your previous checkpoint. |
### Command explanation dropper
@ -187,8 +186,6 @@ These are all the options that can be changed for an arena.
| mustDoGroupedInSequence | true/false | true | Whether grouped dropper arenas must be played in the correct sequence |
| ignoreRecordsUntilGroupBeatenOnce | true/false | false | Whether records won't be registered unless the player has already beaten all arenas in a group. That means players are required to do a second play-through to register a record for a grouped arena. |
| mustDoNormalModeFirst | true/false | true | Whether a player must do the normal/default game-mode before playing any other game-modes |
| makePlayersInvisible | true/false | false | Whether players should be made invisible while playing in a dropper arena |
| disableHitCollision | true/false | true | Whether players should have their entity hit collision disabled while in an arena. This prevents players from pushing each-other if in the same arena. |
| liquidHitBoxDepth | -1 < decimal < 0 | -0.8 | This decides how far inside a non-solid block the player must go before detection triggers (-1, 0). The closer to -1 it is, the more accurate it will seem to the player, but the likelihood of not detecting the hit increases. |
| solidHitBoxDistance | 0 < decimal < 1 | 0.2 | This decides the distance the player must be from a block below them before a hit triggers (0, 1). If too low, the likelihood of detecting the hit decreases, but it won't look like the player hit the block without being near. |
| blockWhitelist | list | [see this](#blockwhitelist-default) | A whitelist for which blocks won't trigger a loss when hit/passed through. The win block check happens before the loss check, so even blocks on the whitelist can be used as the win-block. "+" denotes a [material tag](#notes-about-material-tags). |
@ -200,7 +197,6 @@ These are all the options that can be changed for an arena.
| enforceCheckpointOrder | true/false | false | Whether to enforce the order in which a player must reach checkpoints. Enabling this ensures that a player cannot trigger a previous checkpoint by accident. It also ensures players cannot skip a checkpoint, even if the arena layout makes it possible. |
| mustDoGroupedInSequence | true/false | true | Whether grouped dropper arenas must be played in the correct sequence |
| ignoreRecordsUntilGroupBeatenOnce | true/false | false | Whether records won't be registered unless the player has already beaten all arenas in a group. That means players are required to do a second play-through to register a record for a grouped arena. |
| makePlayersInvisible | true/false | false | Whether players should be made invisible while playing in a dropper arena |
| killPlaneBlocks | list | [see this](#killplaneblocks-default) | The types of blocks compromising parkour arenas' kill planes. Add any materials you want to use for the "bottom" of your parkour arenas. +WOOL and other [material tags](#notes-about-material-tags) are supported. |
#### blockWhitelist default:

View File

@ -3,6 +3,7 @@ package net.knarcraft.minigames;
import net.knarcraft.knargui.GUIListener;
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
import net.knarcraft.minigames.arena.ArenaSession;
import net.knarcraft.minigames.arena.PlayerVisibilityManager;
import net.knarcraft.minigames.arena.dropper.DropperArena;
import net.knarcraft.minigames.arena.dropper.DropperArenaData;
import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
@ -41,7 +42,6 @@ import net.knarcraft.minigames.command.parkour.EditParkourArenaTabCompleter;
import net.knarcraft.minigames.command.parkour.JoinParkourArenaCommand;
import net.knarcraft.minigames.command.parkour.JoinParkourArenaTabCompleter;
import net.knarcraft.minigames.command.parkour.ListParkourArenaCommand;
import net.knarcraft.minigames.command.parkour.ParkourCheckpointCommand;
import net.knarcraft.minigames.command.parkour.ParkourGroupListCommand;
import net.knarcraft.minigames.command.parkour.ParkourGroupSetCommand;
import net.knarcraft.minigames.command.parkour.ParkourGroupSwapCommand;
@ -87,6 +87,7 @@ public final class MiniGames extends JavaPlugin {
private ParkourRecordExpansion parkourRecordExpansion;
private ParkourArenaHandler parkourArenaHandler;
private ArenaPlayerRegistry<ParkourArena> parkourArenaPlayerRegistry;
private PlayerVisibilityManager playerVisibilityManager;
/**
* Gets an instance of this plugin
@ -162,6 +163,15 @@ public final class MiniGames extends JavaPlugin {
return this.parkourConfiguration;
}
/**
* Gets the manager keeping track of player visibility
*
* @return <p>The player visibility manager</p>
*/
public PlayerVisibilityManager getPlayerVisibilityManager() {
return this.playerVisibilityManager;
}
/**
* Gets the current session of the given player
*
@ -288,6 +298,7 @@ public final class MiniGames extends JavaPlugin {
this.parkourArenaPlayerRegistry = new ParkourArenaPlayerRegistry();
this.parkourArenaHandler = new ParkourArenaHandler(this.parkourArenaPlayerRegistry);
this.parkourArenaHandler.load();
this.playerVisibilityManager = new PlayerVisibilityManager();
}
/**
@ -360,7 +371,6 @@ public final class MiniGames extends JavaPlugin {
registerCommand("parkourGroupSet", new ParkourGroupSetCommand(), null);
registerCommand("parkourGroupSwap", new ParkourGroupSwapCommand(), null);
registerCommand("parkourGroupList", new ParkourGroupListCommand(), null);
registerCommand("parkourCheckpoint", new ParkourCheckpointCommand(), null);
}
}

View File

@ -29,6 +29,11 @@ public abstract class AbstractArenaPlayerRegistry<K extends Arena> implements Ar
loadEntryStates();
}
@Override
public @NotNull Set<UUID> getPlayingPlayers() {
return arenaPlayers.keySet();
}
@Override
public @Nullable PlayerEntryState getEntryState(@NotNull UUID playerId) {
return this.entryStates.get(playerId);
@ -65,13 +70,15 @@ public abstract class AbstractArenaPlayerRegistry<K extends Arena> implements Ar
@Override
public void removeForArena(K arena, boolean immediately) {
Set<UUID> removed = new HashSet<>();
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());
entry.getValue().triggerQuit(immediately, false);
removed.add(entry.getKey());
}
}
removed.forEach(this.arenaPlayers::remove);
}
/**

View File

@ -1,5 +1,6 @@
package net.knarcraft.minigames.arena;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.container.PlaceholderContainer;
import net.knarcraft.minigames.property.RecordResult;
@ -33,11 +34,15 @@ public abstract class AbstractArenaSession implements ArenaSession {
}
@Override
public void triggerQuit(boolean immediately) {
public void triggerQuit(boolean immediately, boolean removeSession) {
// Stop this session
removeSession();
if (removeSession) {
removeSession();
}
// Teleport the player out of the arena
teleportToExit(immediately);
// Make the player visible to everyone
MiniGames.getInstance().getPlayerVisibilityManager().showPlayersFor(player);
player.sendMessage(Message.SUCCESS_ARENA_QUIT.getMessage());
}

View File

@ -6,8 +6,6 @@ import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
@ -21,59 +19,49 @@ import java.util.logging.Level;
public abstract class AbstractPlayerEntryState implements PlayerEntryState {
protected final UUID playerId;
private final boolean makePlayerInvisible;
private final Location entryLocation;
private final boolean originalIsFlying;
private final GameMode originalGameMode;
private final boolean originalAllowFlight;
private final boolean originalInvulnerable;
private final boolean originalIsSwimming;
private final boolean originalCollideAble;
/**
* Instantiates a new abstract player entry state
*
* @param player <p>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 player <p>The player whose state this should keep track of</p>
*/
public AbstractPlayerEntryState(@NotNull Player player, boolean makePlayerInvisible) {
public AbstractPlayerEntryState(@NotNull Player player) {
this.playerId = player.getUniqueId();
this.makePlayerInvisible = makePlayerInvisible;
this.entryLocation = player.getLocation().clone();
this.originalIsFlying = player.isFlying();
this.originalGameMode = player.getGameMode();
this.originalAllowFlight = player.getAllowFlight();
this.originalInvulnerable = player.isInvulnerable();
this.originalIsSwimming = player.isSwimming();
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,
public AbstractPlayerEntryState(@NotNull UUID playerId, Location entryLocation,
boolean originalIsFlying, GameMode originalGameMode, boolean originalAllowFlight,
boolean originalInvulnerable, boolean originalIsSwimming,
boolean originalCollideAble) {
boolean originalInvulnerable, boolean originalIsSwimming) {
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
@ -81,18 +69,6 @@ public abstract class AbstractPlayerEntryState implements PlayerEntryState {
return this.playerId;
}
@Override
public void setArenaState() {
Player player = getPlayer();
if (player == null) {
return;
}
if (this.makePlayerInvisible) {
player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY,
PotionEffect.INFINITE_DURATION, 3));
}
}
@Override
public boolean restore() {
Player player = getPlayer();
@ -110,10 +86,6 @@ public abstract class AbstractPlayerEntryState implements PlayerEntryState {
player.setAllowFlight(this.originalAllowFlight);
player.setInvulnerable(this.originalInvulnerable);
player.setSwimming(this.originalIsSwimming);
player.setCollidable(this.originalCollideAble);
if (this.makePlayerInvisible) {
player.removePotionEffect(PotionEffectType.INVISIBILITY);
}
}
@Override
@ -140,14 +112,12 @@ public abstract class AbstractPlayerEntryState implements PlayerEntryState {
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.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
import java.util.UUID;
/**
@ -12,6 +13,13 @@ import java.util.UUID;
*/
public interface ArenaPlayerRegistry<K extends Arena> {
/**
* Gets the ids of the players currently playing
*
* @return <p>The ids of the playing players</p>
*/
@NotNull Set<UUID> getPlayingPlayers();
/**
* Gets the current entry state for the given player
*

View File

@ -28,9 +28,10 @@ public interface ArenaSession {
/**
* Triggers a quit for the player playing in this session
*
* @param immediately <p>Whether to to the teleportation immediately, not using any timers</p>
* @param immediately <p>Whether to to the teleportation immediately, not using any timers</p>
* @param removeSession <p>Whether to also remove the session. Should usually be true.</p>
*/
void triggerQuit(boolean immediately);
void triggerQuit(boolean immediately, boolean removeSession);
/**
* Gets the arena this session is being played in

View File

@ -0,0 +1,95 @@
package net.knarcraft.minigames.arena;
import net.knarcraft.minigames.MiniGames;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* A manager for keeping track of which players have set other players as hidden
*/
public class PlayerVisibilityManager {
private final Set<UUID> hidingEnabledFor = new HashSet<>();
/**
* Toggles whether players should be hidden for the player with the given id
*
* @param player <p>The the player to update</p>
*/
public void toggleHidePlayers(@NotNull ArenaPlayerRegistry<?> playerRegistry, @NotNull Player player) {
if (hidingEnabledFor.contains(player.getUniqueId())) {
hidingEnabledFor.remove(player.getUniqueId());
// Make all other players visible again
changeVisibilityFor(playerRegistry, player, false);
} else {
hidingEnabledFor.add(player.getUniqueId());
// Make all other players hidden
changeVisibilityFor(playerRegistry, player, true);
}
}
/**
* Updates which players are seen as hidden
*
* @param playerRegistry <p>The registry containing all playing players</p>
* @param player <p>The player that joined the arena</p>
*/
public void updateHiddenPlayers(@NotNull ArenaPlayerRegistry<?> playerRegistry, @NotNull Player player) {
boolean hideForPlayer = hidingEnabledFor.contains(player.getUniqueId());
for (UUID playerId : playerRegistry.getPlayingPlayers()) {
Player otherPlayer = Bukkit.getPlayer(playerId);
if (otherPlayer == null) {
continue;
}
// Hide the arena player from the newly joined player
if (hideForPlayer) {
player.hidePlayer(MiniGames.getInstance(), otherPlayer);
}
// Hide the newly joined player from this player
if (hidingEnabledFor.contains(playerId)) {
otherPlayer.hidePlayer(MiniGames.getInstance(), player);
}
}
}
/**
* Makes all players visible to the given player
*
* @param player <p>The player to update visibility for</p>
*/
public void showPlayersFor(@NotNull Player player) {
for (Player otherPlayer : Bukkit.getOnlinePlayers()) {
player.showPlayer(MiniGames.getInstance(), otherPlayer);
otherPlayer.showPlayer(MiniGames.getInstance(), player);
}
}
/**
* Changes whether the given player can see the other players in the arena
*
* @param playerRegistry <p>The player registry containing other players</p>
* @param player <p>The player to change the visibility for</p>
* @param hide <p>Whether to hide the players or show the players</p>
*/
private void changeVisibilityFor(@NotNull ArenaPlayerRegistry<?> playerRegistry, @NotNull Player player, boolean hide) {
for (UUID playerId : playerRegistry.getPlayingPlayers()) {
Player otherPlayer = Bukkit.getPlayer(playerId);
if (otherPlayer == null) {
continue;
}
if (hide) {
player.hidePlayer(MiniGames.getInstance(), otherPlayer);
} else {
player.showPlayer(MiniGames.getInstance(), otherPlayer);
}
}
}
}

View File

@ -3,7 +3,6 @@ package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.AbstractArenaSession;
import net.knarcraft.minigames.arena.PlayerEntryState;
import net.knarcraft.minigames.config.DropperConfiguration;
import net.knarcraft.minigames.config.Message;
import net.knarcraft.minigames.gui.ArenaGUI;
import net.knarcraft.minigames.gui.DropperGUI;
@ -38,11 +37,7 @@ public class DropperArenaSession extends AbstractArenaSession {
this.player = player;
this.gameMode = gameMode;
DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration();
boolean makeInvisible = configuration.makePlayersInvisible();
boolean disableCollision = configuration.disableHitCollision();
this.entryState = new DropperPlayerEntryState(player, gameMode, makeInvisible, disableCollision,
dropperArena.getPlayerHorizontalVelocity());
this.entryState = new DropperPlayerEntryState(player, gameMode, dropperArena.getPlayerHorizontalVelocity());
this.entryState.setArenaState();
}

View File

@ -16,7 +16,6 @@ import java.util.UUID;
public class DropperPlayerEntryState extends AbstractPlayerEntryState {
private final float originalFlySpeed;
private final boolean disableHitCollision;
private final float horizontalVelocity;
private final DropperArenaGameMode arenaGameMode;
@ -26,11 +25,10 @@ public class DropperPlayerEntryState extends AbstractPlayerEntryState {
* @param player <p>The player whose state should be stored</p>
*/
public DropperPlayerEntryState(@NotNull Player player, @NotNull DropperArenaGameMode arenaGameMode,
boolean makePlayerInvisible, boolean disableHitCollision, float horizontalVelocity) {
super(player, makePlayerInvisible);
float horizontalVelocity) {
super(player);
this.originalFlySpeed = player.getFlySpeed();
this.arenaGameMode = arenaGameMode;
this.disableHitCollision = disableHitCollision;
this.horizontalVelocity = horizontalVelocity;
}
@ -38,31 +36,29 @@ public class DropperPlayerEntryState extends AbstractPlayerEntryState {
* 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>
* @param originalFlySpeed <p>The fly-speed of the player before entering the arena</p>
* @param horizontalVelocity <p>The horizontal velocity of the player before entering the arena</p>
*/
public DropperPlayerEntryState(@NotNull UUID playerId, boolean makePlayerInvisible, Location entryLocation,
public DropperPlayerEntryState(@NotNull UUID playerId, 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);
float originalFlySpeed, float horizontalVelocity,
DropperArenaGameMode arenaGameMode) {
super(playerId, entryLocation, originalIsFlying, originalGameMode, originalAllowFlight,
originalInvulnerable, originalIsSwimming);
this.originalFlySpeed = originalFlySpeed;
this.disableHitCollision = disableHitCollision;
this.horizontalVelocity = horizontalVelocity;
this.arenaGameMode = arenaGameMode;
}
@Override
public void setArenaState() {
super.setArenaState();
Player player = getPlayer();
if (player == null) {
return;
@ -71,9 +67,6 @@ public class DropperPlayerEntryState extends AbstractPlayerEntryState {
player.setFlying(true);
player.setGameMode(GameMode.ADVENTURE);
player.setSwimming(false);
if (this.disableHitCollision) {
player.setCollidable(false);
}
// If playing on the inverted game-mode, negate the horizontal velocity to swap the controls
if (this.arenaGameMode == DropperArenaGameMode.INVERTED) {
@ -104,7 +97,6 @@ public class DropperPlayerEntryState extends AbstractPlayerEntryState {
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;
@ -118,22 +110,19 @@ public class DropperPlayerEntryState extends AbstractPlayerEntryState {
@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);
return new DropperPlayerEntryState(playerId, entryLocation, originalIsFlying,
originalGameMode, originalAllowFlight, originalInvulnerable, originalIsSwimming,
originalFlySpeed, horizontalVelocity, arenaGameMode);
}
}

View File

@ -4,7 +4,6 @@ import net.knarcraft.minigames.MiniGames;
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.gui.ArenaGUI;
import net.knarcraft.minigames.gui.ParkourGUI;
import net.knarcraft.minigames.util.PlayerTeleporter;
@ -41,9 +40,7 @@ public class ParkourArenaSession extends AbstractArenaSession {
this.player = player;
this.gameMode = gameMode;
ParkourConfiguration configuration = MiniGames.getInstance().getParkourConfiguration();
boolean makeInvisible = configuration.makePlayersInvisible();
this.entryState = new ParkourPlayerEntryState(player, makeInvisible);
this.entryState = new ParkourPlayerEntryState(player);
this.entryState.setArenaState();
}

View File

@ -20,34 +20,30 @@ public class ParkourPlayerEntryState extends AbstractPlayerEntryState {
*
* @param player <p>The player whose state should be stored</p>
*/
public ParkourPlayerEntryState(@NotNull Player player, boolean makePlayerInvisible) {
super(player, makePlayerInvisible);
public ParkourPlayerEntryState(@NotNull Player player) {
super(player);
}
/**
* 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,
public ParkourPlayerEntryState(@NotNull UUID playerId, Location entryLocation,
boolean originalIsFlying, GameMode originalGameMode, boolean originalAllowFlight,
boolean originalInvulnerable, boolean originalIsSwimming,
boolean originalCollideAble) {
super(playerId, makePlayerInvisible, entryLocation, originalIsFlying, originalGameMode, originalAllowFlight,
originalInvulnerable, originalIsSwimming, originalCollideAble);
boolean originalInvulnerable, boolean originalIsSwimming) {
super(playerId, entryLocation, originalIsFlying, originalGameMode, originalAllowFlight,
originalInvulnerable, originalIsSwimming);
}
@Override
public void setArenaState() {
super.setArenaState();
Player player = getPlayer();
if (player == null) {
return;
@ -56,7 +52,6 @@ public class ParkourPlayerEntryState extends AbstractPlayerEntryState {
player.setFlying(false);
player.setGameMode(GameMode.ADVENTURE);
player.setSwimming(false);
player.setCollidable(false);
}
/**
@ -67,17 +62,15 @@ public class ParkourPlayerEntryState extends AbstractPlayerEntryState {
@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);
return new ParkourPlayerEntryState(playerId, entryLocation, originalIsFlying,
originalGameMode, originalAllowFlight, originalInvulnerable, originalIsSwimming);
}
}

View File

@ -32,7 +32,7 @@ public class LeaveArenaCommand implements TabExecutor {
return false;
}
existingSession.triggerQuit(false);
existingSession.triggerQuit(false, true);
return true;
}

View File

@ -94,11 +94,13 @@ public class JoinDropperArenaCommand implements CommandExecutor {
boolean teleported = PlayerTeleporter.teleportPlayer(player, specifiedArena.getSpawnLocation(), false, false);
if (!teleported) {
player.sendMessage(Message.ERROR_ARENA_TELEPORT_FAILED.getMessage());
newSession.triggerQuit(false);
newSession.triggerQuit(false, true);
return false;
} else {
// Make sure to update the state again in the air to remove a potential swimming state
newSession.getEntryState().setArenaState();
// Update visibility for the player
MiniGames.getInstance().getPlayerVisibilityManager().updateHiddenPlayers(playerRegistry, player);
player.sendMessage(Message.SUCCESS_ARENA_JOINED.getMessage());
return true;
}

View File

@ -92,11 +92,13 @@ public class JoinParkourArenaCommand implements CommandExecutor {
boolean teleported = PlayerTeleporter.teleportPlayer(player, specifiedArena.getSpawnLocation(), false, false);
if (!teleported) {
player.sendMessage(Message.ERROR_ARENA_TELEPORT_FAILED.getMessage());
newSession.triggerQuit(false);
newSession.triggerQuit(false, true);
return false;
} else {
// Make sure to update the state again in the air to remove a potential swimming state
newSession.getEntryState().setArenaState();
// Update visibility for the player
MiniGames.getInstance().getPlayerVisibilityManager().updateHiddenPlayers(playerRegistry, player);
player.sendMessage(Message.SUCCESS_ARENA_JOINED.getMessage());
return true;
}

View File

@ -1,44 +0,0 @@
package net.knarcraft.minigames.command.parkour;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaSession;
import net.knarcraft.minigames.arena.parkour.ParkourArenaSession;
import net.knarcraft.minigames.config.Message;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* The command for returning to the previous checkpoint
*/
public class ParkourCheckpointCommand implements TabExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] strings) {
if (!(commandSender instanceof Player player)) {
commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
return false;
}
ArenaSession session = MiniGames.getInstance().getSession(player.getUniqueId());
if (session instanceof ParkourArenaSession) {
session.triggerLoss();
}
return true;
}
@Nullable
@Override
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
@NotNull String[] strings) {
return new ArrayList<>();
}
}

View File

@ -19,8 +19,6 @@ public class DropperConfiguration extends MiniGameConfiguration {
private boolean mustDoGroupedInSequence;
private boolean ignoreRecordsUntilGroupBeatenOnce;
private boolean mustDoNormalModeFirst;
private boolean makePlayersInvisible;
private boolean disableHitCollision;
private boolean blockSneaking;
private boolean blockSprinting;
private Set<Material> blockWhitelist;
@ -97,24 +95,6 @@ public class DropperConfiguration extends MiniGameConfiguration {
return this.ignoreRecordsUntilGroupBeatenOnce;
}
/**
* Gets whether players should be made invisible while in an arena
*
* @return <p>Whether players should be made invisible</p>
*/
public boolean makePlayersInvisible() {
return this.makePlayersInvisible;
}
/**
* Gets whether entity hit-collision of players in an arena should be disabled
*
* @return <p>Whether to disable hit collision</p>
*/
public boolean disableHitCollision() {
return this.disableHitCollision;
}
/**
* Gets whether players trying to sneak while in a dropper arena to increase their downwards speed should be blocked
*
@ -141,8 +121,6 @@ public class DropperConfiguration extends MiniGameConfiguration {
this.mustDoGroupedInSequence = configuration.getBoolean(rootNode + "mustDoGroupedInSequence", true);
this.ignoreRecordsUntilGroupBeatenOnce = configuration.getBoolean(rootNode + "ignoreRecordsUntilGroupBeatenOnce", false);
this.mustDoNormalModeFirst = configuration.getBoolean(rootNode + "mustDoNormalModeFirst", true);
this.makePlayersInvisible = configuration.getBoolean(rootNode + "makePlayersInvisible", false);
this.disableHitCollision = configuration.getBoolean(rootNode + "disableHitCollision", true);
this.blockSprinting = configuration.getBoolean(rootNode + "blockSprinting", true);
this.blockSneaking = configuration.getBoolean(rootNode + "blockSneaking", true);
this.blockWhitelist = loadMaterialList(rootNode + "blockWhitelist");
@ -176,8 +154,6 @@ public class DropperConfiguration extends MiniGameConfiguration {
"\n" + "Must do groups in sequence: " + mustDoGroupedInSequence +
"\n" + "Ignore records until group beaten once: " + ignoreRecordsUntilGroupBeatenOnce +
"\n" + "Must do normal mode first: " + mustDoNormalModeFirst +
"\n" + "Make players invisible: " + makePlayersInvisible +
"\n" + "Disable hit collision: " + disableHitCollision +
"\n" + "Block whitelist: ");
for (Material material : blockWhitelist) {
builder.append("\n - ").append(material.name());

View File

@ -16,7 +16,6 @@ public class ParkourConfiguration extends MiniGameConfiguration {
private boolean enforceCheckpointOrder;
private boolean mustDoGroupedInSequence;
private boolean ignoreRecordsUntilGroupBeatenOnce;
private boolean makePlayersInvisible;
private Set<Material> killPlaneBlocks;
/**
@ -55,15 +54,6 @@ public class ParkourConfiguration extends MiniGameConfiguration {
return this.ignoreRecordsUntilGroupBeatenOnce;
}
/**
* Gets whether players should be made invisible while in an arena
*
* @return <p>Whether players should be made invisible</p>
*/
public boolean makePlayersInvisible() {
return this.makePlayersInvisible;
}
/**
* Gets all types of blocks constituting parkour arenas' kill planes
*
@ -78,7 +68,6 @@ public class ParkourConfiguration extends MiniGameConfiguration {
this.enforceCheckpointOrder = configuration.getBoolean(rootNode + "enforceCheckpointOrder", false);
this.mustDoGroupedInSequence = configuration.getBoolean(rootNode + "mustDoGroupedInSequence", true);
this.ignoreRecordsUntilGroupBeatenOnce = configuration.getBoolean(rootNode + "ignoreRecordsUntilGroupBeatenOnce", false);
this.makePlayersInvisible = configuration.getBoolean(rootNode + "makePlayersInvisible", false);
this.killPlaneBlocks = loadMaterialList(rootNode + "killPlaneBlocks");
}
@ -89,7 +78,6 @@ public class ParkourConfiguration extends MiniGameConfiguration {
"Current configuration:" +
"\n" + "Must do groups in sequence: " + mustDoGroupedInSequence +
"\n" + "Ignore records until group beaten once: " + ignoreRecordsUntilGroupBeatenOnce +
"\n" + "Make players invisible: " + makePlayersInvisible +
"\n" + "Kill plane blocks: ");
for (Material material : killPlaneBlocks) {
builder.append("\n - ").append(material.name());

View File

@ -4,6 +4,7 @@ import net.knarcraft.knargui.AbstractGUI;
import net.knarcraft.knargui.GUIAction;
import net.knarcraft.knargui.item.GUIItemFactory;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
import net.knarcraft.minigames.arena.ArenaSession;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material;
@ -18,14 +19,18 @@ import java.util.List;
*/
public abstract class ArenaGUI extends AbstractGUI {
protected final ArenaPlayerRegistry<?> playerRegistry;
/**
* Instantiates a new arena gui
*
* @param inventorySize <p>The size of the GUI's inventory</p>
* @param inventoryName <p>The name of the inventory</p>
* @param inventorySize <p>The size of the GUI's inventory</p>
* @param inventoryName <p>The name of the inventory</p>
* @param playerRegistry <p>The player registry used for this GUI</p>
*/
public ArenaGUI(int inventorySize, String inventoryName) {
public ArenaGUI(int inventorySize, String inventoryName, ArenaPlayerRegistry<?> playerRegistry) {
super(inventorySize, inventoryName, null);
this.playerRegistry = playerRegistry;
}
/**
@ -88,9 +93,19 @@ public abstract class ArenaGUI extends AbstractGUI {
return (player) -> {
ArenaSession session = MiniGames.getInstance().getSession(player.getUniqueId());
if (session != null) {
session.triggerQuit(false);
session.triggerQuit(false, true);
}
};
}
/**
* Gets the action to run when triggering the toggle players action
*
* @return <p>The action for triggering player visibility</p>
*/
protected GUIAction getTogglePlayersAction() {
return (player) -> MiniGames.getInstance().getPlayerVisibilityManager().toggleHidePlayers(playerRegistry,
player);
}
}

View File

@ -1,5 +1,7 @@
package net.knarcraft.minigames.gui;
import net.knarcraft.minigames.MiniGames;
/**
* A GUI used in the dropper arena
*/
@ -9,10 +11,11 @@ public class DropperGUI extends ArenaGUI {
* Instantiates a new dropper gui
*/
public DropperGUI() {
super(9, "Dropper");
super(9, "Dropper", MiniGames.getInstance().getDropperArenaPlayerRegistry());
setItem(0, getTogglePlayersItem());
setItem(2, getLeaveItem());
setAnyClickAction(0, getTogglePlayersAction());
setAnyClickAction(2, getLeaveAction());
}

View File

@ -17,11 +17,12 @@ import java.util.List;
public class ParkourGUI extends ArenaGUI {
public ParkourGUI() {
super(9, "Parkour");
super(9, "Parkour", MiniGames.getInstance().getParkourArenaPlayerRegistry());
setItem(0, getTogglePlayersItem());
setItem(2, getGiveUpItem());
setItem(4, getLeaveItem());
setAnyClickAction(0, getTogglePlayersAction());
setAnyClickAction(2, getGiveUpAction());
setAnyClickAction(4, getLeaveAction());
}
@ -32,7 +33,7 @@ public class ParkourGUI extends ArenaGUI {
* @return <p>A give up item</p>
*/
private ItemStack getGiveUpItem() {
GUIItemFactory giveUpItemFactory = new GUIItemFactory(Material.SKELETON_SKULL);
GUIItemFactory giveUpItemFactory = new GUIItemFactory(Material.RESPAWN_ANCHOR);
List<String> loreLines = getLoreLines();
loreLines.add(ChatColor.GRAY + "Use this item to give up");
loreLines.add(ChatColor.GRAY + "and go to the last checkpoint");

View File

@ -29,9 +29,6 @@ public class CommandListener implements Listener {
allowedCommands.add("/mLeave");
allowedCommands.add("/dLeave");
allowedCommands.add("/pLeave");
allowedCommands.add("/parkourCheckpoint");
allowedCommands.add("/pCheckpoint");
allowedCommands.add("/pCheck");
allowedCommands.add("/miniGamesMenu");
allowedCommands.add("/mMenu");

View File

@ -150,13 +150,6 @@ commands:
permission: minigames.remove.parkour
usage: /<command> <arena>
description: Used to remove an existing parkour arena
parkourCheckpoint:
aliases:
- pcheckpoint
- pcheck
permission: minigames.join.parkour
usage: /<command>
description: Used to teleport to the previous checkpoint while in a parkour arena
permissions:
minigames.*:
description: Gives all permissions