Update to allow players to stay in/on vehicle when knocked back from border. From further testing I found that the key was to add a delay after ejecting the player and teleporting the vehicle to give the client time to process the vehicle being teleported. After such a delay it is then safe to set them as passenger of it again. With the delay the client mostly treats the player as if they're not in the vehicle but instead walking around freely.

A delay of at least 2 ticks worked on my local VM test server, but for potentially loaded live servers on the internet with players who potentially have slow/poor connections... a longer delay is probably needed to prevent client glitches. I've set the delay to 10 ticks (half a second) which still looks reasonable enough in-game, I think.

Further, I found that out of the native vehicles boats in particular quite simply cannot be teleported without the client ending up confused about where it actually is for whatever reason. I came up with a workaround for this by destroying the boat and spawning a new one in the correct position.

Bah, gotta love workarounds within workarounds.

As a reminder, this all primarily came about due to the ability to teleport entities which have a passenger being removed by the Bukkit team to apparently combat a nasty related bug.
This commit is contained in:
Brettflan 2013-03-31 08:10:23 -05:00
parent 686072b87c
commit ef001b15bf
3 changed files with 52 additions and 43 deletions

View File

@ -1,35 +1,27 @@
package com.wimbli.WorldBorder;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Effect;
//import org.bukkit.entity.Entity;
//import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Boat;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.Location;
import org.bukkit.Server;
//import org.bukkit.util.Vector;
import org.bukkit.util.Vector;
import org.bukkit.World;
public class BorderCheckTask implements Runnable
{
private transient Server server = null;
public BorderCheckTask(Server theServer)
{
this.server = theServer;
}
public void run()
{
if (server == null)
return;
// if knockback is set to 0, simply return
if (Config.KnockBack() == 0.0)
return;
Player[] players = server.getOnlinePlayers();
Player[] players = Bukkit.getServer().getOnlinePlayers();
for (int i = 0; i < players.length; i++){
checkPlayer(players[i], null, false, true);
@ -56,12 +48,34 @@ public class BorderCheckTask implements Runnable
if (Config.isPlayerBypassing(player.getName()))
return null;
// since we need to forcibly eject players who are inside vehicles, that fires a teleport event (go figure) and
// so would double trigger, so we need to handle it here to prevent sending two messages and two log entries etc.
// see further below for why players in vehicles now have to be ejected
/*
* since we need to forcibly eject players who are inside vehicles, that fires a teleport event (go figure) and
* so would effectively double trigger for us, so we need to handle it here to prevent sending two messages and
* two log entries etc.
* after players are ejected we can wait a few ticks (long enough for their client to receive new entity location)
* and then set them as passenger of the vehicle again
*/
if (player.isInsideVehicle())
{
Location newLoc = newLocation(player, loc, border, false);
Entity ride = player.getVehicle();
player.leaveVehicle();
if (ride != null)
{ // vehicles need to be offset vertically and have velocity stopped
double vertOffset = (ride instanceof LivingEntity) ? 0 : ride.getLocation().getY() - loc.getY();
newLoc.setY(newLoc.getY() + vertOffset);
if (ride instanceof Boat)
{ // boats currently glitch on client when teleported, so crappy workaround is to remove it and spawn a new one
ride.remove();
ride = world.spawnEntity(newLoc, EntityType.BOAT);
}
else
{
ride.setVelocity(new Vector(0, 0, 0));
ride.teleport(newLoc);
}
setPassengerDelayed(ride, player, 10);
}
return null;
}
@ -80,31 +94,7 @@ public class BorderCheckTask implements Runnable
if (returnLocationOnly)
return newLoc;
/* // Bukkit team have removed the ability to teleport any entity which has a passenger, causing this to fail horribly now, so...
* // unfortunately until they become sane again we must always eject player from whatever they're riding, which is done above
if (!player.isInsideVehicle())
player.teleport(newLoc);
else
{
Entity ride = player.getVehicle();
if (ride != null)
{ // vehicles need to be offset vertically and have velocity stopped
double vertOffset = (ride instanceof LivingEntity) ? 0 : ride.getLocation().getY() - loc.getY();
newLoc.setY(newLoc.getY() + vertOffset);
ride.setVelocity(new Vector(0, 0, 0));
ride.teleport(newLoc);
}
else
{ // if player.getVehicle() returns null (when riding a pig on older Bukkit releases, for instance), player has to be ejected
player.leaveVehicle();
player.teleport(newLoc);
}
player.leaveVehicle();
player.teleport(newLoc);
}
*/ // and in the meantime, since vehicle ejection is handled in new spot above, we can safely relocate the player at this point
player.teleport(newLoc);
return null;
}
public static Location checkPlayer(Player player, Location targetLoc, boolean returnLocationOnly)
@ -138,4 +128,16 @@ public class BorderCheckTask implements Runnable
return newLoc;
}
private static void setPassengerDelayed(final Entity vehicle, final Player player, long delay)
{
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(WorldBorder.plugin, new Runnable()
{
@Override
public void run()
{
vehicle.setPassenger(player);
}
}, delay);
}
}

View File

@ -286,7 +286,7 @@ public class Config
{
StopBorderTimer();
borderTask = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, new BorderCheckTask(plugin.getServer()), timerTicks, timerTicks);
borderTask = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, new BorderCheckTask(), timerTicks, timerTicks);
if (borderTask == -1)
LogWarn("Failed to start timed border-checking task! This will prevent the plugin from working. Try restarting Bukkit.");

View File

@ -6,6 +6,13 @@ import org.bukkit.plugin.java.JavaPlugin;
public class WorldBorder extends JavaPlugin
{
public static WorldBorder plugin;
public WorldBorder()
{
plugin = this;
}
@Override
public void onEnable()
{