Merge branch 'master' into dev

# Conflicts:
#	README.md
#	pom.xml
#	src/main/resources/plugin.yml
This commit is contained in:
Kristian Knarvik 2022-02-15 18:16:53 +01:00
commit e3189e9ab2
20 changed files with 558 additions and 408 deletions

View File

@ -324,6 +324,7 @@ gates:
handleCreatureTransportation - Whether or not to handle players that transport creatures by sending vehicles (minecarts, boats) through gates. handleCreatureTransportation - Whether or not to handle players that transport creatures by sending vehicles (minecarts, boats) through gates.
handleNonPlayerVehicles - Whether or not to handle vehicles with a passenger which is not a player going through gates (pigs, horses, villagers, creepers, etc.). handleCreatureTransportation must be enabled. handleNonPlayerVehicles - Whether or not to handle vehicles with a passenger which is not a player going through gates (pigs, horses, villagers, creepers, etc.). handleCreatureTransportation must be enabled.
handleLeashedCreatures - Whether or not to handle creatures leashed by a player going through gates. Set to false to disallow leashed creatures going through gates. handleLeashedCreatures - Whether or not to handle creatures leashed by a player going through gates. Set to false to disallow leashed creatures going through gates.
enableCraftBookRemoveOnEjectFix - Whether to enable a fix that causes loss of NBT data, but allows vehicle teleportation to work when CraftBook's remove minecart/boat on eject setting is enabled
economy: economy:
useEconomy - Whether or not to enable Economy using Vault (must have the Vault plugin) useEconomy - Whether or not to enable Economy using Vault (must have the Vault plugin)
createCost - The cost to create a stargate createCost - The cost to create a stargate
@ -336,6 +337,8 @@ economy:
debugging: debugging:
debug - Whether to show massive debug output debug - Whether to show massive debug output
permissionDebug - Whether to show massive permission debug output permissionDebug - Whether to show massive permission debug output
advanced:
waitForPlayerAfterTeleportDelay - The amount of ticks to wait before adding a player as passenger of a vehicle. On slow servers, a value of 6 is required to avoid client glitches after teleporting on a vehicle.
``` ```
# Message Customization # Message Customization
@ -397,6 +400,15 @@ portalInfoServer=Server: %server%
# Changes # Changes
#### \[Version 0.9.3.4] EpicKnarvik97 fork
- Includes passengers of passengers when teleporting entities
- Fixes a bug which caused Stargate to use more CPU for no reason
- Teleports boats/minecarts like other vehicles unless *enableCraftBookRemoveOnEjectFix* is enabled
- Adds the *waitForPlayerAfterTeleportDelay* config option which allows changing the delay between vehicle teleportation
and the player being teleported to the vehicle
- Makes boats keep their wood type even when re-created
#### \[Version 0.9.3.6] EpicKnarvik97 fork #### \[Version 0.9.3.6] EpicKnarvik97 fork
- Adds the simplified Chinese language file provided by spigot user YKDZ - Adds the simplified Chinese language file provided by spigot user YKDZ

View File

@ -44,25 +44,25 @@
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit-jupiter-api</artifactId>
<version>5.8.0-M1</version> <version>5.8.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.seeseemelk</groupId> <groupId>com.github.seeseemelk</groupId>
<artifactId>MockBukkit-v1.18</artifactId> <artifactId>MockBukkit-v1.18</artifactId>
<version>1.14.0</version> <version>1.15.5</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jetbrains</groupId> <groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId> <artifactId>annotations</artifactId>
<version>19.0.0</version> <version>22.0.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.13.1</version> <version>4.13.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -50,6 +50,9 @@ public enum ConfigOption {
*/ */
HIGHLIGHT_SIGN_COLOR("gates.cosmetic.highlightSignColor", "The text color used for highlighting stargate signs", "WHITE"), HIGHLIGHT_SIGN_COLOR("gates.cosmetic.highlightSignColor", "The text color used for highlighting stargate signs", "WHITE"),
/**
* The colors to use for each type of sign
*/
PER_SIGN_COLORS("gates.cosmetic.perSignColors", "The per-sign color specification", new String[]{ PER_SIGN_COLORS("gates.cosmetic.perSignColors", "The per-sign color specification", new String[]{
"'ACACIA:default,default'", "'BIRCH:default,default'", "'CRIMSON:inverted,inverted'", "'DARK_OAK:inverted,inverted'", "'ACACIA:default,default'", "'BIRCH:default,default'", "'CRIMSON:inverted,inverted'", "'DARK_OAK:inverted,inverted'",
"'JUNGLE:default,default'", "'OAK:default,default'", "'SPRUCE:inverted,inverted'", "'WARPED:inverted,inverted'"}), "'JUNGLE:default,default'", "'OAK:default,default'", "'SPRUCE:inverted,inverted'", "'WARPED:inverted,inverted'"}),
@ -102,6 +105,20 @@ public enum ConfigOption {
HANDLE_LEASHED_CREATURES("gates.functionality.handleLeashedCreatures", HANDLE_LEASHED_CREATURES("gates.functionality.handleLeashedCreatures",
"Whether to enable players to teleport a creature on a leash", true), "Whether to enable players to teleport a creature on a leash", true),
/**
* Whether to enable a fix that makes teleportation of minecarts/boats work even with craftbook's vehicle removal
*/
ENABLE_CRAFT_BOOK_REMOVE_ON_EJECT_FIX("gates.functionality.enableCraftBookRemoveOnEjectFix",
"Whether to enable a fix that causes loss of NBT data, but allows vehicle teleportation to work " +
"when CraftBook's remove minecart/boat on eject setting is enabled", false),
/**
* The delay between teleporting a vehicle and adding the player as passenger
*/
WAIT_FOR_PLAYER_AFTER_TELEPORT_DELAY("advanced.waitForPlayerAfterTeleportDelay",
"The amount of ticks to wait before adding a player as passenger of a vehicle. On slow servers, " +
"a value of 6 is required to avoid client glitches after teleporting on a vehicle.", 1),
/** /**
* Whether to enable economy support for taking payment from players creating/destroying/using stargates * Whether to enable economy support for taking payment from players creating/destroying/using stargates
*/ */

View File

@ -125,6 +125,26 @@ public final class StargateGateConfig {
return (boolean) configOptions.get(ConfigOption.HANDLE_LEASHED_CREATURES); return (boolean) configOptions.get(ConfigOption.HANDLE_LEASHED_CREATURES);
} }
/**
* Gets whether the CraftBook vehicle removal fix is enabled
*
* <p>If enabled, minecarts and boats should be re-created instead of teleported.</p>
*
* @return <p>True if the CraftBook vehicle removal fix is enabled</p>
*/
public boolean enableCraftBookRemoveOnEjectFix() {
return (boolean) configOptions.get(ConfigOption.ENABLE_CRAFT_BOOK_REMOVE_ON_EJECT_FIX);
}
/**
* Gets the delay to use before adding a player as passenger of a teleported vehicle
*
* @return <p>The delay to use before adding a player as passenger of a teleported vehicle</p>
*/
public int waitForPlayerAfterTeleportDelay() {
return (int) configOptions.get(ConfigOption.WAIT_FOR_PLAYER_AFTER_TELEPORT_DELAY);
}
/** /**
* Gets whether the list of destinations within a network should be sorted * Gets whether the list of destinations within a network should be sorted
* *
@ -262,8 +282,8 @@ public final class StargateGateConfig {
if (colors[colorIndex].equalsIgnoreCase("inverted")) { if (colors[colorIndex].equalsIgnoreCase("inverted")) {
//Convert from ChatColor to awt.Color to Bukkit.Color then invert and convert to ChatColor //Convert from ChatColor to awt.Color to Bukkit.Color then invert and convert to ChatColor
java.awt.Color color = defaultColors[colorIndex].getColor(); java.awt.Color color = defaultColors[colorIndex].getColor();
parsedColor = ColorHelper.fromColor(ColorHelper.invert(Color.fromRGB(color.getRed(), parsedColor = ColorHelper.fromColor(ColorHelper.invert(Color.fromRGB(color.getRed(), color.getGreen(),
color.getGreen(), color.getBlue()))); color.getBlue())));
} else { } else {
try { try {
parsedColor = ChatColor.of(colors[colorIndex]); parsedColor = ChatColor.of(colors[colorIndex]);

View File

@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
* <p>This event can be used to overwrite the location the entity is teleported to.</p> * <p>This event can be used to overwrite the location the entity is teleported to.</p>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class StargateEntityPortalEvent extends StargateEvent { public class StargateEntityPortalEvent extends StargateEvent implements StargateTeleportEvent {
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
final Entity travellingEntity; final Entity travellingEntity;
@ -58,6 +58,7 @@ public class StargateEntityPortalEvent extends StargateEvent {
* *
* @return <p>Location of the exit point</p> * @return <p>Location of the exit point</p>
*/ */
@Override
public Location getExit() { public Location getExit() {
return exit; return exit;
} }

View File

@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
* <p>This event can be used to overwrite the location the player is teleported to.</p> * <p>This event can be used to overwrite the location the player is teleported to.</p>
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class StargatePlayerPortalEvent extends StargatePlayerEvent { public class StargatePlayerPortalEvent extends StargatePlayerEvent implements StargateTeleportEvent {
private static final HandlerList handlers = new HandlerList(); private static final HandlerList handlers = new HandlerList();
private final Portal destination; private final Portal destination;
@ -47,6 +47,7 @@ public class StargatePlayerPortalEvent extends StargatePlayerEvent {
* *
* @return <p>Location of the exit point</p> * @return <p>Location of the exit point</p>
*/ */
@Override
public Location getExit() { public Location getExit() {
return exit; return exit;
} }

View File

@ -0,0 +1,18 @@
package net.knarcraft.stargate.event;
import org.bukkit.Location;
import org.bukkit.event.Cancellable;
/**
* A generic teleportation event
*/
public interface StargateTeleportEvent extends Cancellable {
/**
* Return the location of the players exit point
*
* @return <p>Location of the exit point</p>
*/
Location getExit();
}

View File

@ -7,11 +7,11 @@ import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalActivator; import net.knarcraft.stargate.portal.PortalActivator;
import net.knarcraft.stargate.portal.PortalHandler; import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter; import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter;
import net.knarcraft.stargate.portal.teleporter.Teleporter;
import net.knarcraft.stargate.portal.teleporter.VehicleTeleporter; import net.knarcraft.stargate.portal.teleporter.VehicleTeleporter;
import net.knarcraft.stargate.utility.BungeeHelper; import net.knarcraft.stargate.utility.BungeeHelper;
import net.knarcraft.stargate.utility.MaterialHelper; import net.knarcraft.stargate.utility.MaterialHelper;
import net.knarcraft.stargate.utility.PermissionHelper; import net.knarcraft.stargate.utility.PermissionHelper;
import net.knarcraft.stargate.utility.TeleportHelper;
import net.knarcraft.stargate.utility.UUIDMigrationHelper; import net.knarcraft.stargate.utility.UUIDMigrationHelper;
import net.knarcraft.stargate.utility.UpdateChecker; import net.knarcraft.stargate.utility.UpdateChecker;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
@ -131,10 +131,10 @@ public class PlayerEventListener implements Listener {
horse.setOwner(player); horse.setOwner(player);
} }
//Teleport the player's vehicle //Teleport the player's vehicle
new VehicleTeleporter(destination, (Vehicle) playerVehicle).teleport(entrancePortal); new VehicleTeleporter(destination, (Vehicle) playerVehicle).teleportEntity(entrancePortal);
} else { } else {
//Just teleport the player like normal //Just teleport the player like normal
new PlayerTeleporter(destination, player).teleport(entrancePortal, event); new PlayerTeleporter(destination, player).teleportPlayer(entrancePortal, event);
} }
if (!entrancePortal.getOptions().isSilent()) { if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg")); Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
@ -185,7 +185,7 @@ public class PlayerEventListener implements Listener {
} }
//Make sure to check if the player has any leashed creatures, even though leashed teleportation is disabled //Make sure to check if the player has any leashed creatures, even though leashed teleportation is disabled
return Teleporter.noLeashedCreaturesPreventTeleportation(player); return TeleportHelper.noLeashedCreaturesPreventTeleportation(player);
} }
/** /**

View File

@ -95,8 +95,7 @@ public class PortalEventListener implements Listener {
Portal exitPortal = teleportation.getExit(); Portal exitPortal = teleportation.getExit();
//Overwrite respawn location to respawn in front of the portal //Overwrite respawn location to respawn in front of the portal
event.setRespawnLocation(new PlayerTeleporter(exitPortal, respawningPlayer).getExit(respawningPlayer, event.setRespawnLocation(new PlayerTeleporter(exitPortal, respawningPlayer).getExit());
respawningPlayer.getLocation()));
//Properly close the portal to prevent it from staying in a locked state until it times out //Properly close the portal to prevent it from staying in a locked state until it times out
exitPortal.getPortalOpener().closePortal(false); exitPortal.getPortalOpener().closePortal(false);
} }

View File

@ -3,11 +3,10 @@ package net.knarcraft.stargate.listener;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.PortalHandler; import net.knarcraft.stargate.portal.PortalHandler;
import net.knarcraft.stargate.portal.teleporter.Teleporter;
import net.knarcraft.stargate.portal.teleporter.VehicleTeleporter; import net.knarcraft.stargate.portal.teleporter.VehicleTeleporter;
import net.knarcraft.stargate.utility.EconomyHelper; import net.knarcraft.stargate.utility.EconomyHelper;
import net.knarcraft.stargate.utility.EntityHelper; import net.knarcraft.stargate.utility.EntityHelper;
import net.knarcraft.stargate.utility.PermissionHelper; import net.knarcraft.stargate.utility.TeleportHelper;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle; import org.bukkit.entity.Vehicle;
@ -62,11 +61,11 @@ public class VehicleEventListener implements Listener {
private static void teleportVehicle(List<Entity> passengers, Portal entrancePortal, Vehicle vehicle) { private static void teleportVehicle(List<Entity> passengers, Portal entrancePortal, Vehicle vehicle) {
String route = "VehicleEventListener::teleportVehicle"; String route = "VehicleEventListener::teleportVehicle";
if (!passengers.isEmpty() && passengers.get(0) instanceof Player) { if (!passengers.isEmpty() && TeleportHelper.containsPlayer(passengers)) {
Stargate.debug(route, "Found passenger vehicle"); Stargate.debug(route, "Found passenger vehicle");
teleportPlayerAndVehicle(entrancePortal, vehicle, passengers); teleportPlayerAndVehicle(entrancePortal, vehicle);
} else { } else {
Stargate.debug(route, "Found empty vehicle"); Stargate.debug(route, "Found vehicle without players");
Portal destinationPortal = entrancePortal.getPortalActivator().getDestination(); Portal destinationPortal = entrancePortal.getPortalActivator().getDestination();
if (destinationPortal == null) { if (destinationPortal == null) {
Stargate.debug(route, "Unable to find portal destination"); Stargate.debug(route, "Unable to find portal destination");
@ -74,7 +73,7 @@ public class VehicleEventListener implements Listener {
} }
Stargate.debug("vehicleTeleport", destinationPortal.getWorld() + " " + Stargate.debug("vehicleTeleport", destinationPortal.getWorld() + " " +
destinationPortal.getSignLocation()); destinationPortal.getSignLocation());
new VehicleTeleporter(destinationPortal, vehicle).teleport(entrancePortal); new VehicleTeleporter(destinationPortal, vehicle).teleportEntity(entrancePortal);
} }
} }
@ -83,98 +82,66 @@ public class VehicleEventListener implements Listener {
* *
* @param entrancePortal <p>The portal the minecart entered</p> * @param entrancePortal <p>The portal the minecart entered</p>
* @param vehicle <p>The vehicle to teleport</p> * @param vehicle <p>The vehicle to teleport</p>
* @param passengers <p>Any entities sitting in the minecart</p>
*/ */
private static void teleportPlayerAndVehicle(Portal entrancePortal, Vehicle vehicle, List<Entity> passengers) { private static void teleportPlayerAndVehicle(Portal entrancePortal, Vehicle vehicle) {
Player player = (Player) passengers.get(0); Entity rootEntity = vehicle;
//On the assumption that a non-player cannot sit in the driver's seat and since some portals can only be open while (rootEntity.getVehicle() != null) {
// to one player at a time, we only need to check if the portal is open to the driver. rootEntity = rootEntity.getVehicle();
if (!entrancePortal.getPortalOpener().isOpenFor(player)) { }
if (!entrancePortal.getOptions().isSilent()) { List<Player> players = TeleportHelper.getPlayers(rootEntity.getPassengers());
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); Portal destinationPortal = null;
for (Player player : players) {
//The entrance portal must be open for one player for the teleportation to happen
if (!entrancePortal.getPortalOpener().isOpenFor(player)) {
continue;
} }
return;
}
//If no destination exists, the teleportation cannot happen //Check if any of the players has selected the destination
Portal destinationPortal = entrancePortal.getPortalActivator().getDestination(player); Portal possibleDestinationPortal = entrancePortal.getPortalActivator().getDestination(player);
if (destinationPortal == null) { if (possibleDestinationPortal != null) {
return; destinationPortal = possibleDestinationPortal;
}
//Make sure all player passengers are allowed to, and can afford to, enter the portal
for (Entity entity : passengers) {
if (entity instanceof Player && !playerCanTeleport((Player) entity, entrancePortal, destinationPortal)) {
return;
} }
} }
//To prevent the case where the first passenger pays and then the second passenger is denied, this has to be //Cancel the teleport if no players activated the portal, or if any players are denied access
// run after it has been confirmed that all passengers are able to pay boolean cancelTeleport = false;
int cost = EconomyHelper.getUseCost(player, entrancePortal, destinationPortal); for (Player player : players) {
if (cost > 0) { if (destinationPortal == null) {
if (!takePlayerPayment(passengers, entrancePortal, cost)) { cancelTeleport = true;
return; if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("invalidMsg"));
}
} else if (!TeleportHelper.playerCanTeleport(player, entrancePortal, destinationPortal)) {
cancelTeleport = true;
}
}
if (cancelTeleport) {
return;
}
//Take payment from all players
for (Player player : players) {
//To prevent the case where the first passenger pays and then the second passenger is denied, this has to be
// run after it has been confirmed that all passengers are able to pay
int cost = EconomyHelper.getUseCost(player, entrancePortal, destinationPortal);
if (cost > 0) {
if (EconomyHelper.cannotPayTeleportFee(entrancePortal, player, cost)) {
return;
}
} }
} }
//Teleport the vehicle and inform the user if the vehicle was teleported //Teleport the vehicle and inform the user if the vehicle was teleported
boolean teleported = new VehicleTeleporter(destinationPortal, vehicle).teleport(entrancePortal); boolean teleported = new VehicleTeleporter(destinationPortal, vehicle).teleportEntity(entrancePortal);
if (teleported) { if (teleported) {
if (!entrancePortal.getOptions().isSilent()) { if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg")); for (Player player : players) {
Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
}
} }
entrancePortal.getPortalOpener().closePortal(false); entrancePortal.getPortalOpener().closePortal(false);
} }
} }
/**
* Takes payment from all player passengers
*
* @param passengers <p>All passengers in the teleporting vehicle</p>
* @param entrancePortal <p>The portal the vehicle is entering from</p>
* @param cost <p>The cost each player has to pay</p>
* @return <p>True if all player passengers paid successfully</p>
*/
private static boolean takePlayerPayment(List<Entity> passengers, Portal entrancePortal, int cost) {
for (Entity entity : passengers) {
//If the passenger is a player, make it pay
if (entity instanceof Player && EconomyHelper.cannotPayTeleportFee(entrancePortal, (Player) entity, cost)) {
return false;
}
}
return true;
}
/**
* Checks whether the given player is allowed to and can afford to teleport
*
* @param player <p>The player trying to teleport</p>
* @param entrancePortal <p>The portal the player is entering</p>
* @param destinationPortal <p>The portal the player is to exit from</p>
* @return <p>True if the player is allowed to teleport and is able to pay necessary fees</p>
*/
private static boolean playerCanTeleport(Player player, Portal entrancePortal, Portal destinationPortal) {
//Make sure the user can access the portal
if (PermissionHelper.cannotAccessPortal(player, entrancePortal, destinationPortal)) {
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
}
entrancePortal.getPortalOpener().closePortal(false);
return false;
}
//Check if the player is able to afford the teleport fee
int cost = EconomyHelper.getUseCost(player, entrancePortal, destinationPortal);
boolean canAffordFee = cost <= 0 || Stargate.getEconomyConfig().canAffordFee(player, cost);
if (!canAffordFee) {
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("ecoInFunds"));
}
return false;
}
return Teleporter.noLeashedCreaturesPreventTeleportation(player);
}
} }

View File

@ -1,9 +1,7 @@
package net.knarcraft.stargate.portal.teleporter; package net.knarcraft.stargate.portal.teleporter;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.event.StargateEntityPortalEvent; import net.knarcraft.stargate.event.StargateEntityPortalEvent;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
/** /**
@ -16,10 +14,10 @@ public class EntityTeleporter extends Teleporter {
/** /**
* Instantiates a new portal teleporter * Instantiates a new portal teleporter
* *
* @param portal <p>The portal which is the target of the teleportation</p> * @param targetPortal <p>The portal which is the target of the teleportation</p>
*/ */
public EntityTeleporter(Portal portal, Entity teleportingEntity) { public EntityTeleporter(Portal targetPortal, Entity teleportingEntity) {
super(portal); super(targetPortal, teleportingEntity);
this.teleportingEntity = teleportingEntity; this.teleportingEntity = teleportingEntity;
} }
@ -29,44 +27,8 @@ public class EntityTeleporter extends Teleporter {
* @param origin <p>The portal the entity is teleporting from</p> * @param origin <p>The portal the entity is teleporting from</p>
* @return <p>True if the entity was teleported. False otherwise</p> * @return <p>True if the entity was teleported. False otherwise</p>
*/ */
public boolean teleport(Portal origin) { public boolean teleportEntity(Portal origin) {
Location traveller = teleportingEntity.getLocation(); return teleport(origin, new StargateEntityPortalEvent(teleportingEntity, origin, portal, exit));
Location exit = getExit(teleportingEntity, traveller);
//Rotate the entity to face out from the portal
adjustRotation(exit);
//Call the StargateEntityPortalEvent to allow plugins to change destination
if (!origin.equals(portal)) {
exit = triggerEntityPortalEvent(origin, exit);
if (exit == null) {
return false;
}
}
//Load chunks to make sure not to teleport to the void
loadChunks();
teleportingEntity.teleport(exit);
return true;
} }
/**
* Triggers the entity portal event to allow plugins to change the exit location
*
* @param origin <p>The origin portal teleported from</p>
* @param exit <p>The exit location to teleport the entity to</p>
* @return <p>The location the entity should be teleported to, or null if the event was cancelled</p>
*/
protected Location triggerEntityPortalEvent(Portal origin, Location exit) {
StargateEntityPortalEvent stargateEntityPortalEvent = new StargateEntityPortalEvent(teleportingEntity, origin,
portal, exit);
Stargate.getInstance().getServer().getPluginManager().callEvent(stargateEntityPortalEvent);
//Teleport is cancelled. Teleport the entity back to where it came from just for sanity's sake
if (stargateEntityPortalEvent.isCancelled()) {
new EntityTeleporter(origin, teleportingEntity).teleport(origin);
return null;
}
return stargateEntityPortalEvent.getExit();
}
} }

View File

@ -4,12 +4,15 @@ import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.event.StargatePlayerPortalEvent; import net.knarcraft.stargate.event.StargatePlayerPortalEvent;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.utility.DirectionHelper; import net.knarcraft.stargate.utility.DirectionHelper;
import net.knarcraft.stargate.utility.TeleportHelper;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import java.util.List;
/** /**
* The portal teleporter takes care of the actual portal teleportation for any players * The portal teleporter takes care of the actual portal teleportation for any players
*/ */
@ -20,11 +23,11 @@ public class PlayerTeleporter extends Teleporter {
/** /**
* Instantiates a new player teleporter * Instantiates a new player teleporter
* *
* @param portal <p>The portal which is the target of the teleportation</p> * @param targetPortal <p>The portal which is the target of the teleportation</p>
* @param player <p>The teleporting player</p> * @param player <p>The teleporting player</p>
*/ */
public PlayerTeleporter(Portal portal, Player player) { public PlayerTeleporter(Portal targetPortal, Player player) {
super(portal); super(targetPortal, player);
this.player = player; this.player = player;
} }
@ -34,17 +37,13 @@ public class PlayerTeleporter extends Teleporter {
* @param origin <p>The portal the player teleports from</p> * @param origin <p>The portal the player teleports from</p>
* @param event <p>The player move event triggering the event</p> * @param event <p>The player move event triggering the event</p>
*/ */
public void teleport(Portal origin, PlayerMoveEvent event) { public void teleportPlayer(Portal origin, PlayerMoveEvent event) {
double velocity = player.getVelocity().length(); double velocity = player.getVelocity().length();
Location traveller = player.getLocation(); List<Entity> passengers = player.getPassengers();
Location exit = getExit(player, traveller);
//Rotate the player to face out from the portal
adjustRotation(exit);
//Call the StargatePlayerPortalEvent to allow plugins to change destination //Call the StargatePlayerPortalEvent to allow plugins to change destination
if (!origin.equals(portal)) { if (!origin.equals(portal)) {
exit = triggerPlayerPortalEvent(origin, exit, event); exit = triggerPortalEvent(origin, new StargatePlayerPortalEvent(player, origin, portal, exit));
if (exit == null) { if (exit == null) {
return; return;
} }
@ -54,7 +53,11 @@ public class PlayerTeleporter extends Teleporter {
loadChunks(); loadChunks();
//Teleport any creatures leashed by the player in a 15-block range //Teleport any creatures leashed by the player in a 15-block range
teleportLeashedCreatures(player, origin); TeleportHelper.teleportLeashedCreatures(player, origin, portal);
if (player.eject()) {
TeleportHelper.handleEntityPassengers(passengers, player, origin, portal, exit.getDirection());
}
//If no event is passed in, assume it's a teleport, and act as such //If no event is passed in, assume it's a teleport, and act as such
if (event == null) { if (event == null) {
@ -72,23 +75,4 @@ public class PlayerTeleporter extends Teleporter {
}, 1); }, 1);
} }
/**
* Triggers the player portal event to allow plugins to change the exit location
*
* @param origin <p>The origin portal teleported from</p>
* @param exit <p>The exit location to teleport the player to</p>
* @param event <p>The player move event which triggered the teleportation</p>
* @return <p>The location the player should be teleported to, or null if the event was cancelled</p>
*/
private Location triggerPlayerPortalEvent(Portal origin, Location exit, PlayerMoveEvent event) {
StargatePlayerPortalEvent stargatePlayerPortalEvent = new StargatePlayerPortalEvent(player, origin, portal, exit);
Stargate.getInstance().getServer().getPluginManager().callEvent(stargatePlayerPortalEvent);
//Teleport is cancelled. Teleport the player back to where it came from
if (stargatePlayerPortalEvent.isCancelled()) {
new PlayerTeleporter(origin, player).teleport(origin, event);
return null;
}
return stargatePlayerPortalEvent.getExit();
}
} }

View File

@ -4,9 +4,11 @@ import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.container.BlockLocation;
import net.knarcraft.stargate.container.ChunkUnloadRequest; import net.knarcraft.stargate.container.ChunkUnloadRequest;
import net.knarcraft.stargate.container.RelativeBlockVector; import net.knarcraft.stargate.container.RelativeBlockVector;
import net.knarcraft.stargate.event.StargateTeleportEvent;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.utility.DirectionHelper; import net.knarcraft.stargate.utility.DirectionHelper;
import net.knarcraft.stargate.utility.EntityHelper; import net.knarcraft.stargate.utility.EntityHelper;
import net.knarcraft.stargate.utility.TeleportHelper;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
@ -14,9 +16,8 @@ import org.bukkit.block.data.Bisected;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Slab; import org.bukkit.block.data.type.Slab;
import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.AbstractHorse;
import org.bukkit.entity.Creature;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.event.Event;
import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitScheduler;
import java.util.ArrayList; import java.util.ArrayList;
@ -31,28 +32,95 @@ public abstract class Teleporter {
* The portal the entity is teleporting to * The portal the entity is teleporting to
*/ */
protected final Portal portal; protected final Portal portal;
/** /**
* The scheduler to use for delaying tasks * The scheduler to use for delaying tasks
*/ */
protected final BukkitScheduler scheduler; protected final BukkitScheduler scheduler;
/**
* The exit location any entities will be teleported to
*/
protected Location exit;
/**
* The entity being teleported by this teleporter
*/
protected final Entity teleportedEntity;
/** /**
* Instantiates a new portal teleporter * Instantiates a new portal teleporter
* *
* @param portal <p>The portal which is the target of the teleportation</p> * @param portal <p>The portal which is the target of the teleportation</p>
* @param teleportedEntity <p>The entity teleported by this teleporter</p>
*/ */
public Teleporter(Portal portal) { public Teleporter(Portal portal, Entity teleportedEntity) {
this.portal = portal; this.portal = portal;
this.scheduler = Stargate.getInstance().getServer().getScheduler(); this.scheduler = Stargate.getInstance().getServer().getScheduler();
this.teleportedEntity = teleportedEntity;
this.exit = getExit(teleportedEntity);
} }
/**
* Teleports an entity
*
* @param origin <p>The portal the entity teleported from</p>
* @param stargateTeleportEvent <p>The event to call to make sure the teleportation is valid</p>
* @return <p>True if the teleportation was successfully performed</p>
*/
public boolean teleport(Portal origin, StargateTeleportEvent stargateTeleportEvent) {
List<Entity> passengers = teleportedEntity.getPassengers();
//Call the StargateEntityPortalEvent to allow plugins to change destination
if (!origin.equals(portal)) {
exit = triggerPortalEvent(origin, stargateTeleportEvent);
if (exit == null) {
return false;
}
}
//Load chunks to make sure not to teleport to the void
loadChunks();
if (teleportedEntity.eject()) {
TeleportHelper.handleEntityPassengers(passengers, teleportedEntity, origin, portal, exit.getDirection());
}
teleportedEntity.teleport(exit);
return true;
}
/**
* Gets the exit location of this teleporter
*
* @return <p>The exit location of this teleporter</p>
*/
public Location getExit() {
return exit.clone();
}
/**
* Triggers the entity portal event to allow plugins to change the exit location
*
* @param origin <p>The origin portal teleported from</p>
* @param stargateTeleportEvent <p>The exit location to teleport the entity to</p>
* @return <p>The location the entity should be teleported to, or null if the event was cancelled</p>
*/
protected Location triggerPortalEvent(Portal origin, StargateTeleportEvent stargateTeleportEvent) {
Stargate.getInstance().getServer().getPluginManager().callEvent((Event) stargateTeleportEvent);
//Teleport is cancelled. Teleport the entity back to where it came from just for sanity's sake
if (stargateTeleportEvent.isCancelled()) {
new EntityTeleporter(origin, teleportedEntity).teleportEntity(origin);
return null;
}
return stargateTeleportEvent.getExit();
}
/** /**
* Adjusts the rotation of the exit to make the teleporting entity face directly out from the portal * Adjusts the rotation of the exit to make the teleporting entity face directly out from the portal
* *
* @param exit <p>The location the entity will exit from</p> * @param exit <p>The location the entity will exit from</p>
*/ */
protected void adjustRotation(Location exit) { protected void adjustExitLocationRotation(Location exit) {
int adjust = 0; int adjust = 0;
if (portal.getOptions().isBackwards()) { if (portal.getOptions().isBackwards()) {
adjust = 180; adjust = 180;
@ -63,39 +131,14 @@ public abstract class Teleporter {
} }
/** /**
* Gets the exit location for a given entity and current location * Loads the chunks outside the portal's entrance
*
* @param entity <p>The entity to teleport (used to determine distance from portal to avoid suffocation)</p>
* @param traveller <p>The location of the entity travelling</p>
* @return <p>The location the entity should be teleported to.</p>
*/ */
public Location getExit(Entity entity, Location traveller) { protected void loadChunks() {
Location exitLocation = null; for (Chunk chunk : getChunksToLoad()) {
RelativeBlockVector relativeExit = portal.getGate().getLayout().getExit(); chunk.addPluginChunkTicket(Stargate.getInstance());
if (relativeExit != null) { //Allow the chunk to unload after 10 seconds
BlockLocation exit = portal.getBlockAt(relativeExit); Stargate.addChunkUnloadRequest(new ChunkUnloadRequest(chunk, 10000L));
//Move one block out to prevent exiting inside the portal
float portalYaw = portal.getYaw();
if (portal.getOptions().isBackwards()) {
portalYaw += 180;
}
exitLocation = exit.getRelativeLocation(0D, 0D, 1, portalYaw);
if (entity != null) {
double entitySize = EntityHelper.getEntityMaxSize(entity);
//Prevent exit suffocation for players riding horses or similar
if (entitySize > 1) {
exitLocation = preventExitSuffocation(relativeExit, exitLocation, entity);
}
}
} else {
Stargate.logWarning(String.format("Missing destination point in .gate file %s",
portal.getGate().getFilename()));
} }
//Adjust pitch and height
return adjustExitLocation(traveller, exitLocation);
} }
/** /**
@ -185,41 +228,62 @@ public abstract class Teleporter {
* slab check is necessary to prevent the player from clipping through the slab and spawning beneath it. The water * slab check is necessary to prevent the player from clipping through the slab and spawning beneath it. The water
* check is necessary when teleporting boats to prevent it from becoming a submarine.</p> * check is necessary when teleporting boats to prevent it from becoming a submarine.</p>
* *
* @param traveller <p>The location of the travelling entity</p> * @param entity <p>The travelling entity</p>
* @param exitLocation <p>The exit location generated</p> * @param exitLocation <p>The exit location generated</p>
* @return <p>The location the travelling entity should be teleported to</p> * @return <p>The location the travelling entity should be teleported to</p>
*/ */
private Location adjustExitLocation(Location traveller, Location exitLocation) { private Location adjustExitLocationHeight(Entity entity, Location exitLocation) {
if (exitLocation != null) { if (exitLocation != null) {
BlockData blockData = exitLocation.getBlock().getBlockData(); BlockData blockData = exitLocation.getBlock().getBlockData();
if ((blockData instanceof Bisected bisected && bisected.getHalf() == Bisected.Half.BOTTOM) || if ((blockData instanceof Bisected bisected && bisected.getHalf() == Bisected.Half.BOTTOM) ||
(blockData instanceof Slab slab && slab.getType() == Slab.Type.BOTTOM)) { (blockData instanceof Slab slab && slab.getType() == Slab.Type.BOTTOM) ||
//Prevent traveller from spawning inside a slab blockData.getMaterial() == Material.WATER) {
Stargate.debug("adjustExitLocation", "Added a block to get above a slab"); //Prevent traveller from spawning inside a slab, or a boat from spawning inside water
exitLocation.add(0, 1, 0); Stargate.debug("adjustExitLocation", "Added a block to get above a slab or a block of water");
} else if (blockData.getMaterial() == Material.WATER) {
//If there's water outside, go one up to allow for boat teleportation
Stargate.debug("adjustExitLocation", "Added a block to get above a block of water");
exitLocation.add(0, 1, 0); exitLocation.add(0, 1, 0);
} }
exitLocation.setPitch(traveller.getPitch());
return exitLocation; return exitLocation;
} else { } else {
Stargate.logWarning("Unable to generate exit location"); Stargate.logWarning("Unable to generate exit location");
return traveller; return entity.getLocation();
} }
} }
/** /**
* Loads the chunks outside the portal's entrance * Gets the exit location for a given entity and current location
*
* @param entity <p>The entity to teleport (used to determine distance from portal to avoid suffocation)</p>
* @return <p>The location the entity should be teleported to.</p>
*/ */
protected void loadChunks() { private Location getExit(Entity entity) {
for (Chunk chunk : getChunksToLoad()) { Location exitLocation = null;
chunk.addPluginChunkTicket(Stargate.getInstance()); RelativeBlockVector relativeExit = portal.getGate().getLayout().getExit();
//Allow the chunk to unload after 10 seconds if (relativeExit != null) {
Stargate.addChunkUnloadRequest(new ChunkUnloadRequest(chunk, 10000L)); BlockLocation exit = portal.getBlockAt(relativeExit);
//Move one block out to prevent exiting inside the portal
float portalYaw = portal.getYaw();
if (portal.getOptions().isBackwards()) {
portalYaw += 180;
}
exitLocation = exit.getRelativeLocation(0D, 0D, 1, portalYaw);
if (entity != null) {
double entitySize = EntityHelper.getEntityMaxSize(entity);
//Prevent exit suffocation for players riding horses or similar
if (entitySize > 1) {
exitLocation = preventExitSuffocation(relativeExit, exitLocation, entity);
}
}
} else {
Stargate.logWarning(String.format("Missing destination point in .gate file %s",
portal.getGate().getFilename()));
} }
//Adjust height and rotation
Location adjusted = adjustExitLocationHeight(entity, exitLocation);
adjustExitLocationRotation(adjusted);
return adjusted;
} }
/** /**
@ -250,76 +314,4 @@ public abstract class Teleporter {
return chunksToLoad; return chunksToLoad;
} }
/**
* Checks whether a player has leashed creatures that block the teleportation
*
* @param player <p>The player trying to teleport</p>
* @return <p>False if the player has leashed any creatures that cannot go through the portal</p>
*/
public static boolean noLeashedCreaturesPreventTeleportation(Player player) {
//Find any nearby leashed entities to teleport with the player
List<Creature> nearbyCreatures = getLeashedCreatures(player);
//Disallow creatures with passengers to prevent smuggling
for (Creature creature : nearbyCreatures) {
if (!creature.getPassengers().isEmpty()) {
return false;
}
}
//If it's enabled, there is no problem
if (Stargate.getGateConfig().handleLeashedCreatures()) {
return true;
} else {
return nearbyCreatures.isEmpty();
}
}
/**
* Teleports any creatures leashed by the player
*
* <p>Will return false if the teleportation should be aborted because the player has leashed creatures that
* aren't allowed to be teleported with the player.</p>
*
* @param player <p>The player which is teleported</p>
* @param origin <p>The portal the player is teleporting from</p>
*/
protected void teleportLeashedCreatures(Player player, Portal origin) {
//If this feature is disabled, just return
if (!Stargate.getGateConfig().handleLeashedCreatures()) {
return;
}
//Find any nearby leashed entities to teleport with the player
List<Creature> nearbyEntities = getLeashedCreatures(player);
//Teleport all creatures leashed by the player to the portal the player is to exit from
for (Creature creature : nearbyEntities) {
creature.setLeashHolder(null);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> {
new EntityTeleporter(portal, creature).teleport(origin);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> creature.setLeashHolder(player), 6);
}, 2);
}
}
/**
* Gets all creatures leashed by a player within the given range
*
* @param player <p>The player to check</p>
* @return <p>A list of all creatures the player is holding in a leash (lead)</p>
*/
protected static List<Creature> getLeashedCreatures(Player player) {
List<Creature> leashedCreatures = new ArrayList<>();
//Find any nearby leashed entities to teleport with the player
List<Entity> nearbyEntities = player.getNearbyEntities(15, 15, 15);
//Teleport all creatures leashed by the player to the portal the player is to exit from
for (Entity entity : nearbyEntities) {
if (entity instanceof Creature creature && creature.isLeashed() && creature.getLeashHolder() == player) {
leashedCreatures.add(creature);
}
}
return leashedCreatures;
}
} }

View File

@ -2,13 +2,15 @@ package net.knarcraft.stargate.portal.teleporter;
import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.config.StargateGateConfig; import net.knarcraft.stargate.config.StargateGateConfig;
import net.knarcraft.stargate.event.StargateEntityPortalEvent;
import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.utility.DirectionHelper; import net.knarcraft.stargate.utility.DirectionHelper;
import net.knarcraft.stargate.utility.TeleportHelper;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.Boat;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle; import org.bukkit.entity.Vehicle;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
@ -24,11 +26,11 @@ public class VehicleTeleporter extends EntityTeleporter {
/** /**
* Instantiates a new vehicle teleporter * Instantiates a new vehicle teleporter
* *
* @param portal <p>The portal which is the target of the teleportation</p> * @param targetPortal <p>The targetPortal which is the target of the teleportation</p>
* @param teleportingVehicle <p>The teleporting vehicle</p> * @param teleportingVehicle <p>The teleporting vehicle</p>
*/ */
public VehicleTeleporter(Portal portal, Vehicle teleportingVehicle) { public VehicleTeleporter(Portal targetPortal, Vehicle teleportingVehicle) {
super(portal, teleportingVehicle); super(targetPortal, teleportingVehicle);
this.teleportingVehicle = teleportingVehicle; this.teleportingVehicle = teleportingVehicle;
} }
@ -42,29 +44,20 @@ public class VehicleTeleporter extends EntityTeleporter {
* @return <p>True if the vehicle was teleported. False otherwise</p> * @return <p>True if the vehicle was teleported. False otherwise</p>
*/ */
@Override @Override
public boolean teleport(Portal origin) { public boolean teleportEntity(Portal origin) {
Location traveller = teleportingVehicle.getLocation(); Stargate.debug("VehicleTeleporter::teleport", "Preparing to teleport: " + teleportingVehicle);
Location exit = getExit(teleportingVehicle, traveller);
double velocity = teleportingVehicle.getVelocity().length(); double velocity = teleportingVehicle.getVelocity().length();
//Stop and teleport //Stop the vehicle before teleporting
teleportingVehicle.setVelocity(new Vector()); teleportingVehicle.setVelocity(new Vector());
//Get new velocity //Get new velocity
Vector newVelocityDirection = DirectionHelper.getDirectionVectorFromYaw(portal.getYaw()); Vector newVelocityDirection = DirectionHelper.getDirectionVectorFromYaw(portal.getYaw());
Vector newVelocity = newVelocityDirection.multiply(velocity); Vector newVelocity = newVelocityDirection.multiply(velocity);
//Make sure the vehicle points out from the portal
adjustRotation(exit);
//Call the StargateEntityPortalEvent to allow plugins to change destination //Call the StargateEntityPortalEvent to allow plugins to change destination
if (!origin.equals(portal)) { triggerPortalEvent(origin, new StargateEntityPortalEvent(teleportingVehicle, origin, portal, exit));
exit = triggerEntityPortalEvent(origin, exit);
if (exit == null) {
return false;
}
}
//Teleport the vehicle //Teleport the vehicle
return teleportVehicle(exit, newVelocity, origin); return teleportVehicle(exit, newVelocity, origin);
@ -89,12 +82,13 @@ public class VehicleTeleporter extends EntityTeleporter {
return false; return false;
} }
if (!(teleportingVehicle instanceof LivingEntity)) { if (!(teleportingVehicle instanceof LivingEntity) &&
Stargate.getGateConfig().enableCraftBookRemoveOnEjectFix()) {
//Teleport a normal vehicle with passengers (minecart or boat) //Teleport a normal vehicle with passengers (minecart or boat)
putPassengersInNewVehicle(passengers, exit, newVelocity, origin); putPassengersInNewVehicle(passengers, exit, newVelocity, origin);
} else { } else {
//Teleport a living vehicle with passengers (pig, horse, donkey, strider) //Teleport a living vehicle with passengers (pig, horse, donkey, strider)
teleportLivingVehicle(exit, passengers, origin); teleportVehicle(passengers, exit, newVelocity, origin);
} }
} else { } else {
//Check if teleportation of empty vehicles is enabled //Check if teleportation of empty vehicles is enabled
@ -118,54 +112,28 @@ public class VehicleTeleporter extends EntityTeleporter {
private boolean vehiclePassengersAllowed(List<Entity> passengers) { private boolean vehiclePassengersAllowed(List<Entity> passengers) {
StargateGateConfig config = Stargate.getGateConfig(); StargateGateConfig config = Stargate.getGateConfig();
//Don't teleport if the vehicle contains a creature and creature transportation is disabled //Don't teleport if the vehicle contains a creature and creature transportation is disabled
if (containsNonPlayer(passengers) && !config.handleCreatureTransportation()) { if (TeleportHelper.containsNonPlayer(passengers) && !config.handleCreatureTransportation()) {
return false; return false;
} }
//Don't teleport if the player does not contain a player and non-player vehicles is disabled //Don't teleport if the player does not contain a player and non-player vehicles is disabled
return containsPlayer(passengers) || config.handleNonPlayerVehicles(); return TeleportHelper.containsPlayer(passengers) || config.handleNonPlayerVehicles();
}
/**
* Checks whether a list of entities contains any non-players
*
* @param entities <p>The list of entities to check</p>
* @return <p>True if at least one entity is not a player</p>
*/
private boolean containsNonPlayer(List<Entity> entities) {
for (Entity entity : entities) {
if (!(entity instanceof Player)) {
return true;
}
}
return false;
}
/**
* Checks whether a list of entities contains at least one player
*
* @param entities <p>The list of entities to check</p>
* @return <p>True if at least one player is present among the passengers</p>
*/
private boolean containsPlayer(List<Entity> entities) {
for (Entity entity : entities) {
if (entity instanceof Player) {
return true;
}
}
return false;
} }
/** /**
* Teleport a vehicle which is not a minecart or a boat * Teleport a vehicle which is not a minecart or a boat
* *
* @param exit <p>The location the vehicle will exit</p> * @param passengers <p>The passengers of the vehicle</p>
* @param passengers <p>The passengers of the vehicle</p> * @param exit <p>The location the vehicle will exit</p>
* @param origin <p>The portal the vehicle teleported from</p> * @param newVelocity <p>The new velocity of the teleported vehicle</p>
* @param origin <p>The portal the vehicle teleported from</p>
*/ */
private void teleportLivingVehicle(Location exit, List<Entity> passengers, Portal origin) { private void teleportVehicle(List<Entity> passengers, Location exit, Vector newVelocity, Portal origin) {
teleportingVehicle.eject(); if (teleportingVehicle.eject()) {
TeleportHelper.handleEntityPassengers(passengers, teleportingVehicle, origin, portal, exit.getDirection());
}
teleportingVehicle.teleport(exit); teleportingVehicle.teleport(exit);
handleVehiclePassengers(passengers, teleportingVehicle, 2, origin, exit.getDirection()); scheduler.scheduleSyncDelayedTask(Stargate.getInstance(),
() -> teleportingVehicle.setVelocity(newVelocity), 1);
} }
/** /**
@ -189,54 +157,15 @@ public class VehicleTeleporter extends EntityTeleporter {
} }
//Spawn a new vehicle //Spawn a new vehicle
Vehicle newVehicle = vehicleWorld.spawn(exit, teleportingVehicle.getClass()); Vehicle newVehicle = vehicleWorld.spawn(exit, teleportingVehicle.getClass());
if (teleportingVehicle instanceof Boat boat) {
((Boat) newVehicle).setWoodType(boat.getWoodType());
}
//Remove the old vehicle //Remove the old vehicle
teleportingVehicle.eject(); if (teleportingVehicle.eject()) {
TeleportHelper.handleEntityPassengers(passengers, newVehicle, origin, portal, exit.getDirection());
}
teleportingVehicle.remove(); teleportingVehicle.remove();
//Set rotation, add passengers and restore velocity
newVehicle.setRotation(exit.getYaw(), exit.getPitch());
handleVehiclePassengers(passengers, newVehicle, 1, origin, exit.getDirection());
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> newVehicle.setVelocity(newVelocity), 1); scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> newVehicle.setVelocity(newVelocity), 1);
} }
/**
* Ejects, teleports and adds all passengers to the target vehicle
*
* @param passengers <p>The passengers to handle</p>
* @param vehicle <p>The vehicle the passengers should be put into</p>
* @param delay <p>The amount of milliseconds to wait before adding the vehicle passengers</p>
* @param origin <p>The portal the vehicle teleported from</p>
* @param exitRotation <p>The rotation of any passengers exiting the stargate</p>
*/
private void handleVehiclePassengers(List<Entity> passengers, Vehicle vehicle, long delay, Portal origin, Vector exitRotation) {
for (Entity passenger : passengers) {
passenger.eject();
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> {
if (passenger instanceof Player player) {
//Teleport any creatures leashed by the player in a 15-block range
teleportLeashedCreatures(player, origin);
}
teleportAndAddPassenger(vehicle, passenger, exitRotation);
}, delay);
}
}
/**
* Teleports and adds a passenger to a vehicle
*
* <p>Teleportation of living vehicles is really buggy if you wait between the teleportation and passenger adding,
* but there needs to be a delay between teleporting the vehicle and teleporting and adding the passenger.</p>
*
* @param targetVehicle <p>The vehicle to add the passenger to</p>
* @param passenger <p>The passenger to teleport and add</p>
* @param exitDirection <p>The direction of any passengers exiting the stargate</p>
*/
private void teleportAndAddPassenger(Vehicle targetVehicle, Entity passenger, Vector exitDirection) {
if (!passenger.teleport(targetVehicle.getLocation().clone().setDirection(exitDirection))) {
Stargate.debug("handleVehiclePassengers", "Failed to teleport passenger");
}
if (!targetVehicle.addPassenger(passenger)) {
Stargate.debug("handleVehiclePassengers", "Failed to add passenger");
}
}
} }

View File

@ -21,18 +21,22 @@ public class BlockChangeThread implements Runnable {
long sTime = System.nanoTime(); long sTime = System.nanoTime();
//Repeat for at most 0.025 seconds //Repeat for at most 0.025 seconds
while (System.nanoTime() - sTime < 25000000) { while (System.nanoTime() - sTime < 25000000) {
pollQueue(); if (pollQueue()) {
break;
}
} }
} }
/** /**
* Polls the block change request queue for any waiting requests * Polls the block change request queue for any waiting requests
*
* @return <p>True if the queue is empty and it's safe to quit</p>
*/ */
public static void pollQueue() { public static boolean pollQueue() {
//Abort if there's no work to be done //Abort if there's no work to be done
BlockChangeRequest blockChangeRequest = Stargate.getBlockChangeRequestQueue().poll(); BlockChangeRequest blockChangeRequest = Stargate.getBlockChangeRequestQueue().poll();
if (blockChangeRequest == null) { if (blockChangeRequest == null) {
return; return true;
} }
//Change the material of the pulled block //Change the material of the pulled block
@ -46,6 +50,7 @@ public class BlockChangeThread implements Runnable {
//If orientation is relevant, adjust the block's orientation //If orientation is relevant, adjust the block's orientation
orientBlock(block, blockChangeRequest.getAxis()); orientBlock(block, blockChangeRequest.getAxis());
} }
return false;
} }
/** /**

View File

@ -190,7 +190,7 @@ public final class BungeeHelper {
} }
//Teleport the player back to this gate, for sanity's sake //Teleport the player back to this gate, for sanity's sake
new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event); new PlayerTeleporter(entrancePortal, player).teleportPlayer(entrancePortal, event);
//Send the SGBungee packet first, it will be queued by BC if required //Send the SGBungee packet first, it will be queued by BC if required
if (!BungeeHelper.sendTeleportationMessage(player, entrancePortal)) { if (!BungeeHelper.sendTeleportationMessage(player, entrancePortal)) {

View File

@ -6,7 +6,14 @@ import org.bukkit.Color;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class ColorHelper { /**
* A helper class for dealing with colors
*/
public final class ColorHelper {
private ColorHelper() {
}
/** /**
* Inverts the given color * Inverts the given color

View File

@ -387,7 +387,7 @@ public final class PermissionHelper {
if (!entrancePortal.getOptions().isSilent()) { if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
} }
new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event); new PlayerTeleporter(entrancePortal, player).teleportPlayer(entrancePortal, event);
return true; return true;
} }
@ -401,7 +401,7 @@ public final class PermissionHelper {
if (!entrancePortal.getOptions().isSilent()) { if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
} }
new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event); new PlayerTeleporter(entrancePortal, player).teleportPlayer(entrancePortal, event);
entrancePortal.getPortalOpener().closePortal(false); entrancePortal.getPortalOpener().closePortal(false);
return true; return true;
} }

View File

@ -0,0 +1,231 @@
package net.knarcraft.stargate.utility;
import net.knarcraft.stargate.Stargate;
import net.knarcraft.stargate.portal.Portal;
import net.knarcraft.stargate.portal.teleporter.EntityTeleporter;
import org.bukkit.Bukkit;
import org.bukkit.entity.Creature;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
/**
* A helper class with methods for various teleportation tasks
*
* <p>The teleport helper mainly helps with passengers and leashed creatures</p>
*/
public final class TeleportHelper {
private TeleportHelper() {
}
/**
* Checks whether a player has leashed creatures that block the teleportation
*
* @param player <p>The player trying to teleport</p>
* @return <p>False if the player has leashed any creatures that cannot go through the portal</p>
*/
public static boolean noLeashedCreaturesPreventTeleportation(Player player) {
//Find any nearby leashed entities to teleport with the player
List<Creature> nearbyCreatures = getLeashedCreatures(player);
//Disallow creatures with passengers to prevent smuggling
for (Creature creature : nearbyCreatures) {
if (!creature.getPassengers().isEmpty()) {
return false;
}
}
//TODO: Improve this to account for any players sitting on any of the lead creatures
//If it's enabled, there is no problem
if (Stargate.getGateConfig().handleLeashedCreatures()) {
return true;
} else {
return nearbyCreatures.isEmpty();
}
}
/**
* Gets all creatures leashed by a player within the given range
*
* @param player <p>The player to check</p>
* @return <p>A list of all creatures the player is holding in a leash (lead)</p>
*/
public static List<Creature> getLeashedCreatures(Player player) {
List<Creature> leashedCreatures = new ArrayList<>();
//Find any nearby leashed entities to teleport with the player
List<Entity> nearbyEntities = player.getNearbyEntities(15, 15, 15);
//Teleport all creatures leashed by the player to the portal the player is to exit from
for (Entity entity : nearbyEntities) {
if (entity instanceof Creature creature && creature.isLeashed() && creature.getLeashHolder() == player) {
leashedCreatures.add(creature);
}
}
return leashedCreatures;
}
/**
* Teleports and adds a passenger to an entity
*
* <p>Teleportation of living vehicles is really buggy if you wait between the teleportation and passenger adding,
* but there needs to be a delay between teleporting the vehicle and teleporting and adding the passenger.</p>
*
* @param targetVehicle <p>The entity to add the passenger to</p>
* @param passenger <p>The passenger to teleport and add</p>
* @param exitDirection <p>The direction of any passengers exiting the stargate</p>
*/
public static void teleportAndAddPassenger(Entity targetVehicle, Entity passenger, Vector exitDirection) {
if (!passenger.teleport(targetVehicle.getLocation().clone().setDirection(exitDirection))) {
Stargate.debug("handleVehiclePassengers", "Failed to teleport passenger" + passenger);
}
if (!targetVehicle.addPassenger(passenger)) {
Stargate.debug("handleVehiclePassengers", "Failed to add passenger" + passenger);
}
}
/**
* Ejects, teleports and adds all passengers to the target entity
*
* @param passengers <p>The passengers to handle</p>
* @param entity <p>The entity the passengers should be put into</p
* @param origin <p>The portal the entity teleported from</p>
* @param target <p>The portal the entity is teleporting to</p>
* @param exitRotation <p>The rotation of any passengers exiting the stargate</p>
*/
public static void handleEntityPassengers(List<Entity> passengers, Entity entity, Portal origin, Portal target,
Vector exitRotation) {
for (Entity passenger : passengers) {
List<Entity> passengerPassengers = passenger.getPassengers();
if (!passengerPassengers.isEmpty()) {
Stargate.debug("Teleporter::handleEntityPassengers", "Found the entities: " +
passengerPassengers + " as passengers of " + entity);
}
if (passenger.eject()) {
//Teleport any passengers of the passenger
handleEntityPassengers(passengerPassengers, passenger, origin, target, exitRotation);
}
Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), () -> {
if (passenger instanceof Player player) {
//Teleport any creatures leashed by the player in a 15-block range
teleportLeashedCreatures(player, origin, target);
}
TeleportHelper.teleportAndAddPassenger(entity, passenger, exitRotation);
}, passenger instanceof Player ? Stargate.getGateConfig().waitForPlayerAfterTeleportDelay() : 0);
}
}
/**
* Teleports any creatures leashed by the player
*
* <p>Will return false if the teleportation should be aborted because the player has leashed creatures that
* aren't allowed to be teleported with the player.</p>
*
* @param player <p>The player which is teleported</p>
* @param origin <p>The portal the player is teleporting from</p>
* @param target <p>The portal the player is teleporting to</p>
*/
public static void teleportLeashedCreatures(Player player, Portal origin, Portal target) {
//If this feature is disabled, just return
if (!Stargate.getGateConfig().handleLeashedCreatures()) {
return;
}
BukkitScheduler scheduler = Bukkit.getScheduler();
//Find any nearby leashed entities to teleport with the player
List<Creature> nearbyEntities = TeleportHelper.getLeashedCreatures(player);
//Teleport all creatures leashed by the player to the portal the player is to exit from
for (Creature creature : nearbyEntities) {
creature.setLeashHolder(null);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> {
new EntityTeleporter(target, creature).teleportEntity(origin);
scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> creature.setLeashHolder(player),
Stargate.getGateConfig().waitForPlayerAfterTeleportDelay());
}, 2);
}
}
/**
* Checks whether a list of entities or any of their passengers contains any non-players
*
* @param entities <p>The list of entities to check</p>
* @return <p>True if at least one entity is not a player</p>
*/
public static boolean containsNonPlayer(List<Entity> entities) {
for (Entity entity : entities) {
if (!(entity instanceof Player) || containsNonPlayer(entity.getPassengers())) {
return true;
}
}
return false;
}
/**
* Checks whether a list of entities of their passengers contains at least one player
*
* @param entities <p>The list of entities to check</p>
* @return <p>True if at least one player is present among the passengers</p>
*/
public static boolean containsPlayer(List<Entity> entities) {
for (Entity entity : entities) {
if (entity instanceof Player || containsPlayer(entity.getPassengers())) {
return true;
}
}
return false;
}
/**
* Gets all players recursively from a list of entities
*
* @param entities <p>The entities to check for players</p>
* @return <p>The found players</p>
*/
public static List<Player> getPlayers(List<Entity> entities) {
List<Player> players = new ArrayList<>(5);
for (Entity entity : entities) {
if (entity instanceof Player) {
players.add((Player) entity);
}
players.addAll(getPlayers(entity.getPassengers()));
}
return players;
}
/**
* Checks whether the given player is allowed to and can afford to teleport
*
* @param player <p>The player trying to teleport</p>
* @param entrancePortal <p>The portal the player is entering</p>
* @param destinationPortal <p>The portal the player is to exit from</p>
* @return <p>True if the player is allowed to teleport and is able to pay necessary fees</p>
*/
public static boolean playerCanTeleport(Player player, Portal entrancePortal, Portal destinationPortal) {
//Make sure the user can access the portal
if (PermissionHelper.cannotAccessPortal(player, entrancePortal, destinationPortal)) {
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg"));
}
entrancePortal.getPortalOpener().closePortal(false);
return false;
}
//Check if the player is able to afford the teleport fee
int cost = EconomyHelper.getUseCost(player, entrancePortal, destinationPortal);
boolean canAffordFee = cost <= 0 || Stargate.getEconomyConfig().canAffordFee(player, cost);
if (!canAffordFee) {
if (!entrancePortal.getOptions().isSilent()) {
Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("ecoInFunds"));
}
return false;
}
return TeleportHelper.noLeashedCreaturesPreventTeleportation(player);
}
}

View File

@ -54,6 +54,8 @@ gates:
handleNonPlayerVehicles: true handleNonPlayerVehicles: true
# handleLeashedCreatures - Whether to allow creatures lead by a player to teleport with the player # handleLeashedCreatures - Whether to allow creatures lead by a player to teleport with the player
handleLeashedCreatures: true handleLeashedCreatures: true
# enableCraftBookRemoveOnEjectFix - Whether to enable a fix that causes loss of NBT data, but allows vehicle teleportation to work when CraftBook's remove minecart/boat on eject setting is enabled
enableCraftBookRemoveOnEjectFix: false
# I------------I-------------I # # I------------I-------------I #
# stargate economy options # # stargate economy options #
@ -84,3 +86,6 @@ debugging:
debug: false debug: false
# permissionDebug - This will output any and all Permissions checks to console, used for permissions debugging (Requires debug: true) # permissionDebug - This will output any and all Permissions checks to console, used for permissions debugging (Requires debug: true)
permissionDebug: false permissionDebug: false
advanced:
# waitForPlayerAfterTeleportDelay - The amount of ticks to wait before adding a player as passenger of a vehicle. On slow servers, a value of 6 is required to avoid client glitches after teleporting on a vehicle.
waitForPlayerAfterTeleportDelay: 6