Prevent infinite loops due to PlayerDropItemEvent from /give command.
Apparently, the /give command will drop an item "as" the target player, resulting in the player picking up the item if there is room in their inventory. This triggers a PlayerDropItemEvent, which MobArena catches and tries to cancel if the target player is in an arena. Furthermore, if the player is a spectator outside of the arena region, MobArena will force the player to leave the arena. This triggers the reward granting logic, so using the /give command as a reward has the potential of causing an infinite loop. This commit introduces the idea of "leaving players", i.e. players in the process of leaving the arena. In the event of a dropped item from a player who is currently leaving an arena, the PlayerDropItemEvent is ignored, because it is assumed to be from the /give command. Note that this doesn't actually prevent normal PlayerDropItemEvents from causing a forced leave, since the player won't be in the process of leaving at that specific point.
This commit is contained in:
@@ -97,6 +97,7 @@ public class ArenaImpl implements Arena
|
|||||||
private Map<Player,PlayerData> playerData = new HashMap<>();
|
private Map<Player,PlayerData> playerData = new HashMap<>();
|
||||||
|
|
||||||
private Set<Player> arenaPlayers, lobbyPlayers, readyPlayers, specPlayers, deadPlayers;
|
private Set<Player> arenaPlayers, lobbyPlayers, readyPlayers, specPlayers, deadPlayers;
|
||||||
|
private Set<Player> leavingPlayers;
|
||||||
private Set<Player> randoms;
|
private Set<Player> randoms;
|
||||||
|
|
||||||
// Classes stuff
|
// Classes stuff
|
||||||
@@ -164,6 +165,7 @@ public class ArenaImpl implements Arena
|
|||||||
this.specPlayers = new HashSet<>();
|
this.specPlayers = new HashSet<>();
|
||||||
this.deadPlayers = new HashSet<>();
|
this.deadPlayers = new HashSet<>();
|
||||||
this.randoms = new HashSet<>();
|
this.randoms = new HashSet<>();
|
||||||
|
this.leavingPlayers = new HashSet<>();
|
||||||
|
|
||||||
// Classes, items and permissions
|
// Classes, items and permissions
|
||||||
this.classes = plugin.getArenaMaster().getClasses();
|
this.classes = plugin.getArenaMaster().getClasses();
|
||||||
@@ -742,6 +744,12 @@ public class ArenaImpl implements Arena
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Protect against infinite leave loops
|
||||||
|
if (leavingPlayers.contains(p)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
leavingPlayers.add(p);
|
||||||
|
|
||||||
// Clear inventory if player is an arena player, and unmount
|
// Clear inventory if player is an arena player, and unmount
|
||||||
if (arenaPlayers.contains(p)) {
|
if (arenaPlayers.contains(p)) {
|
||||||
unmount(p);
|
unmount(p);
|
||||||
@@ -772,9 +780,16 @@ public class ArenaImpl implements Arena
|
|||||||
discardPlayer(p);
|
discardPlayer(p);
|
||||||
|
|
||||||
endArena();
|
endArena();
|
||||||
|
|
||||||
|
leavingPlayers.remove(p);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLeaving(Player p) {
|
||||||
|
return leavingPlayers.contains(p);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void playerDeath(Player p)
|
public void playerDeath(Player p)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -942,6 +942,21 @@ public class ArenaListener
|
|||||||
public void onPlayerDropItem(PlayerDropItemEvent event) {
|
public void onPlayerDropItem(PlayerDropItemEvent event) {
|
||||||
Player p = event.getPlayer();
|
Player p = event.getPlayer();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the player "drops an item" while in the process of leaving the
|
||||||
|
* arena, it has to be due to something that happens in the leaving
|
||||||
|
* process because most of the event system is single-threaded. This
|
||||||
|
* doesn't make much sense, but it can happen if the player earns a
|
||||||
|
* command reward of /give, which drops the item with a drop delay of
|
||||||
|
* 0 from the player, causing the player to immediately pick it up.
|
||||||
|
* Cancelling this event causes the player to somehow pick up the item
|
||||||
|
* twice, so we don't want to do that. This early return should guard
|
||||||
|
* against this specific case, and hopefully not break anything else.
|
||||||
|
*/
|
||||||
|
if (arena.isLeaving(p)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If the player is active in the arena, only cancel if sharing is not allowed
|
// If the player is active in the arena, only cancel if sharing is not allowed
|
||||||
if (arena.inArena(p)) {
|
if (arena.inArena(p)) {
|
||||||
if (!canShare) {
|
if (!canShare) {
|
||||||
|
|||||||
@@ -153,6 +153,8 @@ public interface Arena
|
|||||||
void playerReady(Player p);
|
void playerReady(Player p);
|
||||||
|
|
||||||
boolean playerLeave(Player p);
|
boolean playerLeave(Player p);
|
||||||
|
|
||||||
|
boolean isLeaving(Player p);
|
||||||
|
|
||||||
void playerDeath(Player p);
|
void playerDeath(Player p);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user