mirror of
https://github.com/garbagemule/MobArena.git
synced 2024-11-26 20:45:17 +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]
|
## [Unreleased]
|
||||||
- It is now possible to add a fixed delay (in seconds) between waves with the new per-arena setting `next-wave-delay`.
|
- 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.
|
- 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.
|
- 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.Arena;
|
||||||
import com.garbagemule.MobArena.framework.ArenaMaster;
|
import com.garbagemule.MobArena.framework.ArenaMaster;
|
||||||
import com.garbagemule.MobArena.things.Thing;
|
import com.garbagemule.MobArena.things.Thing;
|
||||||
|
import com.garbagemule.MobArena.util.JoinInterruptTimer;
|
||||||
import com.garbagemule.MobArena.util.config.ConfigUtils;
|
import com.garbagemule.MobArena.util.config.ConfigUtils;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
@ -44,6 +45,8 @@ public class ArenaMasterImpl implements ArenaMaster
|
|||||||
|
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
|
|
||||||
|
private JoinInterruptTimer joinInterruptTimer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor.
|
* Default constructor.
|
||||||
*/
|
*/
|
||||||
@ -60,6 +63,8 @@ public class ArenaMasterImpl implements ArenaMaster
|
|||||||
this.spawnsPets = new SpawnsPets(Material.BONE, Material.RAW_FISH);
|
this.spawnsPets = new SpawnsPets(Material.BONE, Material.RAW_FISH);
|
||||||
|
|
||||||
this.enabled = config.getBoolean("global-settings.enabled", true);
|
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);
|
return allowedCommands.contains(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JoinInterruptTimer getJoinInterruptTimer() {
|
||||||
|
return joinInterruptTimer;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* /////////////////////////////////////////////////////////////////////////
|
* /////////////////////////////////////////////////////////////////////////
|
||||||
* // // Arena getters //
|
* // // Arena getters //
|
||||||
|
@ -30,6 +30,9 @@ public enum Msg {
|
|||||||
JOIN_PLAYER_LIMIT_REACHED("The player limit of this arena has been reached."),
|
JOIN_PLAYER_LIMIT_REACHED("The player limit of this arena has been reached."),
|
||||||
JOIN_STORE_INV_FAIL("Failed to store inventory. Try again."),
|
JOIN_STORE_INV_FAIL("Failed to store inventory. Try again."),
|
||||||
JOIN_EXISTING_INV_RESTORED("Your old inventory items have been restored."),
|
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!"),
|
JOIN_PLAYER_JOINED("You joined the arena. Have fun!"),
|
||||||
LEAVE_NOT_PLAYING("You are not in the arena."),
|
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."),
|
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;
|
package com.garbagemule.MobArena.commands.user;
|
||||||
|
|
||||||
|
import com.garbagemule.MobArena.MobArena;
|
||||||
import com.garbagemule.MobArena.Msg;
|
import com.garbagemule.MobArena.Msg;
|
||||||
import com.garbagemule.MobArena.commands.Command;
|
import com.garbagemule.MobArena.commands.Command;
|
||||||
import com.garbagemule.MobArena.commands.CommandInfo;
|
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.
|
// Run some rough sanity checks, and grab the arena to join.
|
||||||
Arena toArena = Commands.getArenaToJoinOrSpec(am, p, arg1);
|
Arena toArena = Commands.getArenaToJoinOrSpec(am, p, arg1);
|
||||||
if (toArena == null) {
|
if (toArena == null || !canJoin(p, toArena)) {
|
||||||
return true;
|
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!
|
// 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;
|
package com.garbagemule.MobArena.commands.user;
|
||||||
|
|
||||||
|
import com.garbagemule.MobArena.MobArena;
|
||||||
import com.garbagemule.MobArena.Msg;
|
import com.garbagemule.MobArena.Msg;
|
||||||
import com.garbagemule.MobArena.commands.Command;
|
import com.garbagemule.MobArena.commands.Command;
|
||||||
import com.garbagemule.MobArena.commands.CommandInfo;
|
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.
|
// Run some rough sanity checks, and grab the arena to spec.
|
||||||
Arena toArena = Commands.getArenaToJoinOrSpec(am, p, arg1);
|
Arena toArena = Commands.getArenaToJoinOrSpec(am, p, arg1);
|
||||||
if (toArena == null) {
|
if (toArena == null || !canSpec(p, toArena)) {
|
||||||
return true;
|
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!
|
// 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;
|
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.Messenger;
|
||||||
import com.garbagemule.MobArena.MobArena;
|
import com.garbagemule.MobArena.MobArena;
|
||||||
import com.garbagemule.MobArena.SpawnsPets;
|
import com.garbagemule.MobArena.SpawnsPets;
|
||||||
|
import com.garbagemule.MobArena.util.JoinInterruptTimer;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
@ -88,6 +89,8 @@ public interface ArenaMaster
|
|||||||
|
|
||||||
boolean isAllowed(String command);
|
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
|
min-players: 0
|
||||||
max-players: 0
|
max-players: 0
|
||||||
max-join-distance: 0
|
max-join-distance: 0
|
||||||
|
join-interrupt-timer: 0
|
||||||
first-wave-delay: 5
|
first-wave-delay: 5
|
||||||
next-wave-delay: 0
|
next-wave-delay: 0
|
||||||
wave-interval: 15
|
wave-interval: 15
|
||||||
|
Loading…
Reference in New Issue
Block a user