mirror of
https://github.com/SunNetservers/MiniGames.git
synced 2024-12-05 00:43:15 +01:00
Greatly improves handling of un-exited sessions #25
This commit is contained in:
parent
b2fbaf0e68
commit
401490df58
@ -9,6 +9,7 @@ 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.DropperPlayerEntryState;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArena;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaData;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode;
|
||||
@ -16,6 +17,7 @@ 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;
|
||||
@ -50,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;
|
||||
@ -217,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
|
||||
@ -240,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);
|
||||
|
@ -1,11 +1,16 @@
|
||||
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
|
||||
@ -17,18 +22,29 @@ public abstract class AbstractArenaPlayerRegistry<K extends Arena> implements Ar
|
||||
private final Map<UUID, ArenaSession> arenaPlayers = new HashMap<>();
|
||||
private final Map<UUID, PlayerEntryState> entryStates = new HashMap<>();
|
||||
|
||||
//TODO: Save all entry states each time the map changes
|
||||
//TODO: If a player joins, and their entry state exists, restore the state
|
||||
/**
|
||||
* 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) {
|
||||
this.entryStates.remove(playerId);
|
||||
this.saveEntryStates();
|
||||
return this.arenaPlayers.remove(playerId) != null;
|
||||
}
|
||||
|
||||
@ -44,9 +60,38 @@ public abstract class AbstractArenaPlayerRegistry<K extends Arena> implements Ar
|
||||
// Kick the player gracefully
|
||||
entry.getValue().triggerQuit(immediately);
|
||||
this.arenaPlayers.remove(entry.getKey());
|
||||
this.entryStates.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 are leaving in the middle of a game, or the server crashes. MiniGames will do its best " +
|
||||
"to fix the players' states.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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,74 @@ 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);
|
||||
Player player = getPlayer();
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
restore(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the state of the given player
|
||||
*
|
||||
* @param player <p>The player to restore the state for</p>
|
||||
*/
|
||||
public void restore(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 +124,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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,14 @@ import java.util.UUID;
|
||||
*/
|
||||
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
|
||||
*
|
||||
|
@ -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
|
||||
@ -17,6 +21,20 @@ public interface PlayerEntryState {
|
||||
*/
|
||||
void 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
|
||||
*
|
||||
|
@ -6,4 +6,10 @@ import net.knarcraft.minigames.arena.AbstractArenaPlayerRegistry;
|
||||
* A registry to keep track of which players are playing in which arenas
|
||||
*/
|
||||
public class DropperArenaPlayerRegistry extends AbstractArenaPlayerRegistry<DropperArena> {
|
||||
|
||||
@Override
|
||||
protected String getEntryStateStorageKey() {
|
||||
return "dropper";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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,100 @@ 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);
|
||||
Player player = getPlayer();
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,4 +6,10 @@ import net.knarcraft.minigames.arena.AbstractArenaPlayerRegistry;
|
||||
* A registry to keep track of which players are playing in which arenas
|
||||
*/
|
||||
public class ParkourArenaPlayerRegistry extends AbstractArenaPlayerRegistry<ParkourArena> {
|
||||
|
||||
@Override
|
||||
protected String getEntryStateStorageKey() {
|
||||
return "parkour";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,87 +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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
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.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());
|
||||
|
||||
entryState.restore(player);
|
||||
return entryState.getEntryLocation();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user