Makes levers clicked in parkour arenas turn off

When a lever is clicked while inside a parkour arena, that lever is saved. Once the last player leaves, or the last player restarts, the saved levers are turned off.
This commit is contained in:
Kristian Knarvik 2024-04-22 14:52:19 +02:00
parent a1f76bcfc2
commit fdd6d94102
10 changed files with 168 additions and 36 deletions

View File

@ -19,7 +19,8 @@ import java.util.logging.Level;
*/
public abstract class AbstractArenaPlayerRegistry<K extends Arena> implements ArenaPlayerRegistry<K> {
private final Map<UUID, ArenaSession> arenaPlayers = new HashMap<>();
private final Map<Arena, Set<UUID>> arenaPlayers = new HashMap<>();
private final Map<UUID, ArenaSession> arenaSessions = new HashMap<>();
private final Map<UUID, PlayerEntryState> entryStates = new HashMap<>();
/**
@ -30,19 +31,25 @@ public abstract class AbstractArenaPlayerRegistry<K extends Arena> implements Ar
}
@Override
public @NotNull Set<UUID> getPlayingPlayers() {
return arenaPlayers.keySet();
@NotNull
public Set<UUID> getPlayingPlayers() {
return arenaSessions.keySet();
}
@Override
public @Nullable PlayerEntryState getEntryState(@NotNull UUID playerId) {
@Nullable
public 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.arenaSessions.put(playerId, arenaSession);
this.entryStates.put(playerId, arenaSession.getEntryState());
this.arenaPlayers.putIfAbsent(arenaSession.getArena(), new HashSet<>());
this.arenaPlayers.get(arenaSession.getArena()).add(playerId);
this.saveEntryStates();
}
@ -60,25 +67,38 @@ public abstract class AbstractArenaPlayerRegistry<K extends Arena> implements Ar
this.saveEntryStates();
}
return this.arenaPlayers.remove(playerId) != null;
if (this.arenaSessions.containsKey(playerId)) {
this.arenaPlayers.get(this.arenaSessions.get(playerId).getArena()).remove(playerId);
}
return this.arenaSessions.remove(playerId) != null;
}
@Override
public @Nullable ArenaSession getArenaSession(@NotNull UUID playerId) {
return this.arenaPlayers.getOrDefault(playerId, null);
return this.arenaSessions.getOrDefault(playerId, null);
}
@Override
public void removeForArena(K arena, boolean immediately) {
public void removeForArena(@NotNull K arena, boolean immediately) {
Set<UUID> removed = new HashSet<>();
for (Map.Entry<UUID, ArenaSession> entry : this.arenaPlayers.entrySet()) {
for (Map.Entry<UUID, ArenaSession> entry : this.arenaSessions.entrySet()) {
if (entry.getValue().getArena() == arena) {
// Kick the player gracefully
entry.getValue().triggerQuit(immediately, false);
removed.add(entry.getKey());
}
}
removed.forEach(this.arenaPlayers::remove);
removed.forEach(this.arenaSessions::remove);
}
@Override
@NotNull
public Set<UUID> getPlayingPlayers(@NotNull K arena) {
if (arenaPlayers.containsKey(arena)) {
return arenaPlayers.get(arena);
} else {
return new HashSet<>();
}
}
/**

View File

@ -20,28 +20,32 @@ public interface Arena {
*
* @return <p>The name of this arena</p>
*/
@NotNull String getArenaName();
@NotNull
String getArenaName();
/**
* Gets the data stored for this arena
*
* @return <p>The stored data</p>
*/
@NotNull ArenaData getData();
@NotNull
ArenaData getData();
/**
* Gets the id of this arena
*
* @return <p>This arena's identifier</p>
*/
@NotNull UUID getArenaId();
@NotNull
UUID getArenaId();
/**
* Gets this arena's sanitized name
*
* @return <p>This arena's sanitized name</p>
*/
@NotNull String getArenaNameSanitized();
@NotNull
String getArenaNameSanitized();
/**
* Removes the data file belonging to this arena
@ -63,7 +67,7 @@ public interface Arena {
* @param block <p>The block to check</p>
* @return <p>True if standing on the block will cause a win</p>
*/
boolean willCauseWin(Block block);
boolean willCauseWin(@NotNull Block block);
/**
* Gets whether standing on the given block should cause a loss
@ -71,7 +75,7 @@ public interface Arena {
* @param block <p>The block to check</p>
* @return <p>True if standing on the block will cause a loss</p>
*/
boolean willCauseLoss(Block block);
boolean willCauseLoss(@NotNull Block block);
/**
* Gets whether the win location is a solid block
@ -85,14 +89,16 @@ public interface Arena {
*
* @return <p>This arena's spawn location</p>
*/
@NotNull Location getSpawnLocation();
@NotNull
Location getSpawnLocation();
/**
* Gets this arena's exit location
*
* @return <p>This arena's exit location, or null if no such location is set.</p>
*/
@Nullable Location getExitLocation();
@Nullable
Location getExitLocation();
/**
* Adds a reward to this arena
@ -115,6 +121,7 @@ public interface Arena {
* @param rewardCondition <p>The condition to get the rewards for</p>
* @return <p>All rewards</p>
*/
@NotNull Set<Reward> getRewards(RewardCondition rewardCondition);
@NotNull
Set<Reward> getRewards(RewardCondition rewardCondition);
}

View File

@ -18,7 +18,17 @@ public interface ArenaPlayerRegistry<K extends Arena> {
*
* @return <p>The ids of the playing players</p>
*/
@NotNull Set<UUID> getPlayingPlayers();
@NotNull
Set<UUID> getPlayingPlayers();
/**
* Gets all players currently playing in the given arena
*
* @param arena <p>The arena to check</p>
* @return <p>All players currently in the arena</p>
*/
@NotNull
Set<UUID> getPlayingPlayers(@NotNull K arena);
/**
* Gets the current entry state for the given player
@ -26,7 +36,8 @@ public interface ArenaPlayerRegistry<K extends Arena> {
* @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);
@Nullable
PlayerEntryState getEntryState(@NotNull UUID playerId);
/**
* Registers that the given player has started playing the given dropper arena session
@ -50,7 +61,8 @@ public interface ArenaPlayerRegistry<K extends Arena> {
* @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);
@Nullable
ArenaSession getArenaSession(@NotNull UUID playerId);
/**
* Removes all active sessions for the given arena
@ -58,6 +70,6 @@ public interface ArenaPlayerRegistry<K extends Arena> {
* @param arena <p>The arena to remove sessions for</p>
* @param immediately <p>Whether to immediately teleport the player</p>
*/
void removeForArena(K arena, boolean immediately);
void removeForArena(@NotNull K arena, boolean immediately);
}

View File

@ -22,7 +22,7 @@ public class PlayerVisibilityManager {
*
* @param player <p>The the player to update</p>
*/
public void toggleHidePlayers(@NotNull ArenaPlayerRegistry<?> playerRegistry, @NotNull Player player) {
public void toggleHidePlayers(@Nullable ArenaPlayerRegistry<?> playerRegistry, @NotNull Player player) {
if (displayingEnabledFor.contains(player.getUniqueId())) {
displayingEnabledFor.remove(player.getUniqueId());
// Make all other players hidden

View File

@ -243,12 +243,12 @@ public class DropperArena implements Arena {
}
@Override
public boolean willCauseWin(Block block) {
public boolean willCauseWin(@NotNull Block block) {
return block.getType() == winBlockType;
}
@Override
public boolean willCauseLoss(Block block) {
public boolean willCauseLoss(@NotNull Block block) {
return !dropperConfiguration.getBlockWhitelist().contains(block.getType());
}

View File

@ -340,7 +340,7 @@ public class ParkourArena implements Arena {
}
@Override
public boolean willCauseLoss(Block block) {
public boolean willCauseLoss(@NotNull Block block) {
return this.getKillPlaneBlocks().contains(block.getType()) || this.getObstacleBlocks().contains(block.getType());
}

View File

@ -11,5 +11,5 @@ public class ParkourArenaPlayerRegistry extends AbstractArenaPlayerRegistry<Park
protected String getEntryStateStorageKey() {
return "parkour";
}
}

View File

@ -3,6 +3,7 @@ package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.AbstractArenaSession;
import net.knarcraft.minigames.arena.Arena;
import net.knarcraft.minigames.arena.PlayerEntryState;
import net.knarcraft.minigames.arena.reward.RewardCondition;
import net.knarcraft.minigames.config.MiniGameMessage;
@ -13,10 +14,17 @@ import net.knarcraft.minigames.util.GeyserHelper;
import net.knarcraft.minigames.util.PlayerTeleporter;
import net.knarcraft.minigames.util.RewardHelper;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Powerable;
import org.bukkit.entity.Player;
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.logging.Level;
/**
@ -24,10 +32,11 @@ import java.util.logging.Level;
*/
public class ParkourArenaSession extends AbstractArenaSession {
private static final @NotNull Map<Arena, Set<Block>> changedLevers = new HashMap<>();
private final @NotNull ParkourArena arena;
private final @NotNull Player player;
private final @NotNull ParkourArenaGameMode gameMode;
private Location reachedCheckpoint = null;
private @Nullable Location reachedCheckpoint = null;
/**
* Instantiates a new parkour arena session
@ -65,6 +74,16 @@ public class ParkourArenaSession extends AbstractArenaSession {
this.reachedCheckpoint = location;
}
/**
* Registers a lever change
*
* @param block <p>The block of the lever</p>
*/
public void registerChangedLever(@NotNull Block block) {
changedLevers.putIfAbsent(this.arena, new HashSet<>());
changedLevers.get(this.arena).add(block);
}
/**
* Gets the checkpoint currently registered as the player's spawn location
*
@ -132,6 +151,9 @@ public class ParkourArenaSession extends AbstractArenaSession {
@Override
public void reset() {
this.reachedCheckpoint = null;
if (MiniGames.getInstance().getParkourArenaPlayerRegistry().getPlayingPlayers(this.arena).size() == 1) {
resetLevers();
}
super.reset();
}
@ -154,4 +176,33 @@ public class ParkourArenaSession extends AbstractArenaSession {
};
}
@Override
public void triggerQuit(boolean immediately, boolean removeSession) {
super.triggerQuit(immediately, removeSession);
if (MiniGames.getInstance().getParkourArenaPlayerRegistry().getPlayingPlayers(this.arena).isEmpty()) {
resetLevers();
}
}
/**
* Resets all levers if the arena is empty
*/
private void resetLevers() {
// Make a copy in case new player join while the levers are resetting
Set<Block> changed = changedLevers.remove(this.arena);
if (changed == null || changed.isEmpty()) {
return;
}
// Reset all levers toggled by players in the arena
for (Block block : changed) {
BlockData blockData = block.getBlockData();
if (blockData instanceof Powerable powerable) {
powerable.setPowered(false);
block.setBlockData(blockData);
}
}
}
}

View File

@ -17,6 +17,7 @@ import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@ -32,7 +33,7 @@ public abstract class ArenaGUI extends AbstractGUI {
* @param inventorySize <p>The size of the GUI's inventory</p>
* @param inventoryName <p>The name of the inventory</p>
*/
public ArenaGUI(int inventorySize, String inventoryName) {
public ArenaGUI(int inventorySize, @NotNull String inventoryName) {
super(inventorySize, inventoryName, null);
}
@ -79,6 +80,7 @@ public abstract class ArenaGUI extends AbstractGUI {
*
* @return <p>A player toggle item</p>
*/
@NotNull
protected ItemStack getTogglePlayersItemDisabled() {
GUIItemFactory togglePlayersItemFactory = new SimpleGUIItemFactory(Material.PLAYER_HEAD);
List<String> loreLines = getLoreLines();
@ -95,7 +97,8 @@ public abstract class ArenaGUI extends AbstractGUI {
* @param player <p>The player to get the item for</p>
* @return <p>A player toggle item</p>
*/
protected ItemStack getTogglePlayersItemEnabled(Player player) {
@NotNull
protected ItemStack getTogglePlayersItemEnabled(@NotNull Player player) {
if (GeyserHelper.isGeyserPlayer(player)) {
return getTogglePlayersItemEnabledBedrock();
} else {
@ -108,6 +111,7 @@ public abstract class ArenaGUI extends AbstractGUI {
*
* @return <p>A player toggle item</p>
*/
@NotNull
protected ItemStack getTogglePlayersItemEnabledJava() {
PlayerHeadGUIItemFactory togglePlayersItemFactory = new PlayerHeadGUIItemFactory();
togglePlayersItemFactory.useSkin("c10591e6909e6a281b371836e462d67a2c78fa0952e910f32b41a26c48c1757c");
@ -119,6 +123,7 @@ public abstract class ArenaGUI extends AbstractGUI {
*
* @return <p>A player toggle item</p>
*/
@NotNull
protected ItemStack getTogglePlayersItemEnabledBedrock() {
return setTogglePlayersItemData(new SimpleGUIItemFactory(Material.SKELETON_SKULL));
}
@ -129,6 +134,7 @@ public abstract class ArenaGUI extends AbstractGUI {
* @param itemFactory <p>The factory to apply the data to</p>
* @return <p>The finished item, with the data applied</p>
*/
@NotNull
protected ItemStack setTogglePlayersItemData(@NotNull GUIItemFactory itemFactory) {
List<String> loreLines = getLoreLines();
loreLines.add(ChatColor.GRAY + "Use this item to enable the visibility");
@ -143,6 +149,7 @@ public abstract class ArenaGUI extends AbstractGUI {
*
* @return <p>A give up item</p>
*/
@NotNull
protected ItemStack getGiveUpItem() {
GUIItemFactory giveUpItemFactory = new SimpleGUIItemFactory(Material.RESPAWN_ANCHOR);
List<String> loreLines = getLoreLines();
@ -158,6 +165,7 @@ public abstract class ArenaGUI extends AbstractGUI {
*
* @return <p>A leave item</p>
*/
@NotNull
protected ItemStack getLeaveItem() {
GUIItemFactory leaveItemFactory = new SimpleGUIItemFactory(Material.BARRIER);
List<String> loreLines = getLoreLines();
@ -172,6 +180,7 @@ public abstract class ArenaGUI extends AbstractGUI {
*
* @return <p>An arraylist with one blank line</p>
*/
@NotNull
protected List<String> getLoreLines() {
List<String> loreLines = new ArrayList<>();
loreLines.add("");
@ -184,7 +193,7 @@ public abstract class ArenaGUI extends AbstractGUI {
* @param inventorySlot <p>The inventory slot the action should be added to</p>
* @param action <p>The action to register</p>
*/
protected void setAnyClickAction(int inventorySlot, GUIAction action) {
protected void setAnyClickAction(int inventorySlot, @NotNull GUIAction action) {
setClickAction(inventorySlot, ClickType.LEFT, action);
setClickAction(inventorySlot, ClickType.RIGHT, action);
}
@ -194,6 +203,7 @@ public abstract class ArenaGUI extends AbstractGUI {
*
* @return <p>The leave action</p>
*/
@NotNull
public static GUIAction getLeaveAction() {
return (player) -> {
ArenaSession session = MiniGames.getInstance().getSession(player.getUniqueId());
@ -208,6 +218,7 @@ public abstract class ArenaGUI extends AbstractGUI {
*
* @return <p>The action for triggering a session restart</p>
*/
@NotNull
public static GUIAction getRestartAction() {
return (player -> {
ArenaSession session = MiniGames.getInstance().getSession(player.getUniqueId());
@ -224,7 +235,8 @@ public abstract class ArenaGUI extends AbstractGUI {
* @param inventorySlot <p>The inventory slot to replace when toggling</p>
* @return <p>The action for triggering player visibility</p>
*/
public GUIAction getTogglePlayersAction(ArenaPlayerRegistry<?> playerRegistry, int inventorySlot) {
@NotNull
public GUIAction getTogglePlayersAction(@Nullable ArenaPlayerRegistry<?> playerRegistry, int inventorySlot) {
return (player) -> {
PlayerVisibilityManager visibilityManager = MiniGames.getInstance().getPlayerVisibilityManager();
visibilityManager.toggleHidePlayers(playerRegistry, player);
@ -241,6 +253,7 @@ public abstract class ArenaGUI extends AbstractGUI {
*
* @return <p>The give up action</p>
*/
@NotNull
public static GUIAction getGiveUpAction() {
return (player) -> {
ArenaSession session = MiniGames.getInstance().getSession(player.getUniqueId());

View File

@ -1,17 +1,25 @@
package net.knarcraft.minigames.listener;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
import net.knarcraft.minigames.arena.parkour.ParkourArena;
import net.knarcraft.minigames.arena.parkour.ParkourArenaSession;
import net.knarcraft.minigames.gui.ArenaGUI;
import net.knarcraft.minigames.gui.ParkourGUI;
import net.knarcraft.minigames.property.PersistentDataKey;
import net.knarcraft.minigames.util.GUIHelper;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.data.type.Switch;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
/**
* A listener that listens for player interactions
@ -19,7 +27,26 @@ import org.bukkit.persistence.PersistentDataType;
public class InteractListener implements Listener {
@EventHandler
public void menuInteractListener(PlayerInteractEvent event) {
public void menuInteractListener(@NotNull PlayerInteractEvent event) {
handleMenuClick(event);
Block clicked = event.getClickedBlock();
if (event.useInteractedBlock() != Event.Result.DENY && event.getAction() == Action.RIGHT_CLICK_BLOCK &&
clicked != null && clicked.getBlockData() instanceof Switch) {
ArenaPlayerRegistry<ParkourArena> playerRegistry = MiniGames.getInstance().getParkourArenaPlayerRegistry();
ParkourArenaSession arenaSession = (ParkourArenaSession) playerRegistry.getArenaSession(event.getPlayer().getUniqueId());
if (arenaSession != null) {
arenaSession.registerChangedLever(clicked);
}
}
}
/**
* Handles clicking of the menu item
*
* @param event <p>The triggered player interact event</p>
*/
private void handleMenuClick(@NotNull PlayerInteractEvent event) {
ItemStack item = event.getItem();
if (item == null || !item.hasItemMeta()) {
return;
@ -33,7 +60,8 @@ public class InteractListener implements Listener {
PersistentDataKey.MENU_ITEM.getKeyName()), PersistentDataType.INTEGER);
if (persistentData != null && persistentData == PersistentDataKey.MENU_ITEM.getDataValue()) {
event.setCancelled(true);
event.setUseItemInHand(Event.Result.DENY);
event.setUseInteractedBlock(Event.Result.DENY);
GUIHelper.openGUI(event.getPlayer());
return;
}
@ -41,7 +69,8 @@ public class InteractListener implements Listener {
persistentData = meta.getPersistentDataContainer().get(new NamespacedKey(MiniGames.getInstance(),
PersistentDataKey.LEAVE_ITEM.getKeyName()), PersistentDataType.INTEGER);
if (persistentData != null) {
event.setCancelled(true);
event.setUseItemInHand(Event.Result.DENY);
event.setUseInteractedBlock(Event.Result.DENY);
if (persistentData == PersistentDataKey.LEAVE_ITEM.getDataValue()) {
ArenaGUI.getLeaveAction().run(event.getPlayer());
} else if (persistentData == PersistentDataKey.GIVE_UP_ITEM.getDataValue()) {