2011-04-09 17:56:51 +02:00
package com.wimbli.WorldBorder ;
2015-02-08 16:14:31 +01:00
import java.util.Collection ;
2013-07-05 22:33:57 +02:00
import java.util.Collections ;
import java.util.LinkedHashSet ;
2018-08-22 07:20:04 +02:00
import java.util.List ;
2013-07-05 22:33:57 +02:00
import java.util.Set ;
2015-02-08 16:14:31 +01:00
import com.google.common.collect.ImmutableList ;
2013-03-31 15:10:23 +02:00
import org.bukkit.Bukkit ;
import org.bukkit.entity.Entity ;
import org.bukkit.entity.LivingEntity ;
2011-04-09 17:56:51 +02:00
import org.bukkit.entity.Player ;
2014-01-27 03:51:37 +01:00
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause ;
2011-04-09 17:56:51 +02:00
import org.bukkit.Location ;
2013-03-31 15:10:23 +02:00
import org.bukkit.util.Vector ;
2011-04-09 17:56:51 +02:00
import org.bukkit.World ;
2012-11-05 00:01:51 +01:00
2011-04-09 17:56:51 +02:00
public class BorderCheckTask implements Runnable
{
2013-07-05 22:33:57 +02:00
@Override
2011-04-09 17:56:51 +02:00
public void run ( )
{
2012-11-04 23:46:46 +01:00
// if knockback is set to 0, simply return
if ( Config . KnockBack ( ) = = 0 . 0 )
return ;
2015-06-23 19:38:26 +02:00
Collection < Player > players = ImmutableList . copyOf ( Bukkit . getServer ( ) . getOnlinePlayers ( ) ) ;
2011-07-11 16:48:06 +02:00
2015-06-23 19:38:26 +02:00
for ( Player player : players )
2014-02-17 12:51:40 +01:00
{
2015-06-23 19:38:26 +02:00
checkPlayer ( player , null , false , true ) ;
2011-12-14 14:18:18 +01:00
}
}
2011-04-09 17:56:51 +02:00
2013-07-05 22:33:57 +02:00
// 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 > ( ) ) ;
2011-12-14 14:18:18 +01:00
// 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
2013-03-30 08:56:12 +01:00
public static Location checkPlayer ( Player player , Location targetLoc , boolean returnLocationOnly , boolean notify )
2011-12-14 14:18:18 +01:00
{
if ( player = = null | | ! player . isOnline ( ) ) return null ;
2011-05-02 11:14:30 +02:00
2012-03-03 14:02:03 +01:00
Location loc = ( targetLoc = = null ) ? player . getLocation ( ) . clone ( ) : targetLoc ;
2011-12-14 14:18:18 +01:00
if ( loc = = null ) return null ;
2011-04-09 17:56:51 +02:00
2011-12-14 14:18:18 +01:00
World world = loc . getWorld ( ) ;
if ( world = = null ) return null ;
BorderData border = Config . Border ( world . getName ( ) ) ;
if ( border = = null ) return null ;
2011-04-09 17:56:51 +02:00
2011-12-14 14:18:18 +01:00
if ( border . insideBorder ( loc . getX ( ) , loc . getZ ( ) , Config . ShapeRound ( ) ) )
return null ;
2011-04-09 17:56:51 +02:00
2013-07-05 22:33:57 +02:00
// if player is in bypass list (from bypass command), allow them beyond border; also ignore players currently being handled already
2014-05-28 18:13:16 +02:00
if ( Config . isPlayerBypassing ( player . getUniqueId ( ) ) | | handlingPlayers . contains ( player . getName ( ) . toLowerCase ( ) ) )
2012-10-19 07:55:08 +02:00
return null ;
2013-07-05 22:33:57 +02:00
// 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 ( ) ) ;
Location newLoc = newLocation ( player , loc , border , notify ) ;
boolean handlingVehicle = false ;
2013-03-31 15:10:23 +02:00
/ *
* 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
* /
2013-03-30 08:56:12 +01:00
if ( player . isInsideVehicle ( ) )
{
2013-03-31 15:10:23 +02:00
Entity ride = player . getVehicle ( ) ;
2013-03-30 08:56:12 +01:00
player . leaveVehicle ( ) ;
2013-03-31 15:10:23 +02:00
if ( ride ! = null )
{ // vehicles need to be offset vertically and have velocity stopped
double vertOffset = ( ride instanceof LivingEntity ) ? 0 : ride . getLocation ( ) . getY ( ) - loc . getY ( ) ;
2013-07-05 22:33:57 +02:00
Location rideLoc = newLoc . clone ( ) ;
rideLoc . setY ( newLoc . getY ( ) + vertOffset ) ;
if ( Config . Debug ( ) )
2014-02-17 13:37:18 +01:00
Config . logWarn ( " Player was riding a \" " + ride . toString ( ) + " \" . " ) ;
2016-11-29 10:13:06 +01:00
ride . setVelocity ( new Vector ( 0 , 0 , 0 ) ) ;
ride . teleport ( rideLoc , TeleportCause . PLUGIN ) ;
2013-07-07 09:51:24 +02:00
if ( Config . RemountTicks ( ) > 0 )
{
setPassengerDelayed ( ride , player , player . getName ( ) , Config . RemountTicks ( ) ) ;
handlingVehicle = true ;
}
2013-03-31 15:10:23 +02:00
}
2013-03-30 08:56:12 +01:00
}
2014-03-12 10:50:59 +01:00
// check if player has something (a pet, maybe?) riding them; only possible through odd plugins.
// it can prevent all teleportation of the player completely, so it's very much not good and needs handling
2018-08-22 07:20:04 +02:00
List < Entity > passengers = player . getPassengers ( ) ;
if ( ! passengers . isEmpty ( ) )
2014-03-12 10:50:59 +01:00
{
player . eject ( ) ;
2018-08-22 07:20:04 +02:00
for ( Entity rider : passengers )
{
rider . teleport ( newLoc , TeleportCause . PLUGIN ) ;
if ( Config . Debug ( ) )
Config . logWarn ( " Player had a passenger riding on them: " + rider . getType ( ) ) ;
}
2018-08-30 07:09:42 +02:00
player . sendMessage ( " Your passenger " + ( ( passengers . size ( ) > 1 ) ? " s have " : " has " ) + " been ejected. " ) ;
2014-03-12 10:50:59 +01:00
}
2013-07-05 22:33:57 +02:00
// give some particle and sound effects where the player was beyond the border, if "whoosh effect" is enabled
Config . showWhooshEffect ( loc ) ;
2011-07-27 23:30:07 +02:00
2013-07-05 22:33:57 +02:00
if ( ! returnLocationOnly )
2014-01-27 03:51:37 +01:00
player . teleport ( newLoc , TeleportCause . PLUGIN ) ;
2013-07-05 22:33:57 +02:00
if ( ! handlingVehicle )
handlingPlayers . remove ( player . getName ( ) . toLowerCase ( ) ) ;
2011-12-14 14:18:18 +01:00
if ( returnLocationOnly )
return newLoc ;
return null ;
2011-04-09 17:56:51 +02:00
}
2013-03-30 08:56:12 +01:00
public static Location checkPlayer ( Player player , Location targetLoc , boolean returnLocationOnly )
{
return checkPlayer ( player , targetLoc , returnLocationOnly , true ) ;
}
2011-04-09 17:56:51 +02:00
2013-03-30 08:56:12 +01:00
private static Location newLocation ( Player player , Location loc , BorderData border , boolean notify )
2011-04-09 17:56:51 +02:00
{
if ( Config . Debug ( ) )
{
2014-02-17 13:37:18 +01:00
Config . logWarn ( ( notify ? " Border crossing " : " Check was run " ) + " in \" " + loc . getWorld ( ) . getName ( ) + " \" . Border " + border . toString ( ) ) ;
Config . logWarn ( " Player position X: " + Config . coord . format ( loc . getX ( ) ) + " Y: " + Config . coord . format ( loc . getY ( ) ) + " Z: " + Config . coord . format ( loc . getZ ( ) ) ) ;
2011-04-09 17:56:51 +02:00
}
2013-03-29 23:57:52 +01:00
Location newLoc = border . correctedPosition ( loc , Config . ShapeRound ( ) , player . isFlying ( ) ) ;
2011-04-09 17:56:51 +02:00
// it's remotely possible (such as in the Nether) a suitable location isn't available, in which case...
if ( newLoc = = null )
{
if ( Config . Debug ( ) )
2014-02-17 13:37:18 +01:00
Config . logWarn ( " Target new location unviable, using spawn or killing player. " ) ;
2013-08-11 19:09:21 +02:00
if ( Config . getIfPlayerKill ( ) )
{
player . setHealth ( 0 . 0D ) ;
return null ;
}
2012-01-31 17:32:26 +01:00
newLoc = player . getWorld ( ) . getSpawnLocation ( ) ;
2011-04-09 17:56:51 +02:00
}
if ( Config . Debug ( ) )
2014-02-17 13:37:18 +01:00
Config . logWarn ( " New position in world \" " + newLoc . getWorld ( ) . getName ( ) + " \" at X: " + Config . coord . format ( newLoc . getX ( ) ) + " Y: " + Config . coord . format ( newLoc . getY ( ) ) + " Z: " + Config . coord . format ( newLoc . getZ ( ) ) ) ;
2011-04-09 17:56:51 +02:00
2013-03-30 08:56:12 +01:00
if ( notify )
2013-11-03 18:34:09 +01:00
player . sendMessage ( Config . Message ( ) ) ;
2011-04-09 17:56:51 +02:00
return newLoc ;
}
2013-03-31 15:10:23 +02:00
2013-07-05 22:33:57 +02:00
private static void setPassengerDelayed ( final Entity vehicle , final Player player , final String playerName , long delay )
2013-03-31 15:10:23 +02:00
{
Bukkit . getServer ( ) . getScheduler ( ) . scheduleSyncDelayedTask ( WorldBorder . plugin , new Runnable ( )
{
@Override
public void run ( )
{
2013-07-05 22:33:57 +02:00
handlingPlayers . remove ( playerName . toLowerCase ( ) ) ;
if ( vehicle = = null | | player = = null )
return ;
2018-08-22 07:20:04 +02:00
vehicle . addPassenger ( player ) ;
2013-03-31 15:10:23 +02:00
}
} , delay ) ;
}
2011-04-09 17:56:51 +02:00
}