mirror of
https://github.com/garbagemule/MobArena.git
synced 2024-11-22 18:46:45 +01:00
Add join-interrupt-timer per-arena setting.
This commit adds a new per-arena setting, join-interrupt-timer, which, when set to a positive number, will introduce a delay to the join and spec commands. During this delay, if the player takes damage or moves more than one block's distance, the command will be interrupted. Closes #482
This commit is contained in:
parent
c1375a31f5
commit
924a419b47
@ -12,6 +12,7 @@ These changes will (most likely) be included in the next version.
|
||||
|
||||
## [Unreleased]
|
||||
- It is now possible to add a fixed delay (in seconds) between waves with the new per-arena setting `next-wave-delay`.
|
||||
- The new per-arena setting `join-interrupt-timer` makes it possible to add a "delay" to the join and spec commands. If the player moves or takes damage during this delay, the command is interrupted. This should help prevent exploits on PvP servers.
|
||||
- Right-clicking is now allowed in the lobby. This makes it possible to activate blocks like buttons and levers.
|
||||
- Snow and ice no longer melts in arenas.
|
||||
|
||||
|
@ -6,6 +6,7 @@ import static com.garbagemule.MobArena.util.config.ConfigUtils.parseLocation;
|
||||
import com.garbagemule.MobArena.framework.Arena;
|
||||
import com.garbagemule.MobArena.framework.ArenaMaster;
|
||||
import com.garbagemule.MobArena.things.Thing;
|
||||
import com.garbagemule.MobArena.util.JoinInterruptTimer;
|
||||
import com.garbagemule.MobArena.util.config.ConfigUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
@ -44,6 +45,8 @@ public class ArenaMasterImpl implements ArenaMaster
|
||||
|
||||
private boolean enabled;
|
||||
|
||||
private JoinInterruptTimer joinInterruptTimer;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
@ -60,6 +63,8 @@ public class ArenaMasterImpl implements ArenaMaster
|
||||
this.spawnsPets = new SpawnsPets(Material.BONE, Material.RAW_FISH);
|
||||
|
||||
this.enabled = config.getBoolean("global-settings.enabled", true);
|
||||
|
||||
this.joinInterruptTimer = new JoinInterruptTimer();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -114,6 +119,10 @@ public class ArenaMasterImpl implements ArenaMaster
|
||||
return allowedCommands.contains(command);
|
||||
}
|
||||
|
||||
public JoinInterruptTimer getJoinInterruptTimer() {
|
||||
return joinInterruptTimer;
|
||||
}
|
||||
|
||||
/*
|
||||
* /////////////////////////////////////////////////////////////////////////
|
||||
* // // Arena getters //
|
||||
|
@ -30,6 +30,9 @@ public enum Msg {
|
||||
JOIN_PLAYER_LIMIT_REACHED("The player limit of this arena has been reached."),
|
||||
JOIN_STORE_INV_FAIL("Failed to store inventory. Try again."),
|
||||
JOIN_EXISTING_INV_RESTORED("Your old inventory items have been restored."),
|
||||
JOIN_AFTER_DELAY("Joining arena in &c%&r seconds..."),
|
||||
JOIN_INTERRUPTED_BY_DAMAGE("Join aborted due to taking damage."),
|
||||
JOIN_INTERRUPTED_BY_MOVEMENT("Join aborted due to movement."),
|
||||
JOIN_PLAYER_JOINED("You joined the arena. Have fun!"),
|
||||
LEAVE_NOT_PLAYING("You are not in the arena."),
|
||||
LEAVE_NOT_READY("You did not ready up in time! Next time, ready up by clicking an iron block."),
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.garbagemule.MobArena.commands.user;
|
||||
|
||||
import com.garbagemule.MobArena.MobArena;
|
||||
import com.garbagemule.MobArena.Msg;
|
||||
import com.garbagemule.MobArena.commands.Command;
|
||||
import com.garbagemule.MobArena.commands.CommandInfo;
|
||||
@ -31,26 +32,43 @@ public class JoinCommand implements Command
|
||||
|
||||
// Run some rough sanity checks, and grab the arena to join.
|
||||
Arena toArena = Commands.getArenaToJoinOrSpec(am, p, arg1);
|
||||
if (toArena == null) {
|
||||
if (toArena == null || !canJoin(p, toArena)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Deny joining from other arenas
|
||||
Arena fromArena = am.getArenaWithPlayer(p);
|
||||
if (fromArena != null && (fromArena.inArena(p) || fromArena.inLobby(p))) {
|
||||
fromArena.getMessenger().tell(p, Msg.JOIN_ALREADY_PLAYING);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Per-arena sanity checks
|
||||
if (!toArena.canJoin(p)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Force leave previous arena
|
||||
if (fromArena != null) fromArena.playerLeave(p);
|
||||
|
||||
// Join the arena!
|
||||
return toArena.playerJoin(p, p.getLocation());
|
||||
int seconds = toArena.getSettings().getInt("join-interrupt-timer", 0);
|
||||
if (seconds > 0) {
|
||||
boolean started = am.getJoinInterruptTimer().start(p, toArena, seconds, () -> tryJoin(p, toArena));
|
||||
if (started) {
|
||||
toArena.getMessenger().tell(p, Msg.JOIN_AFTER_DELAY, String.valueOf(seconds));
|
||||
} else {
|
||||
toArena.getMessenger().tell(p, Msg.JOIN_ALREADY_PLAYING);
|
||||
}
|
||||
} else {
|
||||
tryJoin(p, toArena);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean canJoin(Player player, Arena arena) {
|
||||
MobArena plugin = arena.getPlugin();
|
||||
ArenaMaster am = plugin.getArenaMaster();
|
||||
if (am.getJoinInterruptTimer().isWaiting(player)) {
|
||||
plugin.getGlobalMessenger().tell(player, Msg.JOIN_ALREADY_PLAYING);
|
||||
return false;
|
||||
}
|
||||
Arena current = am.getArenaWithPlayer(player);
|
||||
if (current != null) {
|
||||
current.getMessenger().tell(player, Msg.JOIN_ALREADY_PLAYING);
|
||||
return false;
|
||||
}
|
||||
return arena.canJoin(player);
|
||||
}
|
||||
|
||||
private void tryJoin(Player player, Arena arena) {
|
||||
if (canJoin(player, arena)) {
|
||||
arena.playerJoin(player, player.getLocation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.garbagemule.MobArena.commands.user;
|
||||
|
||||
import com.garbagemule.MobArena.MobArena;
|
||||
import com.garbagemule.MobArena.Msg;
|
||||
import com.garbagemule.MobArena.commands.Command;
|
||||
import com.garbagemule.MobArena.commands.CommandInfo;
|
||||
@ -31,27 +32,43 @@ public class SpecCommand implements Command
|
||||
|
||||
// Run some rough sanity checks, and grab the arena to spec.
|
||||
Arena toArena = Commands.getArenaToJoinOrSpec(am, p, arg1);
|
||||
if (toArena == null) {
|
||||
if (toArena == null || !canSpec(p, toArena)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Deny spectating from other arenas
|
||||
Arena fromArena = am.getArenaWithPlayer(p);
|
||||
if (fromArena != null && (fromArena.inArena(p) || fromArena.inLobby(p))) {
|
||||
fromArena.getMessenger().tell(p, Msg.SPEC_ALREADY_PLAYING);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Per-arena sanity checks
|
||||
if (!toArena.canSpec(p)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Force leave previous arena
|
||||
if (fromArena != null) fromArena.playerLeave(p);
|
||||
|
||||
// Spec the arena!
|
||||
toArena.playerSpec(p, p.getLocation());
|
||||
int seconds = toArena.getSettings().getInt("join-interrupt-timer", 0);
|
||||
if (seconds > 0) {
|
||||
boolean started = am.getJoinInterruptTimer().start(p, toArena, seconds, () -> trySpec(p, toArena));
|
||||
if (started) {
|
||||
toArena.getMessenger().tell(p, Msg.JOIN_AFTER_DELAY, String.valueOf(seconds));
|
||||
} else {
|
||||
toArena.getMessenger().tell(p, Msg.SPEC_ALREADY_PLAYING);
|
||||
}
|
||||
} else {
|
||||
trySpec(p, toArena);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean canSpec(Player player, Arena arena) {
|
||||
MobArena plugin = arena.getPlugin();
|
||||
ArenaMaster am = plugin.getArenaMaster();
|
||||
if (am.getJoinInterruptTimer().isWaiting(player)) {
|
||||
plugin.getGlobalMessenger().tell(player, Msg.SPEC_ALREADY_PLAYING);
|
||||
return false;
|
||||
}
|
||||
Arena current = arena.getPlugin().getArenaMaster().getArenaWithPlayer(player);
|
||||
if (current != null) {
|
||||
current.getMessenger().tell(player, Msg.SPEC_ALREADY_PLAYING);
|
||||
return false;
|
||||
}
|
||||
return arena.canSpec(player);
|
||||
}
|
||||
|
||||
private void trySpec(Player player, Arena arena) {
|
||||
if (canSpec(player, arena)) {
|
||||
arena.playerSpec(player, player.getLocation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import com.garbagemule.MobArena.ArenaClass;
|
||||
import com.garbagemule.MobArena.Messenger;
|
||||
import com.garbagemule.MobArena.MobArena;
|
||||
import com.garbagemule.MobArena.SpawnsPets;
|
||||
import com.garbagemule.MobArena.util.JoinInterruptTimer;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
@ -87,6 +88,8 @@ public interface ArenaMaster
|
||||
Arena getArenaWithName(Collection<Arena> arenas, String configName);
|
||||
|
||||
boolean isAllowed(String command);
|
||||
|
||||
JoinInterruptTimer getJoinInterruptTimer();
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,154 @@
|
||||
package com.garbagemule.MobArena.util;
|
||||
|
||||
import com.garbagemule.MobArena.Msg;
|
||||
import com.garbagemule.MobArena.framework.Arena;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.player.PlayerKickEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class JoinInterruptTimer {
|
||||
private Set<UUID> waiting;
|
||||
|
||||
public JoinInterruptTimer() {
|
||||
this.waiting = new HashSet<>();
|
||||
}
|
||||
|
||||
public boolean isWaiting(Player player) {
|
||||
return waiting.contains(player.getUniqueId());
|
||||
}
|
||||
|
||||
public boolean start(Player player, Arena arena, int seconds, Runnable completed) {
|
||||
if (isWaiting(player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UUID id = player.getUniqueId();
|
||||
waiting.add(id);
|
||||
|
||||
TimedInterruptListener listener = new TimedInterruptListener(
|
||||
player,
|
||||
arena,
|
||||
() -> waiting.remove(id),
|
||||
completed
|
||||
);
|
||||
listener.runTaskLater(arena.getPlugin(), seconds * 20);
|
||||
Bukkit.getPluginManager().registerEvents(listener, arena.getPlugin());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static class TimedInterruptListener extends BukkitRunnable implements Listener {
|
||||
Player player;
|
||||
Location location;
|
||||
Arena arena;
|
||||
Runnable stopped;
|
||||
Runnable completed;
|
||||
|
||||
boolean done;
|
||||
|
||||
TimedInterruptListener(Player player, Arena arena, Runnable stopped, Runnable completed) {
|
||||
this.player = player;
|
||||
this.location = player.getLocation();
|
||||
this.arena = arena;
|
||||
this.stopped = stopped;
|
||||
this.completed = completed;
|
||||
|
||||
this.done = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
complete();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void on(EntityDamageEvent event) {
|
||||
if (done || !event.getEntity().equals(player)) {
|
||||
return;
|
||||
}
|
||||
arena.getMessenger().tell(player, Msg.JOIN_INTERRUPTED_BY_DAMAGE);
|
||||
interrupt();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void on(PlayerMoveEvent event) {
|
||||
if (done || !event.getPlayer().equals(player)) {
|
||||
return;
|
||||
}
|
||||
if (location.getWorld() == event.getTo().getWorld() && location.distanceSquared(event.getTo()) < 1) {
|
||||
return;
|
||||
}
|
||||
arena.getMessenger().tell(player, Msg.JOIN_INTERRUPTED_BY_MOVEMENT);
|
||||
interrupt();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void on(PlayerQuitEvent event) {
|
||||
if (done || !event.getPlayer().equals(player)) {
|
||||
return;
|
||||
}
|
||||
interrupt();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void on(PlayerKickEvent event) {
|
||||
if (done || !event.getPlayer().equals(player)) {
|
||||
return;
|
||||
}
|
||||
interrupt();
|
||||
}
|
||||
|
||||
void complete() {
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
done = true;
|
||||
|
||||
try {
|
||||
HandlerList.unregisterAll(this);
|
||||
stopped.run();
|
||||
completed.run();
|
||||
} finally {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
void interrupt() {
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
done = true;
|
||||
|
||||
try {
|
||||
HandlerList.unregisterAll(this);
|
||||
stopped.run();
|
||||
super.cancel();
|
||||
} catch (IllegalStateException e) {
|
||||
// Swallow this exception from super.cancel()
|
||||
} finally {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
player = null;
|
||||
location = null;
|
||||
arena = null;
|
||||
stopped = null;
|
||||
completed = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ share-items-in-arena: true
|
||||
min-players: 0
|
||||
max-players: 0
|
||||
max-join-distance: 0
|
||||
join-interrupt-timer: 0
|
||||
first-wave-delay: 5
|
||||
next-wave-delay: 0
|
||||
wave-interval: 15
|
||||
|
Loading…
Reference in New Issue
Block a user