This commit is contained in:
Daniel Boston 2016-07-16 02:55:12 +00:00 committed by GitHub
commit 2e7cffd637
5 changed files with 147 additions and 20 deletions

20
pom.xml
View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.wimbli.WorldBorder</groupId>
<artifactId>WorldBorder</artifactId>
<version>1.8.5</version>
<version>1.8.8</version>
<name>WorldBorder</name>
<url>https://github.com/Brettflan/WorldBorder</url>
<issueManagement>
@ -31,13 +31,13 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.8.7-R0.1-SNAPSHOT</version>
<version>1.9-R0.1-SNAPSHOT</version>
</dependency>
<!--Bukkit API-->
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<version>1.8.7-R0.1-SNAPSHOT</version>
<version>1.9-R0.1-SNAPSHOT</version>
</dependency>
<!--Dynmap API-->
<dependency>
@ -49,15 +49,21 @@
<build>
<defaultGoal>clean install</defaultGoal>
<finalName>${project.artifactId}</finalName>
<finalName>${project.artifactId}-${project.version}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<version>3.5.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>

View File

@ -2,8 +2,9 @@ package com.wimbli.WorldBorder;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import com.google.common.collect.ImmutableList;
@ -17,7 +18,7 @@ import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.Location;
import org.bukkit.util.Vector;
import org.bukkit.World;
import org.bukkit.scheduler.BukkitRunnable;
public class BorderCheckTask implements Runnable
{
@ -36,8 +37,97 @@ public class BorderCheckTask implements Runnable
}
}
// track players who are being handled (moved back inside the border) already; needed since Bukkit is sometimes sending teleport events with the old (now incorrect) location still indicated, which can lead to a loop when we then teleport them thinking they're outside the border, triggering event again, etc.
private static Set<String> handlingPlayers = Collections.synchronizedSet(new LinkedHashSet<String>());
/** track players who are being handled (moved back inside the border) already; needed
* since Bukkit is sometimes sending teleport events with the old (now incorrect) location
* still indicated, which can lead to a loop when we then teleport them thinking they're
* outside the border, triggering event again, etc.
*/
private static Map<String, BukkitRunnable> handlingPlayers = Collections.synchronizedMap(new LinkedHashMap<String, BukkitRunnable>());
/**
* In 1.9, there is a significant delay between teleportation event and when the player's location is actually updated.
* However, the player world is updated immediately. This disconnection causes the regular checkPlayer to
* incorrectly test the player's prior-world location against the new-world location during that amorphous
* in-between period. Basically, this checks for the location to actually update, e.g. to be different from the
* "from" location of the portal event.
*
* This function allows a configurable recheck to let Minecraft "catch up" the player's <i>real</i> location.
*
* In the meantime, the player is exempted from border crossing checks (and from spurious additional teleport event
* checks).
*
* Note that additional portal teleports (e.g. if the player immediately portals back) will reset
* this check gracefully.
*
* @param player The player who is being exempted.
* @param prior the location the player has come from
* @param maxDelay The <i>maximum</i> ticks to spend exempting this player
* @param recheckDelay The ticks to wait inbetween rechecks.
*/
public static void timedPlayerExemption(final Player player, final Location prior, final long maxDelay, final long recheckDelay) {
// Check for existing watch; cancel if one exists.
BukkitRunnable alreadyWatching = handlingPlayers.get(player.getName().toLowerCase());
if (alreadyWatching != null) {
try {
alreadyWatching.cancel();
} catch (IllegalStateException e){}
}
alreadyWatching = new BukkitRunnable() {
private final String playerName = player.getName().toLowerCase();
private final UUID playerUUID = player.getUniqueId();
private long currentDelay = recheckDelay;
@Override
public void run() {
// Are we done checking?
if (currentDelay > maxDelay) {
this.cancel();
handlingPlayers.remove(playerName);
if (Config.Debug())
Config.log("Done watching " + playerName + ". They are in the hands of fate, now.");
return;
}
currentDelay += recheckDelay;
// Is this player still online?
Player player = Bukkit.getPlayer(playerUUID);
if (player == null) { // assume offline
this.cancel();
handlingPlayers.remove(playerName);
if (Config.Debug())
Config.log("Looks like " + playerName + " logged off. Suspending watch.");
return;
}
// Are we still stuck between worlds?
Location current = player.getLocation();
if (current.getBlockX() != prior.getBlockX() && current.getBlockY() != prior.getBlockY() &&
current.getBlockZ() != prior.getBlockZ()) {
// No, we made it!
this.cancel();
handlingPlayers.remove(playerName);
if (Config.Debug())
Config.log("Minecraft caught up with " + playerName + ". Ending watch.");
return;
}
if (Config.Debug()) {
Config.log("Based on teleport " + playerName +
" has moved, but Minecraft still has them at old location " +
prior.toString() + ". Checking again in " + recheckDelay);
}
}
};
// Store the exemption and start the recheck.
handlingPlayers.put(player.getName().toLowerCase(), alreadyWatching);
alreadyWatching.runTaskTimer(WorldBorder.plugin, recheckDelay, recheckDelay);
if (Config.Debug())
Config.log("Rechecking " + player.getName() + "'s world every " +
recheckDelay + " ticks for " + maxDelay + " ticks.");
}
// set targetLoc only if not current player location; set returnLocationOnly to true to have new Location returned if they need to be moved to one, instead of directly handling it
public static Location checkPlayer(Player player, Location targetLoc, boolean returnLocationOnly, boolean notify)
@ -56,11 +146,11 @@ public class BorderCheckTask implements Runnable
return null;
// if player is in bypass list (from bypass command), allow them beyond border; also ignore players currently being handled already
if (Config.isPlayerBypassing(player.getUniqueId()) || handlingPlayers.contains(player.getName().toLowerCase()))
if (Config.isPlayerBypassing(player.getUniqueId()) || handlingPlayers.containsKey(player.getName().toLowerCase()))
return null;
// tag this player as being handled so we can't get stuck in a loop due to Bukkit currently sometimes repeatedly providing incorrect location through teleport event
handlingPlayers.add(player.getName().toLowerCase());
handlingPlayers.put(player.getName().toLowerCase(), null);
Location newLoc = newLocation(player, loc, border, notify);
boolean handlingVehicle = false;

View File

@ -56,6 +56,8 @@ public class Config
private static int fillMemoryTolerance = 500;
private static boolean preventBlockPlace = false;
private static boolean preventMobSpawn = false;
private static long maxExemptionTicks = 21l;
private static long portalRecheckTicks = 4l; // These together give 5 recheck chances over the course of a second.
// for monitoring plugin efficiency
// public static long timeUsed = 0;
@ -544,6 +546,21 @@ public class Config
return false;
}
public static long getMaxExemptionTicks() {
return maxExemptionTicks;
}
public static void setMaxExemptionTicks(long maxExemptionTicks) {
Config.maxExemptionTicks = maxExemptionTicks;
}
public static long getPortalRecheckTicks() {
return portalRecheckTicks;
}
public static void setPortalRecheckTicks(long portalRecheckTicks) {
Config.portalRecheckTicks = portalRecheckTicks;
}
public static String replaceAmpColors (String message)
@ -605,6 +622,8 @@ public class Config
fillMemoryTolerance = cfg.getInt("fill-memory-tolerance", 500);
preventBlockPlace = cfg.getBoolean("prevent-block-place");
preventMobSpawn = cfg.getBoolean("prevent-mob-spawn");
maxExemptionTicks = cfg.getLong("max-exemption-ticks", 21l);
portalRecheckTicks = cfg.getLong("portal-recheck-ticks", 4l);
StartBorderTimer();
@ -713,6 +732,8 @@ public class Config
cfg.set("fill-memory-tolerance", fillMemoryTolerance);
cfg.set("prevent-block-place", preventBlockPlace);
cfg.set("prevent-mob-spawn", preventMobSpawn);
cfg.set("max-exemption-ticks", maxExemptionTicks);
cfg.set("portal-recheck-ticks", portalRecheckTicks);
cfg.set("worlds", null);
for(Entry<String, BorderData> stringBorderDataEntry : borders.entrySet())
@ -751,4 +772,5 @@ public class Config
if (logIt)
logConfig("Configuration saved.");
}
}

View File

@ -9,7 +9,6 @@ import org.bukkit.event.player.PlayerPortalEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.Location;
public class WBListener implements Listener
{
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
@ -19,8 +18,11 @@ public class WBListener implements Listener
if (Config.KnockBack() == 0.0)
return;
if (Config.Debug())
Config.log("Teleport cause: " + event.getCause().toString());
if (event instanceof PlayerPortalEvent) { // Avoid overlapping management.
if (Config.Debug())
Config.log("Skipping teleport management event - covered by onPlayerPortal");
return;
}
Location newLoc = BorderCheckTask.checkPlayer(event.getPlayer(), event.getTo(), true, true);
if (newLoc != null)
@ -38,13 +40,20 @@ public class WBListener implements Listener
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerPortal(PlayerPortalEvent event)
{
if (Config.Debug())
Config.log("Player Portal Teleport cause: " + event.getCause().toString());
// if knockback is set to 0, or portal redirection is disabled, simply return
if (Config.KnockBack() == 0.0 || !Config.portalRedirection())
return;
Location newLoc = BorderCheckTask.checkPlayer(event.getPlayer(), event.getTo(), true, false);
if (newLoc != null)
if (newLoc != null) {
event.setTo(newLoc);
}
BorderCheckTask.timedPlayerExemption(event.getPlayer(), event.getFrom().clone(),
Config.getMaxExemptionTicks(), Config.getPortalRecheckTicks());
}
@EventHandler(priority = EventPriority.MONITOR)

View File

@ -1,7 +1,7 @@
name: WorldBorder
name: ${project.name}
author: Brettflan
description: Efficient, feature-rich plugin for limiting the size of your worlds.
version: 1.8.5
version: ${project.version}
main: com.wimbli.WorldBorder.WorldBorder
softdepend:
- dynmap