diff --git a/HEADER b/HEADER new file mode 100644 index 0000000..126ab6a --- /dev/null +++ b/HEADER @@ -0,0 +1,19 @@ +Stargate - A portal plugin for Bukkit +Copyright (C) 2011 Shaun (sturmeh) +Copyright (C) 2011 Dinnerbone +Copyright (C) 2011-2013 Steven "Drakia" Scott +Copyright (C) 2015-2020 Michael Smith (PseudoKnight) +Copyright (C) 2021-2022 Kristian Knarvik (EpicKnarvik97) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . \ No newline at end of file diff --git a/README.md b/README.md index ae308f4..05785ef 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ can share a network or be split into clusters; they can be hidden on a network o - **Vault economy support** -- can add costs for create, destroy and use. - **Ability to create custom gate configurations**. Four different default gate configurations are available. - **Message customization** -- **Multiple built-in languages** (de, en, es, fr, hu, it, nb-no, nl, nn-no, pt-br, ru) +- **Multiple built-in languages** (de, en, es, fr, hu, it, ja, nb-no, nl, nn-no, pt-br, ru, zh_cn) - **Teleport across worlds or servers** (BungeeCord supported) - **Vehicle teleportation** -- teleport minecarts, boats, horses, pigs and striders - **Leashed teleportation** -- teleport any creature in a leash with the player @@ -26,6 +26,11 @@ This was originally TheDgtl's Bukkit port of the Stargate plugin for hMod by Din of [PseudoKnight's fork](https://github.com/PseudoKnight/Stargate-Bukkit). This fork's main purpose is to create a clean version of Stargate compliant with Spigot 1.17, even if it means changing the entire project's previous structure. +## License + +Stargate is licensed under the GNU Lesser General Public License Version 3.0. This includes every source and resource +file. See the HEADER file for a more detailed license description. + ## Migration This plugin should be compatible with configurations from the Stargate plugin all the way back. The nethergate.gate @@ -298,7 +303,7 @@ while the per-gate costs re defined in the .gate files. To define a certain cost # Configuration ``` -language - The language to use (Included languages: en, de, es, fr, hu, it, nb-no, nl, nn-no, pt-br, ru) +language - The language to use (Included languages: en, de, es, fr, hu, it, ja, nb-no, nl, nn-no, pt-br, ru, zh_cn) adminUpdateAlert - Whether to alert admins about an available update when joining the server folders: portalFolder - The folder your portal databases are saved in @@ -306,6 +311,7 @@ folders: gates: maxGatesEachNetwork - If non-zero, will define the maximum amount of gates allowed on any network. defaultGateNetwork - The default gate network + exitVelocity - The velocity to give players exiting stargates, relative to the entry velocity (1 = same as entry velocity) cosmetic: rememberDestination - Whether to set the first destination as the last used destination for all gates sortNetworkDestinations - If true, network lists will be sorted alphabetically. @@ -323,6 +329,7 @@ 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. 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: useEconomy - Whether or not to enable Economy using Vault (must have the Vault plugin) createCost - The cost to create a stargate @@ -335,11 +342,13 @@ economy: debugging: debug - Whether to show massive 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 -It is possible to customize all the messages Stargate displays, including the [Stargate] prefix. You can find the +It is possible to customize all the messages Stargate displays, including the \[Stargate] prefix. You can find the strings in plugins/Stargate/lang/chosenLanguage.txt. If a string is removed, or left blank, it will default to the default english string. There are some special cases @@ -396,6 +405,38 @@ portalInfoServer=Server: %server% # Changes +#### \[Version 0.9.3.7] EpicKnarvik97 fork + +- Adds the Japanese language file provided by spigot user furplag + +#### \[Version 0.9.3.6] EpicKnarvik97 fork + +- Adds the simplified Chinese language file provided by spigot user YKDZ + +#### \[Version 0.9.3.5] EpicKnarvik97 fork + +- Fixes the wait for player delay being too low by default +- Performs some minor code optimizations and restructuring + +#### \[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.3] EpicKnarvik97 fork + +- Prevents Zombified Piglins from randomly spawning at Stargates + +#### \[Version 0.9.3.2] EpicKnarvik97 fork + +- Adds a config option to set the exit velocity of any players exiting a stargate +- Adjusts vehicle teleportation a bit to prevent passengers' exit rotation from being wrong +- Improves the checking for buggy double-clicks on non-button blocks + #### \[Version 0.9.3.1] EpicKnarvik97 fork - Ignores the type of air when checking if a stargate is valid diff --git a/pom.xml b/pom.xml index 2dfda2c..313190c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ net.knarcraft Stargate - 0.9.3.1 + 0.9.4.1 @@ -28,13 +28,17 @@ vault-repo http://nexus.hc.to/content/repositories/pub_releases + + dynmap + https://repo.mikeprimm.com/ + org.spigotmc spigot-api - 1.18.1-R0.1-SNAPSHOT + 1.19-R0.1-SNAPSHOT net.milkbowl.vault @@ -44,27 +48,32 @@ org.junit.jupiter junit-jupiter-api - 5.8.0-M1 + 5.8.2 test com.github.seeseemelk MockBukkit-v1.18 - 1.14.0 + 1.15.5 test org.jetbrains annotations - 19.0.0 + 22.0.0 compile junit junit - 4.13.1 + 4.13.2 test + + us.dynmap + dynmap-api + 3.1-beta-2 + diff --git a/src/main/java/net/knarcraft/stargate/Stargate.java b/src/main/java/net/knarcraft/stargate/Stargate.java index 7b4355f..b9ed27d 100644 --- a/src/main/java/net/knarcraft/stargate/Stargate.java +++ b/src/main/java/net/knarcraft/stargate/Stargate.java @@ -10,6 +10,7 @@ import net.knarcraft.stargate.container.BlockChangeRequest; import net.knarcraft.stargate.container.ChunkUnloadRequest; import net.knarcraft.stargate.listener.BlockEventListener; import net.knarcraft.stargate.listener.EntityEventListener; +import net.knarcraft.stargate.listener.EntitySpawnListener; import net.knarcraft.stargate.listener.PlayerEventListener; import net.knarcraft.stargate.listener.PluginEventListener; import net.knarcraft.stargate.listener.PortalEventListener; @@ -38,24 +39,44 @@ import java.util.Queue; import java.util.logging.Level; import java.util.logging.Logger; +/* +Stargate - A portal plugin for Bukkit +Copyright (C) 2011 Shaun (sturmeh) +Copyright (C) 2011 Dinnerbone +Copyright (C) 2011-2013 Steven "Drakia" Scott +Copyright (C) 2015-2020 Michael Smith (PseudoKnight) +Copyright (C) 2021-2022 Kristian Knarvik (EpicKnarvik97) + +The following license notice applies to all source and resource files in the Stargate project: + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . + */ + /** * The main class of the Stargate plugin */ @SuppressWarnings("unused") public class Stargate extends JavaPlugin { - //Used for changing gate open/closed material. private static final Queue blockChangeRequestQueue = new LinkedList<>(); private static final Queue chunkUnloadQueue = new PriorityQueue<>(); private static Logger logger; private static Stargate stargate; - private static String pluginVersion; - private static PluginManager pluginManager; private static StargateConfig stargateConfig; - private static String updateAvailable = null; /** @@ -388,6 +409,7 @@ public class Stargate extends JavaPlugin { pluginManager.registerEvents(new WorldEventListener(), this); pluginManager.registerEvents(new PluginEventListener(this), this); pluginManager.registerEvents(new TeleportEventListener(), this); + pluginManager.registerEvents(new EntitySpawnListener(), this); } /** @@ -428,4 +450,5 @@ public class Stargate extends JavaPlugin { public static StargateConfig getStargateConfig() { return stargateConfig; } + } diff --git a/src/main/java/net/knarcraft/stargate/command/CommandAbout.java b/src/main/java/net/knarcraft/stargate/command/CommandAbout.java index 3e5b441..f0acd50 100644 --- a/src/main/java/net/knarcraft/stargate/command/CommandAbout.java +++ b/src/main/java/net/knarcraft/stargate/command/CommandAbout.java @@ -23,8 +23,9 @@ public class CommandAbout implements CommandExecutor { commandSender.sendMessage(textColor + "Go to " + highlightColor + "https://git.knarcraft.net/EpicKnarvik97/Stargate " + textColor + "for the official repository"); String author = Stargate.getStargateConfig().getLanguageLoader().getString("author"); - if (!author.isEmpty()) + if (!author.isEmpty()) { commandSender.sendMessage(textColor + "Language created by " + highlightColor + author); + } return true; } diff --git a/src/main/java/net/knarcraft/stargate/command/CommandConfig.java b/src/main/java/net/knarcraft/stargate/command/CommandConfig.java index 0f6dccb..6374518 100644 --- a/src/main/java/net/knarcraft/stargate/command/CommandConfig.java +++ b/src/main/java/net/knarcraft/stargate/command/CommandConfig.java @@ -3,12 +3,12 @@ package net.knarcraft.stargate.command; import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.config.ConfigOption; import net.knarcraft.stargate.config.ConfigTag; +import net.knarcraft.stargate.config.DynmapManager; import net.knarcraft.stargate.config.OptionDataType; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.PortalRegistry; import net.knarcraft.stargate.portal.PortalSignDrawer; import net.md_5.bungee.api.ChatColor; -import org.apache.commons.lang.StringUtils; import org.bukkit.Material; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -90,6 +90,15 @@ public class CommandConfig implements CommandExecutor { configuration.set(selectedOption.getConfigNode(), intValue); } } + case DOUBLE -> { + Double doubleValue = getDouble(commandSender, selectedOption, value); + if (doubleValue == null) { + return; + } else { + Stargate.getStargateConfig().getConfigOptionsReference().put(selectedOption, doubleValue); + configuration.set(selectedOption.getConfigNode(), doubleValue); + } + } case STRING -> { updateStringConfigValue(selectedOption, commandSender, value); configuration.set(selectedOption.getConfigNode(), value); @@ -314,6 +323,30 @@ public class CommandConfig implements CommandExecutor { } } + /** + * Gets a double from a string + * + * @param commandSender

The command sender that sent the config command

+ * @param selectedOption

The option the command sender is trying to change

+ * @param value

The value given

+ * @return

A double, or null if it was invalid

+ */ + private Double getDouble(CommandSender commandSender, ConfigOption selectedOption, String value) { + try { + double doubleValue = Double.parseDouble(value); + + if (selectedOption == ConfigOption.EXIT_VELOCITY && doubleValue < 0) { + commandSender.sendMessage(ChatColor.RED + "This config option cannot be negative."); + return null; + } + + return doubleValue; + } catch (NumberFormatException exception) { + commandSender.sendMessage(ChatColor.RED + "Invalid number given"); + return null; + } + } + /** * Reloads the config if necessary * @@ -345,6 +378,10 @@ public class CommandConfig implements CommandExecutor { //Load or unload Vault and Economy as necessary Stargate.getStargateConfig().reloadEconomy(); } + if (ConfigTag.requiresDynmapReload(configOption)) { + //Regenerate all Dynmap markers + DynmapManager.addAllPortalMarkers(); + } } } @@ -383,7 +420,7 @@ public class CommandConfig implements CommandExecutor { Object defaultValue = option.getDefaultValue(); String stringValue = String.valueOf(defaultValue); if (option.getDataType() == OptionDataType.STRING_LIST) { - stringValue = "[" + StringUtils.join((String[]) defaultValue, ",") + "]"; + stringValue = "[" + String.join(",", (String[]) defaultValue) + "]"; } return ChatColor.GOLD + option.getName() + ChatColor.WHITE + " - " + ChatColor.GREEN + option.getDescription() + ChatColor.DARK_GRAY + " (Default: " + ChatColor.GRAY + stringValue + ChatColor.DARK_GRAY + ")"; diff --git a/src/main/java/net/knarcraft/stargate/command/CommandStarGate.java b/src/main/java/net/knarcraft/stargate/command/CommandStarGate.java index bd82080..2185b54 100644 --- a/src/main/java/net/knarcraft/stargate/command/CommandStarGate.java +++ b/src/main/java/net/knarcraft/stargate/command/CommandStarGate.java @@ -2,12 +2,13 @@ package net.knarcraft.stargate.command; import net.knarcraft.stargate.Stargate; import net.md_5.bungee.api.ChatColor; -import org.apache.commons.lang.ArrayUtils; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; +import java.util.Arrays; + /** * This command represents any command which starts with stargate * @@ -25,7 +26,7 @@ public class CommandStarGate implements CommandExecutor { } else if (args[0].equalsIgnoreCase("reload")) { return new CommandReload().onCommand(commandSender, command, s, args); } else if (args[0].equalsIgnoreCase("config")) { - String[] subArgs = (String[]) ArrayUtils.remove(args, 0); + String[] subArgs = Arrays.copyOfRange(args, 1, args.length); return new CommandConfig().onCommand(commandSender, command, s, subArgs); } return false; @@ -35,4 +36,5 @@ public class CommandStarGate implements CommandExecutor { return true; } } + } diff --git a/src/main/java/net/knarcraft/stargate/command/ConfigTabCompleter.java b/src/main/java/net/knarcraft/stargate/command/ConfigTabCompleter.java index c37de81..cb5447d 100644 --- a/src/main/java/net/knarcraft/stargate/command/ConfigTabCompleter.java +++ b/src/main/java/net/knarcraft/stargate/command/ConfigTabCompleter.java @@ -21,16 +21,17 @@ public class ConfigTabCompleter implements TabCompleter { private List signTypes; private List booleans; - private List numbers; + private List integers; private List chatColors; private List languages; private List extendedColors; + private List doubles; @Nullable @Override public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] args) { - if (signTypes == null || booleans == null || numbers == null || chatColors == null || languages == null) { + if (signTypes == null || booleans == null || integers == null || chatColors == null || languages == null) { initializeAutoCompleteLists(); } if (args.length > 1) { @@ -104,7 +105,16 @@ public class ConfigTabCompleter implements TabCompleter { //If the config value is an integer, display some valid numbers if (selectedOption.getDataType() == OptionDataType.INTEGER) { if (typedText.trim().isEmpty()) { - return numbers; + return integers; + } else { + return new ArrayList<>(); + } + } + + //If the config value is a double, display some valid numbers + if (selectedOption.getDataType() == OptionDataType.DOUBLE) { + if (typedText.trim().isEmpty()) { + return doubles; } else { return new ArrayList<>(); } @@ -164,9 +174,9 @@ public class ConfigTabCompleter implements TabCompleter { booleans.add("true"); booleans.add("false"); - numbers = new ArrayList<>(); - numbers.add("0"); - numbers.add("5"); + integers = new ArrayList<>(); + integers.add("0"); + integers.add("5"); signTypes = new ArrayList<>(); for (Material material : Material.values()) { @@ -181,6 +191,12 @@ public class ConfigTabCompleter implements TabCompleter { extendedColors = new ArrayList<>(chatColors); extendedColors.add("default"); extendedColors.add("inverted"); + + doubles = new ArrayList<>(); + doubles.add("5"); + doubles.add("1"); + doubles.add("0.5"); + doubles.add("0.1"); } @@ -201,22 +217,10 @@ public class ConfigTabCompleter implements TabCompleter { */ private List getChatColors() { List chatColors = new ArrayList<>(); - chatColors.add(ChatColor.WHITE); - chatColors.add(ChatColor.BLUE); - chatColors.add(ChatColor.DARK_BLUE); - chatColors.add(ChatColor.DARK_PURPLE); - chatColors.add(ChatColor.LIGHT_PURPLE); - chatColors.add(ChatColor.GOLD); - chatColors.add(ChatColor.GREEN); - chatColors.add(ChatColor.BLACK); - chatColors.add(ChatColor.DARK_GREEN); - chatColors.add(ChatColor.DARK_RED); - chatColors.add(ChatColor.RED); - chatColors.add(ChatColor.AQUA); - chatColors.add(ChatColor.DARK_AQUA); - chatColors.add(ChatColor.DARK_GRAY); - chatColors.add(ChatColor.GRAY); - chatColors.add(ChatColor.YELLOW); + char[] colors = new char[]{'a', 'b', 'c', 'd', 'e', 'f', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; + for (char color : colors) { + chatColors.add(ChatColor.getByChar(color)); + } chatColors.add(ChatColor.of("#ed76d9")); chatColors.add(ChatColor.of("#ffecb7")); return chatColors; @@ -233,11 +237,13 @@ public class ConfigTabCompleter implements TabCompleter { languages.add("fr"); languages.add("hu"); languages.add("it"); + languages.add("ja"); languages.add("nb-no"); languages.add("nl"); languages.add("nn-no"); languages.add("pt-br"); languages.add("ru"); + languages.add("zh_cn"); //TODO: Generate this list dynamically by listing the language files in the jar and adding the user's custom // language files } diff --git a/src/main/java/net/knarcraft/stargate/command/StarGateTabCompleter.java b/src/main/java/net/knarcraft/stargate/command/StarGateTabCompleter.java index d67b4db..96ecd3d 100644 --- a/src/main/java/net/knarcraft/stargate/command/StarGateTabCompleter.java +++ b/src/main/java/net/knarcraft/stargate/command/StarGateTabCompleter.java @@ -1,6 +1,5 @@ package net.knarcraft.stargate.command; -import org.apache.commons.lang.ArrayUtils; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.TabCompleter; @@ -9,6 +8,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -29,7 +29,7 @@ public class StarGateTabCompleter implements TabCompleter { } return matchingCommands; } else if (args.length > 1 && args[0].equalsIgnoreCase("config")) { - String[] subArgs = (String[]) ArrayUtils.remove(args, 0); + String[] subArgs = Arrays.copyOfRange(args, 1, args.length); return new ConfigTabCompleter().onTabComplete(commandSender, command, s, subArgs); } else { return new ArrayList<>(); diff --git a/src/main/java/net/knarcraft/stargate/config/ConfigOption.java b/src/main/java/net/knarcraft/stargate/config/ConfigOption.java index 76d3870..e12ea59 100644 --- a/src/main/java/net/knarcraft/stargate/config/ConfigOption.java +++ b/src/main/java/net/knarcraft/stargate/config/ConfigOption.java @@ -50,6 +50,9 @@ public enum ConfigOption { */ 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[]{ "'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'"}), @@ -102,6 +105,20 @@ public enum ConfigOption { HANDLE_LEASHED_CREATURES("gates.functionality.handleLeashedCreatures", "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.", 6), + /** * Whether to enable economy support for taking payment from players creating/destroying/using stargates */ @@ -156,8 +173,23 @@ public enum ConfigOption { /** * Whether to alert admins about new updates */ - ADMIN_UPDATE_ALERT("adminUpdateAlert", "Whether to alert admins about new plugin updates", true); + ADMIN_UPDATE_ALERT("adminUpdateAlert", "Whether to alert admins about new plugin updates", true), + /** + * The velocity of players exiting a stargate, relative to the entry velocity + */ + EXIT_VELOCITY("gates.exitVelocity", "The velocity of players exiting stargates, relative to the entry velocity", 0.1D), + + /** + * Whether to enable showing Stargates in Dynmap + */ + ENABLE_DYNMAP("dynmap.enableDynmap", "Whether to display Stargates in Dynmap's map", true), + + /** + * Whether to hide Dynmap icons by default + */ + DYNMAP_ICONS_DEFAULT_HIDDEN("dynmap.dynmapIconsHiddenByDefault", + "Whether to hide Stargate's Dynmap icons by default, requiring the user to enable them.", true); private final String configNode; private final String description; @@ -184,6 +216,8 @@ public enum ConfigOption { this.dataType = OptionDataType.BOOLEAN; } else if (defaultValue instanceof Integer) { this.dataType = OptionDataType.INTEGER; + } else if (defaultValue instanceof Double) { + this.dataType = OptionDataType.DOUBLE; } else { throw new IllegalArgumentException("Unknown config data type encountered: " + defaultValue); } diff --git a/src/main/java/net/knarcraft/stargate/config/ConfigTag.java b/src/main/java/net/knarcraft/stargate/config/ConfigTag.java index 49c9083..024b205 100644 --- a/src/main/java/net/knarcraft/stargate/config/ConfigTag.java +++ b/src/main/java/net/knarcraft/stargate/config/ConfigTag.java @@ -9,7 +9,8 @@ public enum ConfigTag { COLOR(new ConfigOption[]{ConfigOption.FREE_GATES_COLOR, ConfigOption.MAIN_SIGN_COLOR, ConfigOption.HIGHLIGHT_SIGN_COLOR, ConfigOption.PER_SIGN_COLORS}), - FOLDER(new ConfigOption[]{ConfigOption.GATE_FOLDER, ConfigOption.PORTAL_FOLDER}); + FOLDER(new ConfigOption[]{ConfigOption.GATE_FOLDER, ConfigOption.PORTAL_FOLDER}), + DYNMAP(new ConfigOption[]{ConfigOption.ENABLE_DYNMAP, ConfigOption.DYNMAP_ICONS_DEFAULT_HIDDEN}); private final ConfigOption[] taggedOptions; @@ -52,6 +53,16 @@ public enum ConfigTag { return FOLDER.isTagged(option); } + /** + * Checks whether a given config option requires a re-load of all Dynmap markers + * + * @param configOption

The config option to check

+ * @return

True if changing the config option requires a reload of all dynmap markers

+ */ + public static boolean requiresDynmapReload(ConfigOption configOption) { + return DYNMAP.isTagged(configOption); + } + /** * Checks whether a given config option requires a portal reload to take effect * diff --git a/src/main/java/net/knarcraft/stargate/config/DynmapManager.java b/src/main/java/net/knarcraft/stargate/config/DynmapManager.java new file mode 100644 index 0000000..5eaa0c9 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/config/DynmapManager.java @@ -0,0 +1,134 @@ +package net.knarcraft.stargate.config; + +import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.portal.Portal; +import net.knarcraft.stargate.portal.PortalRegistry; +import org.bukkit.Location; +import org.bukkit.World; +import org.dynmap.DynmapAPI; +import org.dynmap.markers.GenericMarker; +import org.dynmap.markers.Marker; +import org.dynmap.markers.MarkerIcon; +import org.dynmap.markers.MarkerSet; + +/** + * A manager for dealing with everything Dynmap + */ +public final class DynmapManager { + + private static MarkerSet markerSet; + private static MarkerIcon portalIcon; + + private DynmapManager() { + + } + + /** + * Initializes the dynmap manager + * + * @param dynmapAPI

A reference

+ */ + public static void initialize(DynmapAPI dynmapAPI) { + if (dynmapAPI == null || dynmapAPI.getMarkerAPI() == null) { + markerSet = null; + portalIcon = null; + } else { + markerSet = dynmapAPI.getMarkerAPI().createMarkerSet("stargate", "Stargate", null, false); + if (markerSet != null) { + markerSet.setHideByDefault(Stargate.getStargateConfig().hideDynmapIcons()); + } + portalIcon = dynmapAPI.getMarkerAPI().getMarkerIcon("portal"); + } + } + + /** + * Adds all portal markers for all current portals + */ + public static void addAllPortalMarkers() { + if (markerSet == null || Stargate.getStargateConfig().isDynmapDisabled()) { + //Remove any existing markers if dynmap has been disabled after startup + if (markerSet != null) { + markerSet.getMarkers().forEach(GenericMarker::deleteMarker); + } + return; + } + markerSet.setHideByDefault(Stargate.getStargateConfig().hideDynmapIcons()); + //Remove all existing markers for a clean start + markerSet.getMarkers().forEach(GenericMarker::deleteMarker); + + for (Portal portal : PortalRegistry.getAllPortals()) { + addPortalMarker(portal); + } + } + + /** + * Adds a portal marker for the given portal + * + * @param portal

The portal to add a marker for

+ */ + public static void addPortalMarker(Portal portal) { + if (markerSet == null || Stargate.getStargateConfig().isDynmapDisabled()) { + return; + } + World world = portal.getWorld(); + if (portal.getOptions().isHidden() || world == null) { + return; + } + + Location location = portal.getBlockAt(portal.getGate().getLayout().getExit()); + Marker marker = markerSet.createMarker(getPortalMarkerId(portal), portal.getName(), world.getName(), + location.getX(), location.getY(), location.getZ(), portalIcon, false); + if (marker == null) { + Stargate.logWarning(String.format( + """ + Unable to create marker for portal + Portal marker id: %s + Portal name: %s + Portal world: %s + Portal location: %s,%s,%s""", + getPortalMarkerId(portal), portal.getName(), world.getName(), location.getX(), location.getY(), + location.getZ())); + return; + } + String networkPrompt; + if (portal.getOptions().isBungee()) { + networkPrompt = "Server"; + } else { + networkPrompt = "Network"; + } + String markerDescription = String.format("Name: %s
%s: %s
Destination: " + + "%s
Owner: %s
", portal.getName(), networkPrompt, portal.getNetwork(), + portal.getDestinationName(), portal.getOwner().getName()); + marker.setDescription(markerDescription); + marker.setLabel(portal.getName(), true); + if (portalIcon != null) { + marker.setMarkerIcon(portalIcon); + } + } + + /** + * Removes the portal marker for the given portal + * + * @param portal

The portal to remove the marker for

+ */ + public static void removePortalMarker(Portal portal) { + if (markerSet == null || Stargate.getStargateConfig().isDynmapDisabled()) { + return; + } + Marker marker = markerSet.findMarker(getPortalMarkerId(portal)); + if (marker != null) { + marker.deleteMarker(); + } + } + + /** + * Gets the id used for the given portal's marker + * + * @param portal

The portal to get a marker id for

+ * @return

+ */ + private static String getPortalMarkerId(Portal portal) { + return portal.getNetwork() + "-:-" + portal.getName(); + } + +} diff --git a/src/main/java/net/knarcraft/stargate/config/OptionDataType.java b/src/main/java/net/knarcraft/stargate/config/OptionDataType.java index 5cb8bee..25fcf76 100644 --- a/src/main/java/net/knarcraft/stargate/config/OptionDataType.java +++ b/src/main/java/net/knarcraft/stargate/config/OptionDataType.java @@ -6,17 +6,28 @@ package net.knarcraft.stargate.config; public enum OptionDataType { /** - * The data type for the option is a String + * The data type if the option is a String */ STRING, + /** - * The data type for the option is a Boolean + * The data type if the option is a Boolean */ BOOLEAN, - STRING_LIST, + /** - * The data type for the option is an Integer + * The data type if the option is a string list */ - INTEGER + STRING_LIST, + + /** + * The data type if the option is an Integer + */ + INTEGER, + + /** + * The data type if the option is a double + */ + DOUBLE } diff --git a/src/main/java/net/knarcraft/stargate/config/StargateConfig.java b/src/main/java/net/knarcraft/stargate/config/StargateConfig.java index 346bb4d..b718d8b 100644 --- a/src/main/java/net/knarcraft/stargate/config/StargateConfig.java +++ b/src/main/java/net/knarcraft/stargate/config/StargateConfig.java @@ -15,6 +15,7 @@ import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.plugin.messaging.Messenger; +import org.dynmap.DynmapAPI; import java.io.File; import java.io.IOException; @@ -101,6 +102,11 @@ public final class StargateConfig { //Set up vault economy if vault has been loaded setupVaultEconomy(); + DynmapAPI dynmapAPI = (DynmapAPI) Bukkit.getPluginManager().getPlugin("dynmap"); + if (dynmapAPI != null) { + DynmapManager.initialize(dynmapAPI); + DynmapManager.addAllPortalMarkers(); + } } /** @@ -152,6 +158,24 @@ public final class StargateConfig { return (boolean) configOptions.get(ConfigOption.PERMISSION_DEBUG); } + /** + * Gets whether Dynmap integration is disabled + * + * @return

Whether Dynmap integration is disabled

+ */ + public boolean isDynmapDisabled() { + return !((boolean) configOptions.get(ConfigOption.ENABLE_DYNMAP)); + } + + /** + * Gets whether Dynmap icons should be hidden by default + * + * @return

Whether Dynmap icons should be hidden by default

+ */ + public boolean hideDynmapIcons() { + return (boolean) configOptions.get(ConfigOption.DYNMAP_ICONS_DEFAULT_HIDDEN); + } + /** * Gets the object containing economy config values * @@ -189,6 +213,9 @@ public final class StargateConfig { startStopBungeeListener(stargateGateConfig.enableBungee()); } + //Reload portal markers + DynmapManager.addAllPortalMarkers(); + messageSender.sendErrorMessage(sender, languageLoader.getString("reloaded")); } @@ -358,6 +385,7 @@ public final class StargateConfig { } case BOOLEAN -> optionValue = newConfig.getBoolean(configNode); case INTEGER -> optionValue = newConfig.getInt(configNode); + case DOUBLE -> optionValue = newConfig.getDouble(configNode); default -> throw new IllegalArgumentException("Invalid config data type encountered"); } configOptions.put(option, optionValue); @@ -519,4 +547,5 @@ public final class StargateConfig { public LanguageLoader getLanguageLoader() { return languageLoader; } + } diff --git a/src/main/java/net/knarcraft/stargate/config/StargateGateConfig.java b/src/main/java/net/knarcraft/stargate/config/StargateGateConfig.java index 001ced9..053d4b6 100644 --- a/src/main/java/net/knarcraft/stargate/config/StargateGateConfig.java +++ b/src/main/java/net/knarcraft/stargate/config/StargateGateConfig.java @@ -16,6 +16,7 @@ import java.util.Map; * The Stargate gate config keeps track of all global config values related to gates */ public final class StargateGateConfig { + private static final int activeTime = 10; private static final int openTime = 10; private final Map configOptions; @@ -125,6 +126,29 @@ public final class StargateGateConfig { return (boolean) configOptions.get(ConfigOption.HANDLE_LEASHED_CREATURES); } + /** + * Gets whether the CraftBook vehicle removal fix is enabled + * + *

If enabled, minecarts and boats should be re-created instead of teleported.

+ * + * @return

True if the CraftBook vehicle removal fix is enabled

+ */ + 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

The delay to use before adding a player as passenger of a teleported vehicle

+ */ + public int waitForPlayerAfterTeleportDelay() { + if ((int) configOptions.get(ConfigOption.WAIT_FOR_PLAYER_AFTER_TELEPORT_DELAY) < 2) { + configOptions.put(ConfigOption.WAIT_FOR_PLAYER_AFTER_TELEPORT_DELAY, 6); + } + return (int) configOptions.get(ConfigOption.WAIT_FOR_PLAYER_AFTER_TELEPORT_DELAY); + } + /** * Gets whether the list of destinations within a network should be sorted * @@ -179,6 +203,15 @@ public final class StargateGateConfig { return (String) configOptions.get(ConfigOption.DEFAULT_GATE_NETWORK); } + /** + * Gets the exit velocity of players using stargates, relative to the entry velocity + * + * @return

The relative exit velocity

+ */ + public double getExitVelocity() { + return (double) configOptions.get(ConfigOption.EXIT_VELOCITY); + } + /** * Loads all config values related to gates */ @@ -253,8 +286,8 @@ public final class StargateGateConfig { if (colors[colorIndex].equalsIgnoreCase("inverted")) { //Convert from ChatColor to awt.Color to Bukkit.Color then invert and convert to ChatColor java.awt.Color color = defaultColors[colorIndex].getColor(); - parsedColor = ColorHelper.fromColor(ColorHelper.invert(Color.fromRGB(color.getRed(), - color.getGreen(), color.getBlue()))); + parsedColor = ColorHelper.fromColor(ColorHelper.invert(Color.fromRGB(color.getRed(), color.getGreen(), + color.getBlue()))); } else { try { parsedColor = ChatColor.of(colors[colorIndex]); @@ -291,4 +324,5 @@ public final class StargateGateConfig { PortalSignDrawer.setHighlightColor(ChatColor.WHITE); } } + } diff --git a/src/main/java/net/knarcraft/stargate/container/RelativeBlockVector.java b/src/main/java/net/knarcraft/stargate/container/RelativeBlockVector.java index 81bddef..8cb6942 100644 --- a/src/main/java/net/knarcraft/stargate/container/RelativeBlockVector.java +++ b/src/main/java/net/knarcraft/stargate/container/RelativeBlockVector.java @@ -60,16 +60,11 @@ public class RelativeBlockVector { * @return

A new relative block vector with the property altered

*/ public RelativeBlockVector addToVector(Property propertyToAddTo, int valueToAdd) { - switch (propertyToAddTo) { - case RIGHT: - return new RelativeBlockVector(this.right + valueToAdd, this.down, this.out); - case DOWN: - return new RelativeBlockVector(this.right, this.down + valueToAdd, this.out); - case OUT: - return new RelativeBlockVector(this.right, this.down, this.out + valueToAdd); - default: - throw new IllegalArgumentException("Invalid relative block vector property given"); - } + return switch (propertyToAddTo) { + case RIGHT -> new RelativeBlockVector(this.right + valueToAdd, this.down, this.out); + case DOWN -> new RelativeBlockVector(this.right, this.down + valueToAdd, this.out); + case OUT -> new RelativeBlockVector(this.right, this.down, this.out + valueToAdd); + }; } /** diff --git a/src/main/java/net/knarcraft/stargate/event/StargateEntityPortalEvent.java b/src/main/java/net/knarcraft/stargate/event/StargateEntityPortalEvent.java index 0cf3448..cd34504 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargateEntityPortalEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargateEntityPortalEvent.java @@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull; *

This event can be used to overwrite the location the entity is teleported to.

*/ @SuppressWarnings("unused") -public class StargateEntityPortalEvent extends StargateEvent { +public class StargateEntityPortalEvent extends StargateEvent implements StargateTeleportEvent { private static final HandlerList handlers = new HandlerList(); final Entity travellingEntity; @@ -58,6 +58,7 @@ public class StargateEntityPortalEvent extends StargateEvent { * * @return

Location of the exit point

*/ + @Override public Location getExit() { return exit; } diff --git a/src/main/java/net/knarcraft/stargate/event/StargatePlayerPortalEvent.java b/src/main/java/net/knarcraft/stargate/event/StargatePlayerPortalEvent.java index 05d474b..3d2526a 100644 --- a/src/main/java/net/knarcraft/stargate/event/StargatePlayerPortalEvent.java +++ b/src/main/java/net/knarcraft/stargate/event/StargatePlayerPortalEvent.java @@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull; *

This event can be used to overwrite the location the player is teleported to.

*/ @SuppressWarnings("unused") -public class StargatePlayerPortalEvent extends StargatePlayerEvent { +public class StargatePlayerPortalEvent extends StargatePlayerEvent implements StargateTeleportEvent { private static final HandlerList handlers = new HandlerList(); private final Portal destination; @@ -47,6 +47,7 @@ public class StargatePlayerPortalEvent extends StargatePlayerEvent { * * @return

Location of the exit point

*/ + @Override public Location getExit() { return exit; } diff --git a/src/main/java/net/knarcraft/stargate/event/StargateTeleportEvent.java b/src/main/java/net/knarcraft/stargate/event/StargateTeleportEvent.java new file mode 100644 index 0000000..e440828 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/event/StargateTeleportEvent.java @@ -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

Location of the exit point

+ */ + Location getExit(); + +} diff --git a/src/main/java/net/knarcraft/stargate/listener/EntitySpawnListener.java b/src/main/java/net/knarcraft/stargate/listener/EntitySpawnListener.java new file mode 100644 index 0000000..b4c0b2c --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/listener/EntitySpawnListener.java @@ -0,0 +1,25 @@ +package net.knarcraft.stargate.listener; + +import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.portal.PortalHandler; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.CreatureSpawnEvent; + +/** + * A listener that listens for any relevant events causing entities to spawn + */ +public class EntitySpawnListener implements Listener { + + @EventHandler + public void onCreatureSpawn(CreatureSpawnEvent event) { + //Prevent Zombified Piglins and other creatures form spawning at stargates + if (event.getSpawnReason() == CreatureSpawnEvent.SpawnReason.NETHER_PORTAL) { + if (PortalHandler.getByEntrance(event.getLocation()) != null) { + event.setCancelled(true); + Stargate.debug("EntitySpawnListener", "Prevented creature from spawning at Stargate"); + } + } + } + +} diff --git a/src/main/java/net/knarcraft/stargate/listener/PlayerEventListener.java b/src/main/java/net/knarcraft/stargate/listener/PlayerEventListener.java index b182690..82438a8 100644 --- a/src/main/java/net/knarcraft/stargate/listener/PlayerEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/PlayerEventListener.java @@ -7,16 +7,17 @@ import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.PortalActivator; import net.knarcraft.stargate.portal.PortalHandler; 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.utility.BungeeHelper; import net.knarcraft.stargate.utility.MaterialHelper; import net.knarcraft.stargate.utility.PermissionHelper; +import net.knarcraft.stargate.utility.TeleportHelper; import net.knarcraft.stargate.utility.UUIDMigrationHelper; import net.knarcraft.stargate.utility.UpdateChecker; import net.md_5.bungee.api.ChatColor; import org.bukkit.Bukkit; import org.bukkit.GameMode; +import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.data.type.WallSign; import org.bukkit.entity.AbstractHorse; @@ -33,6 +34,10 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +import java.util.HashMap; +import java.util.Map; /** * This listener listens to any player-related events related to stargates @@ -40,8 +45,7 @@ import org.bukkit.inventory.ItemStack; @SuppressWarnings("unused") public class PlayerEventListener implements Listener { - private static long eventTime; - private static PlayerInteractEvent previousEvent; + private static final Map previousEventTimes = new HashMap<>(); /** * This event handler handles detection of any player teleporting through a bungee gate @@ -101,6 +105,11 @@ public class PlayerEventListener implements Listener { return; } Portal entrancePortal = PortalHandler.getByEntrance(toLocation); + //Check an additional block away in case the portal is a bungee portal using END_PORTAL + if (entrancePortal == null) { + entrancePortal = PortalHandler.getByAdjacentEntrance(toLocation); + } + Portal destination = entrancePortal.getPortalActivator().getDestination(player); Entity playerVehicle = player.getVehicle(); @@ -129,10 +138,11 @@ public class PlayerEventListener implements Listener { horse.setOwner(player); } //Teleport the player's vehicle - new VehicleTeleporter(destination, (Vehicle) playerVehicle).teleport(entrancePortal); + player.setVelocity(new Vector()); + new VehicleTeleporter(destination, (Vehicle) playerVehicle).teleportEntity(entrancePortal); } else { //Just teleport the player like normal - new PlayerTeleporter(destination, player).teleport(entrancePortal, event); + new PlayerTeleporter(destination, player).teleportPlayer(entrancePortal, event); } if (!entrancePortal.getOptions().isSilent()) { Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg")); @@ -159,7 +169,12 @@ public class PlayerEventListener implements Listener { //Check if the player moved from a portal Portal entrancePortal = PortalHandler.getByEntrance(toLocation); if (entrancePortal == null) { - return false; + //Check an additional block away for BungeeCord portals using END_PORTAL as its material + entrancePortal = PortalHandler.getByAdjacentEntrance(toLocation); + if (entrancePortal == null || !entrancePortal.getOptions().isBungee() || + entrancePortal.getGate().getPortalOpenBlock() != Material.END_PORTAL) { + return false; + } } Portal destination = entrancePortal.getPortalActivator().getDestination(player); @@ -183,7 +198,7 @@ public class PlayerEventListener implements Listener { } //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); } /** @@ -226,11 +241,14 @@ public class PlayerEventListener implements Listener { EquipmentSlot hand = event.getHand(); if (hand != null && (PermissionHelper.hasPermission(player, "stargate.admin.dye") || portal.isOwner(player))) { - String itemName = player.getInventory().getItem(hand).getType().toString(); - if (itemName.endsWith("DYE") || itemName.endsWith("INK_SAC")) { - event.setUseInteractedBlock(Event.Result.ALLOW); - Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), portal::drawSign, 1); - return; + ItemStack item = player.getInventory().getItem(hand); + if (item != null) { + String itemName = item.getType().toString(); + if (itemName.endsWith("DYE") || itemName.endsWith("INK_SAC")) { + event.setUseInteractedBlock(Event.Result.ALLOW); + Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), portal::drawSign, 1); + return; + } } } @@ -295,7 +313,7 @@ public class PlayerEventListener implements Listener { } //Prevent a double click caused by a Spigot bug - if (clickIsBug(event, block)) { + if (clickIsBug(event.getPlayer(), block)) { return; } @@ -321,7 +339,7 @@ public class PlayerEventListener implements Listener { } else { //Display information about the portal if it has no sign ItemStack heldItem = player.getInventory().getItem(hand); - if (heldItem.getType().isAir() || !heldItem.getType().isBlock()) { + if (heldItem != null && (heldItem.getType().isAir() || !heldItem.getType().isBlock())) { displayPortalInfo(block, player); } } @@ -366,19 +384,17 @@ public class PlayerEventListener implements Listener { * immediately, or causing portal information printing twice. This fix should detect the bug without breaking * clicking once the bug is fixed.

* - * @param event

The event causing the right click

- * @param block

The block to check

+ * @param player

The player performing the right-click

+ * @param block

The block to check

* @return

True if the click is a bug and should be cancelled

*/ - private boolean clickIsBug(PlayerInteractEvent event, Block block) { - if (previousEvent != null && - event.getPlayer() == previousEvent.getPlayer() && eventTime + 15 > System.currentTimeMillis()) { - previousEvent = null; - eventTime = 0; + private boolean clickIsBug(Player player, Block block) { + Long previousEventTime = previousEventTimes.get(player); + if (previousEventTime != null && previousEventTime + 50 > System.currentTimeMillis()) { + previousEventTimes.put(player, null); return true; } - previousEvent = event; - eventTime = System.currentTimeMillis(); + previousEventTimes.put(player, System.currentTimeMillis()); return false; } diff --git a/src/main/java/net/knarcraft/stargate/listener/PluginEventListener.java b/src/main/java/net/knarcraft/stargate/listener/PluginEventListener.java index a6e0dba..2f70de3 100644 --- a/src/main/java/net/knarcraft/stargate/listener/PluginEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/PluginEventListener.java @@ -49,4 +49,5 @@ public class PluginEventListener implements Listener { Stargate.logInfo("Vault plugin lost."); } } + } diff --git a/src/main/java/net/knarcraft/stargate/listener/PortalEventListener.java b/src/main/java/net/knarcraft/stargate/listener/PortalEventListener.java index 8510b9e..c81640f 100644 --- a/src/main/java/net/knarcraft/stargate/listener/PortalEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/PortalEventListener.java @@ -6,6 +6,7 @@ import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.PortalHandler; import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter; import net.knarcraft.stargate.utility.PermissionHelper; +import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; @@ -67,14 +68,23 @@ public class PortalEventListener implements Listener { return; } + Stargate.debug("PortalEventListener::onEntityPortalEnter", + "Found player " + player + " entering END_PORTAL " + portal); + //Remove any old player teleportations in case weird things happen playersFromTheEnd.removeIf((teleportation -> teleportation.getPlayer() == player)); //Decide if the anything stops the player from teleporting - if (PermissionHelper.playerCannotTeleport(portal, portal.getPortalActivator().getDestination(), player, null)) { + if (PermissionHelper.playerCannotTeleport(portal, portal.getPortalActivator().getDestination(), player, null) || + portal.getOptions().isBungee()) { //Teleport the player back to the portal they came in, just in case playersFromTheEnd.add(new FromTheEndTeleportation(player, portal)); + Stargate.debug("PortalEventListener::onEntityPortalEnter", + "Sending player back to the entrance"); + } else { + playersFromTheEnd.add(new FromTheEndTeleportation(player, portal.getPortalActivator().getDestination())); + Stargate.debug("PortalEventListener::onEntityPortalEnter", + "Sending player to destination"); } - playersFromTheEnd.add(new FromTheEndTeleportation(player, portal.getPortalActivator().getDestination())); } } @@ -95,10 +105,18 @@ public class PortalEventListener implements Listener { Portal exitPortal = teleportation.getExit(); //Overwrite respawn location to respawn in front of the portal - event.setRespawnLocation(new PlayerTeleporter(exitPortal, respawningPlayer).getExit(respawningPlayer, - respawningPlayer.getLocation())); + PlayerTeleporter teleporter = new PlayerTeleporter(exitPortal, respawningPlayer); + Location respawnLocation = teleporter.getExit(); + event.setRespawnLocation(respawnLocation); + //Try and force the player if for some reason the changing of respawn location isn't properly handled + Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), () -> + respawningPlayer.teleport(respawnLocation), 1); + //Properly close the portal to prevent it from staying in a locked state until it times out exitPortal.getPortalOpener().closePortal(false); + + Stargate.debug("PortalEventListener::onRespawn", "Overwriting respawn for " + respawningPlayer + + " to " + respawnLocation.getWorld() + ":" + respawnLocation); } } diff --git a/src/main/java/net/knarcraft/stargate/listener/VehicleEventListener.java b/src/main/java/net/knarcraft/stargate/listener/VehicleEventListener.java index 0b263a2..42c1102 100644 --- a/src/main/java/net/knarcraft/stargate/listener/VehicleEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/VehicleEventListener.java @@ -3,11 +3,10 @@ package net.knarcraft.stargate.listener; import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.PortalHandler; -import net.knarcraft.stargate.portal.teleporter.Teleporter; import net.knarcraft.stargate.portal.teleporter.VehicleTeleporter; import net.knarcraft.stargate.utility.EconomyHelper; 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.Player; import org.bukkit.entity.Vehicle; @@ -62,11 +61,11 @@ public class VehicleEventListener implements Listener { private static void teleportVehicle(List passengers, Portal entrancePortal, Vehicle vehicle) { String route = "VehicleEventListener::teleportVehicle"; - if (!passengers.isEmpty() && passengers.get(0) instanceof Player) { + if (!passengers.isEmpty() && TeleportHelper.containsPlayer(passengers)) { Stargate.debug(route, "Found passenger vehicle"); - teleportPlayerAndVehicle(entrancePortal, vehicle, passengers); + teleportPlayerAndVehicle(entrancePortal, vehicle); } else { - Stargate.debug(route, "Found empty vehicle"); + Stargate.debug(route, "Found vehicle without players"); Portal destinationPortal = entrancePortal.getPortalActivator().getDestination(); if (destinationPortal == null) { Stargate.debug(route, "Unable to find portal destination"); @@ -74,7 +73,7 @@ public class VehicleEventListener implements Listener { } Stargate.debug("vehicleTeleport", destinationPortal.getWorld() + " " + 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

The portal the minecart entered

* @param vehicle

The vehicle to teleport

- * @param passengers

Any entities sitting in the minecart

*/ - private static void teleportPlayerAndVehicle(Portal entrancePortal, Vehicle vehicle, List passengers) { - Player player = (Player) passengers.get(0); - //On the assumption that a non-player cannot sit in the driver's seat and since some portals can only be open - // to one player at a time, we only need to check if the portal is open to the driver. - if (!entrancePortal.getPortalOpener().isOpenFor(player)) { - if (!entrancePortal.getOptions().isSilent()) { - Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); + private static void teleportPlayerAndVehicle(Portal entrancePortal, Vehicle vehicle) { + Entity rootEntity = vehicle; + while (rootEntity.getVehicle() != null) { + rootEntity = rootEntity.getVehicle(); + } + List players = TeleportHelper.getPlayers(rootEntity.getPassengers()); + 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 - Portal destinationPortal = entrancePortal.getPortalActivator().getDestination(player); - if (destinationPortal == null) { - return; - } - - //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; + //Check if any of the players has selected the destination + Portal possibleDestinationPortal = entrancePortal.getPortalActivator().getDestination(player); + if (possibleDestinationPortal != null) { + destinationPortal = possibleDestinationPortal; } } - //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 (!takePlayerPayment(passengers, entrancePortal, cost)) { - return; + //Cancel the teleport if no players activated the portal, or if any players are denied access + boolean cancelTeleport = false; + for (Player player : players) { + if (destinationPortal == null) { + cancelTeleport = true; + 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 - boolean teleported = new VehicleTeleporter(destinationPortal, vehicle).teleport(entrancePortal); + boolean teleported = new VehicleTeleporter(destinationPortal, vehicle).teleportEntity(entrancePortal); if (teleported) { 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); } } - /** - * Takes payment from all player passengers - * - * @param passengers

All passengers in the teleporting vehicle

- * @param entrancePortal

The portal the vehicle is entering from

- * @param cost

The cost each player has to pay

- * @return

True if all player passengers paid successfully

- */ - private static boolean takePlayerPayment(List 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

The player trying to teleport

- * @param entrancePortal

The portal the player is entering

- * @param destinationPortal

The portal the player is to exit from

- * @return

True if the player is allowed to teleport and is able to pay necessary fees

- */ - 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); - } - } diff --git a/src/main/java/net/knarcraft/stargate/listener/WorldEventListener.java b/src/main/java/net/knarcraft/stargate/listener/WorldEventListener.java index bd4ccc5..6329d6d 100644 --- a/src/main/java/net/knarcraft/stargate/listener/WorldEventListener.java +++ b/src/main/java/net/knarcraft/stargate/listener/WorldEventListener.java @@ -46,4 +46,5 @@ public class WorldEventListener implements Listener { PortalRegistry.clearPortals(world); } } + } diff --git a/src/main/java/net/knarcraft/stargate/portal/Portal.java b/src/main/java/net/knarcraft/stargate/portal/Portal.java index 9af1838..6c7db86 100644 --- a/src/main/java/net/knarcraft/stargate/portal/Portal.java +++ b/src/main/java/net/knarcraft/stargate/portal/Portal.java @@ -349,4 +349,5 @@ public class Portal { return cleanNetwork.equalsIgnoreCase(other.cleanNetwork); } } + } diff --git a/src/main/java/net/knarcraft/stargate/portal/PortalHandler.java b/src/main/java/net/knarcraft/stargate/portal/PortalHandler.java index cabff97..44ffb81 100644 --- a/src/main/java/net/knarcraft/stargate/portal/PortalHandler.java +++ b/src/main/java/net/knarcraft/stargate/portal/PortalHandler.java @@ -448,4 +448,5 @@ public class PortalHandler { } return input.replaceAll("[|:#]", "").trim(); } + } diff --git a/src/main/java/net/knarcraft/stargate/portal/PortalRegistry.java b/src/main/java/net/knarcraft/stargate/portal/PortalRegistry.java index 74faa96..fe07562 100644 --- a/src/main/java/net/knarcraft/stargate/portal/PortalRegistry.java +++ b/src/main/java/net/knarcraft/stargate/portal/PortalRegistry.java @@ -1,6 +1,7 @@ package net.knarcraft.stargate.portal; import net.knarcraft.stargate.Stargate; +import net.knarcraft.stargate.config.DynmapManager; import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.utility.PortalFileHelper; import org.bukkit.World; @@ -224,6 +225,7 @@ public class PortalRegistry { PortalFileHelper.saveAllPortals(portal.getWorld()); portal.setRegistered(false); + DynmapManager.removePortalMarker(portal); } /** @@ -289,6 +291,7 @@ public class PortalRegistry { allPortals.add(portal); portal.setRegistered(true); + DynmapManager.addPortalMarker(portal); } } diff --git a/src/main/java/net/knarcraft/stargate/portal/teleporter/EntityTeleporter.java b/src/main/java/net/knarcraft/stargate/portal/teleporter/EntityTeleporter.java index df12f74..e963c92 100644 --- a/src/main/java/net/knarcraft/stargate/portal/teleporter/EntityTeleporter.java +++ b/src/main/java/net/knarcraft/stargate/portal/teleporter/EntityTeleporter.java @@ -1,9 +1,7 @@ package net.knarcraft.stargate.portal.teleporter; -import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.event.StargateEntityPortalEvent; import net.knarcraft.stargate.portal.Portal; -import org.bukkit.Location; import org.bukkit.entity.Entity; /** @@ -16,10 +14,10 @@ public class EntityTeleporter extends Teleporter { /** * Instantiates a new portal teleporter * - * @param portal

The portal which is the target of the teleportation

+ * @param targetPortal

The portal which is the target of the teleportation

*/ - public EntityTeleporter(Portal portal, Entity teleportingEntity) { - super(portal); + public EntityTeleporter(Portal targetPortal, Entity teleportingEntity) { + super(targetPortal, teleportingEntity); this.teleportingEntity = teleportingEntity; } @@ -29,44 +27,8 @@ public class EntityTeleporter extends Teleporter { * @param origin

The portal the entity is teleporting from

* @return

True if the entity was teleported. False otherwise

*/ - public boolean teleport(Portal origin) { - Location traveller = teleportingEntity.getLocation(); - 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; + public boolean teleportEntity(Portal origin) { + return teleport(origin, new StargateEntityPortalEvent(teleportingEntity, origin, portal, exit)); } - /** - * Triggers the entity portal event to allow plugins to change the exit location - * - * @param origin

The origin portal teleported from

- * @param exit

The exit location to teleport the entity to

- * @return

The location the entity should be teleported to, or null if the event was cancelled

- */ - 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(); - } } diff --git a/src/main/java/net/knarcraft/stargate/portal/teleporter/PlayerTeleporter.java b/src/main/java/net/knarcraft/stargate/portal/teleporter/PlayerTeleporter.java index e8232bb..3460340 100644 --- a/src/main/java/net/knarcraft/stargate/portal/teleporter/PlayerTeleporter.java +++ b/src/main/java/net/knarcraft/stargate/portal/teleporter/PlayerTeleporter.java @@ -3,9 +3,15 @@ package net.knarcraft.stargate.portal.teleporter; import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.event.StargatePlayerPortalEvent; import net.knarcraft.stargate.portal.Portal; -import org.bukkit.Location; +import net.knarcraft.stargate.utility.DirectionHelper; +import net.knarcraft.stargate.utility.TeleportHelper; +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.util.Vector; + +import java.util.List; /** * The portal teleporter takes care of the actual portal teleportation for any players @@ -17,11 +23,11 @@ public class PlayerTeleporter extends Teleporter { /** * Instantiates a new player teleporter * - * @param portal

The portal which is the target of the teleportation

- * @param player

The teleporting player

+ * @param targetPortal

The portal which is the target of the teleportation

+ * @param player

The teleporting player

*/ - public PlayerTeleporter(Portal portal, Player player) { - super(portal); + public PlayerTeleporter(Portal targetPortal, Player player) { + super(targetPortal, player); this.player = player; } @@ -31,53 +37,42 @@ public class PlayerTeleporter extends Teleporter { * @param origin

The portal the player teleports from

* @param event

The player move event triggering the event

*/ - public void teleport(Portal origin, PlayerMoveEvent event) { - Location traveller = player.getLocation(); - Location exit = getExit(player, traveller); - - //Rotate the player to face out from the portal - adjustRotation(exit); + public void teleportPlayer(Portal origin, PlayerMoveEvent event) { + double velocity = player.getVelocity().length(); + List passengers = player.getPassengers(); //Call the StargatePlayerPortalEvent to allow plugins to change destination if (!origin.equals(portal)) { - exit = triggerPlayerPortalEvent(origin, exit, event); + exit = triggerPortalEvent(origin, new StargatePlayerPortalEvent(player, origin, portal, exit)); if (exit == null) { return; } } + //Calculate the exit velocity of the player + Vector newVelocityDirection = DirectionHelper.getDirectionVectorFromYaw(portal.getYaw()); + Vector newVelocity = newVelocityDirection.multiply(velocity * Stargate.getGateConfig().getExitVelocity()); + //Load chunks to make sure not to teleport to the void loadChunks(); //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(), newVelocity); + } //If no event is passed in, assume it's a teleport, and act as such if (event == null) { player.teleport(exit); } else { - //The new method to teleport in a move event is set the "to" field. + //Set the exit location of the event event.setTo(exit); } - } - /** - * Triggers the player portal event to allow plugins to change the exit location - * - * @param origin

The origin portal teleported from

- * @param exit

The exit location to teleport the player to

- * @param event

The player move event which triggered the teleportation

- * @return

The location the player should be teleported to, or null if the event was cancelled

- */ - 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(); + //Set the velocity of the teleported player after the teleportation is finished + Bukkit.getScheduler().scheduleSyncDelayedTask(Stargate.getInstance(), () -> player.setVelocity(newVelocity), 1); } } diff --git a/src/main/java/net/knarcraft/stargate/portal/teleporter/Teleporter.java b/src/main/java/net/knarcraft/stargate/portal/teleporter/Teleporter.java index 5ffd9f4..7e1f7ee 100644 --- a/src/main/java/net/knarcraft/stargate/portal/teleporter/Teleporter.java +++ b/src/main/java/net/knarcraft/stargate/portal/teleporter/Teleporter.java @@ -4,9 +4,11 @@ import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.container.BlockLocation; import net.knarcraft.stargate.container.ChunkUnloadRequest; import net.knarcraft.stargate.container.RelativeBlockVector; +import net.knarcraft.stargate.event.StargateTeleportEvent; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.utility.DirectionHelper; import net.knarcraft.stargate.utility.EntityHelper; +import net.knarcraft.stargate.utility.TeleportHelper; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; @@ -14,10 +16,10 @@ import org.bukkit.block.data.Bisected; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.Slab; import org.bukkit.entity.AbstractHorse; -import org.bukkit.entity.Creature; import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; +import org.bukkit.event.Event; import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.util.Vector; import java.util.ArrayList; import java.util.List; @@ -31,71 +33,114 @@ public abstract class Teleporter { * The portal the entity is teleporting to */ protected final Portal portal; + /** * The scheduler to use for delaying tasks */ 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 * - * @param portal

The portal which is the target of the teleportation

+ * @param portal

The portal which is the target of the teleportation

+ * @param teleportedEntity

The entity teleported by this teleporter

*/ - public Teleporter(Portal portal) { + public Teleporter(Portal portal, Entity teleportedEntity) { this.portal = portal; this.scheduler = Stargate.getInstance().getServer().getScheduler(); + this.teleportedEntity = teleportedEntity; + this.exit = getExit(teleportedEntity); } + /** + * Teleports an entity + * + * @param origin

The portal the entity teleported from

+ * @param stargateTeleportEvent

The event to call to make sure the teleportation is valid

+ * @return

True if the teleportation was successfully performed

+ */ + public boolean teleport(Portal origin, StargateTeleportEvent stargateTeleportEvent) { + List 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(), + new Vector()); + } + teleportedEntity.teleport(exit); + return true; + } + + /** + * Gets the exit location of this teleporter + * + * @return

The exit location of this teleporter

+ */ + public Location getExit() { + return exit.clone(); + } + + /** + * Triggers the entity portal event to allow plugins to change the exit location + * + * @param origin

The origin portal teleported from

+ * @param stargateTeleportEvent

The exit location to teleport the entity to

+ * @return

The location the entity should be teleported to, or null if the event was cancelled

+ */ + 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 * * @param exit

The location the entity will exit from

*/ - protected void adjustRotation(Location exit) { + protected void adjustExitLocationRotation(Location exit) { int adjust = 0; if (portal.getOptions().isBackwards()) { adjust = 180; } float newYaw = (portal.getYaw() + adjust) % 360; Stargate.debug("Portal::adjustRotation", "Setting exit yaw to " + newYaw); - exit.setYaw(newYaw); + exit.setDirection(DirectionHelper.getDirectionVectorFromYaw(newYaw)); } /** - * Gets the exit location for a given entity and current location - * - * @param entity

The entity to teleport (used to determine distance from portal to avoid suffocation)

- * @param traveller

The location of the entity travelling

- * @return

The location the entity should be teleported to.

+ * Loads the chunks outside the portal's entrance */ - public Location getExit(Entity entity, Location traveller) { - Location exitLocation = null; - RelativeBlockVector relativeExit = portal.getGate().getLayout().getExit(); - if (relativeExit != null) { - 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())); + protected void loadChunks() { + for (Chunk chunk : getChunksToLoad()) { + chunk.addPluginChunkTicket(Stargate.getInstance()); + //Allow the chunk to unload after 10 seconds + Stargate.addChunkUnloadRequest(new ChunkUnloadRequest(chunk, 10000L)); } - - //Adjust pitch and height - return adjustExitLocation(traveller, exitLocation); } /** @@ -141,7 +186,7 @@ public abstract class Teleporter { if (entitySize > 1) { double entityOffset; if (portal.getOptions().isAlwaysOn()) { - entityOffset = entityBoxSize / 2D; + entityOffset = (entityBoxSize / 2D); } else { entityOffset = (entitySize / 2D) - 1; } @@ -185,41 +230,62 @@ public abstract class Teleporter { * 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.

* - * @param traveller

The location of the travelling entity

+ * @param entity

The travelling entity

* @param exitLocation

The exit location generated

* @return

The location the travelling entity should be teleported to

*/ - private Location adjustExitLocation(Location traveller, Location exitLocation) { + private Location adjustExitLocationHeight(Entity entity, Location exitLocation) { if (exitLocation != null) { BlockData blockData = exitLocation.getBlock().getBlockData(); if ((blockData instanceof Bisected bisected && bisected.getHalf() == Bisected.Half.BOTTOM) || - (blockData instanceof Slab slab && slab.getType() == Slab.Type.BOTTOM)) { - //Prevent traveller from spawning inside a slab - Stargate.debug("adjustExitLocation", "Added a block to get above a slab"); - exitLocation.add(0, 1, 0); - } 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"); + (blockData instanceof Slab slab && slab.getType() == Slab.Type.BOTTOM) || + blockData.getMaterial() == Material.WATER) { + //Prevent traveller from spawning inside a slab, or a boat from spawning inside water + Stargate.debug("adjustExitLocation", "Added a block to get above a slab or a block of water"); exitLocation.add(0, 1, 0); } - - exitLocation.setPitch(traveller.getPitch()); return exitLocation; } else { 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

The entity to teleport (used to determine distance from portal to avoid suffocation)

+ * @return

The location the entity should be teleported to.

*/ - protected void loadChunks() { - for (Chunk chunk : getChunksToLoad()) { - chunk.addPluginChunkTicket(Stargate.getInstance()); - //Allow the chunk to unload after 3 seconds - Stargate.addChunkUnloadRequest(new ChunkUnloadRequest(chunk, 3000L)); + private Location getExit(Entity entity) { + Location exitLocation = null; + RelativeBlockVector relativeExit = portal.getGate().getLayout().getExit(); + if (relativeExit != null) { + 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 +316,4 @@ public abstract class Teleporter { return chunksToLoad; } - /** - * Checks whether a player has leashed creatures that block the teleportation - * - * @param player

The player trying to teleport

- * @return

False if the player has leashed any creatures that cannot go through the portal

- */ - public static boolean noLeashedCreaturesPreventTeleportation(Player player) { - //Find any nearby leashed entities to teleport with the player - List 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 - * - *

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.

- * - * @param player

The player which is teleported

- * @param origin

The portal the player is teleporting from

- */ - 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 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

The player to check

- * @return

A list of all creatures the player is holding in a leash (lead)

- */ - protected static List getLeashedCreatures(Player player) { - List leashedCreatures = new ArrayList<>(); - //Find any nearby leashed entities to teleport with the player - List 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; - } - } diff --git a/src/main/java/net/knarcraft/stargate/portal/teleporter/VehicleTeleporter.java b/src/main/java/net/knarcraft/stargate/portal/teleporter/VehicleTeleporter.java index 87c3288..20d4353 100644 --- a/src/main/java/net/knarcraft/stargate/portal/teleporter/VehicleTeleporter.java +++ b/src/main/java/net/knarcraft/stargate/portal/teleporter/VehicleTeleporter.java @@ -2,14 +2,17 @@ package net.knarcraft.stargate.portal.teleporter; import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.config.StargateGateConfig; +import net.knarcraft.stargate.event.StargateEntityPortalEvent; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.utility.DirectionHelper; +import net.knarcraft.stargate.utility.TeleportHelper; import org.bukkit.Location; import org.bukkit.World; +import org.bukkit.entity.Boat; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; import org.bukkit.entity.Vehicle; +import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.util.Vector; import java.util.List; @@ -24,11 +27,11 @@ public class VehicleTeleporter extends EntityTeleporter { /** * Instantiates a new vehicle teleporter * - * @param portal

The portal which is the target of the teleportation

+ * @param targetPortal

The targetPortal which is the target of the teleportation

* @param teleportingVehicle

The teleporting vehicle

*/ - public VehicleTeleporter(Portal portal, Vehicle teleportingVehicle) { - super(portal, teleportingVehicle); + public VehicleTeleporter(Portal targetPortal, Vehicle teleportingVehicle) { + super(targetPortal, teleportingVehicle); this.teleportingVehicle = teleportingVehicle; } @@ -42,28 +45,22 @@ public class VehicleTeleporter extends EntityTeleporter { * @return

True if the vehicle was teleported. False otherwise

*/ @Override - public boolean teleport(Portal origin) { - Location traveller = teleportingVehicle.getLocation(); - Location exit = getExit(teleportingVehicle, traveller); + public boolean teleportEntity(Portal origin) { + Stargate.debug("VehicleTeleporter::teleport", "Preparing to teleport: " + teleportingVehicle); double velocity = teleportingVehicle.getVelocity().length(); - //Stop and teleport + //Stop the vehicle before teleporting teleportingVehicle.setVelocity(new Vector()); //Get new velocity Vector newVelocityDirection = DirectionHelper.getDirectionVectorFromYaw(portal.getYaw()); 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 - if (!origin.equals(portal)) { - exit = triggerEntityPortalEvent(origin, exit); - if (exit == null) { - return false; - } + exit = triggerPortalEvent(origin, new StargateEntityPortalEvent(teleportingVehicle, origin, portal, exit)); + if (exit == null) { + return false; } //Teleport the vehicle @@ -89,12 +86,13 @@ public class VehicleTeleporter extends EntityTeleporter { return false; } - if (!(teleportingVehicle instanceof LivingEntity)) { + if (!(teleportingVehicle instanceof LivingEntity) && + Stargate.getGateConfig().enableCraftBookRemoveOnEjectFix()) { //Teleport a normal vehicle with passengers (minecart or boat) putPassengersInNewVehicle(passengers, exit, newVelocity, origin); } else { //Teleport a living vehicle with passengers (pig, horse, donkey, strider) - teleportLivingVehicle(exit, passengers, origin); + teleportVehicle(passengers, exit, newVelocity, origin); } } else { //Check if teleportation of empty vehicles is enabled @@ -118,54 +116,35 @@ public class VehicleTeleporter extends EntityTeleporter { private boolean vehiclePassengersAllowed(List passengers) { StargateGateConfig config = Stargate.getGateConfig(); //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; } //Don't teleport if the player does not contain a player and non-player vehicles is disabled - return containsPlayer(passengers) || config.handleNonPlayerVehicles(); - } - - /** - * Checks whether a list of entities contains any non-players - * - * @param entities

The list of entities to check

- * @return

True if at least one entity is not a player

- */ - private boolean containsNonPlayer(List 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

The list of entities to check

- * @return

True if at least one player is present among the passengers

- */ - private boolean containsPlayer(List entities) { - for (Entity entity : entities) { - if (entity instanceof Player) { - return true; - } - } - return false; + return TeleportHelper.containsPlayer(passengers) || config.handleNonPlayerVehicles(); } /** * Teleport a vehicle which is not a minecart or a boat * - * @param exit

The location the vehicle will exit

- * @param passengers

The passengers of the vehicle

- * @param origin

The portal the vehicle teleported from

+ * @param passengers

The passengers of the vehicle

+ * @param exit

The location the vehicle will exit

+ * @param newVelocity

The new velocity of the teleported vehicle

+ * @param origin

The portal the vehicle teleported from

*/ - private void teleportLivingVehicle(Location exit, List passengers, Portal origin) { - teleportingVehicle.eject(); - teleportingVehicle.teleport(exit); - handleVehiclePassengers(passengers, teleportingVehicle, 2, origin); + private void teleportVehicle(List passengers, Location exit, Vector newVelocity, Portal origin) { + if (teleportingVehicle.eject()) { + TeleportHelper.handleEntityPassengers(passengers, teleportingVehicle, origin, portal, exit.getDirection(), + newVelocity); + } + Stargate.debug("VehicleTeleporter::teleportVehicle", "Teleporting " + teleportingVehicle + + " to final location " + exit + " with direction " + exit.getDirection()); + teleportingVehicle.teleport(exit, PlayerTeleportEvent.TeleportCause.PLUGIN); + scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), + () -> { + Stargate.debug("VehicleTeleporter::teleportVehicle", "Setting velocity " + newVelocity + + " for vehicle " + teleportingVehicle); + teleportingVehicle.setVelocity(newVelocity); + }, 1); } /** @@ -189,52 +168,16 @@ public class VehicleTeleporter extends EntityTeleporter { } //Spawn a new vehicle Vehicle newVehicle = vehicleWorld.spawn(exit, teleportingVehicle.getClass()); + if (teleportingVehicle instanceof Boat boat) { + ((Boat) newVehicle).setWoodType(boat.getWoodType()); + } //Remove the old vehicle - teleportingVehicle.eject(); + if (teleportingVehicle.eject()) { + TeleportHelper.handleEntityPassengers(passengers, newVehicle, origin, portal, exit.getDirection(), + newVelocity); + } teleportingVehicle.remove(); - //Set rotation, add passengers and restore velocity - newVehicle.setRotation(exit.getYaw(), exit.getPitch()); - handleVehiclePassengers(passengers, newVehicle, 1, origin); scheduler.scheduleSyncDelayedTask(Stargate.getInstance(), () -> newVehicle.setVelocity(newVelocity), 1); } - /** - * Ejects, teleports and adds all passengers to the target vehicle - * - * @param passengers

The passengers to handle

- * @param vehicle

The vehicle the passengers should be put into

- * @param delay

The amount of milliseconds to wait before adding the vehicle passengers

- * @param origin

The portal the vehicle teleported from

- */ - private void handleVehiclePassengers(List passengers, Vehicle vehicle, long delay, Portal origin) { - 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); - }, delay); - } - } - - /** - * Teleports and adds a passenger to a vehicle - * - *

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.

- * - * @param targetVehicle

The vehicle to add the passenger to

- * @param passenger

The passenger to teleport and add

- */ - private void teleportAndAddPassenger(Vehicle targetVehicle, Entity passenger) { - if (!passenger.teleport(targetVehicle.getLocation())) { - Stargate.debug("handleVehiclePassengers", "Failed to teleport passenger"); - } - if (!targetVehicle.addPassenger(passenger)) { - Stargate.debug("handleVehiclePassengers", "Failed to add passenger"); - } - } - } diff --git a/src/main/java/net/knarcraft/stargate/thread/BlockChangeThread.java b/src/main/java/net/knarcraft/stargate/thread/BlockChangeThread.java index d177a0b..faa14fc 100644 --- a/src/main/java/net/knarcraft/stargate/thread/BlockChangeThread.java +++ b/src/main/java/net/knarcraft/stargate/thread/BlockChangeThread.java @@ -21,18 +21,22 @@ public class BlockChangeThread implements Runnable { long sTime = System.nanoTime(); //Repeat for at most 0.025 seconds while (System.nanoTime() - sTime < 25000000) { - pollQueue(); + if (pollQueue()) { + break; + } } } /** * Polls the block change request queue for any waiting requests + * + * @return

True if the queue is empty and it's safe to quit

*/ - public static void pollQueue() { + public static boolean pollQueue() { //Abort if there's no work to be done BlockChangeRequest blockChangeRequest = Stargate.getBlockChangeRequestQueue().poll(); if (blockChangeRequest == null) { - return; + return true; } //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 orientBlock(block, blockChangeRequest.getAxis()); } + return false; } /** diff --git a/src/main/java/net/knarcraft/stargate/utility/BungeeHelper.java b/src/main/java/net/knarcraft/stargate/utility/BungeeHelper.java index 00ee6db..677e80f 100644 --- a/src/main/java/net/knarcraft/stargate/utility/BungeeHelper.java +++ b/src/main/java/net/knarcraft/stargate/utility/BungeeHelper.java @@ -190,7 +190,7 @@ public final class BungeeHelper { } //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 if (!BungeeHelper.sendTeleportationMessage(player, entrancePortal)) { diff --git a/src/main/java/net/knarcraft/stargate/utility/ColorHelper.java b/src/main/java/net/knarcraft/stargate/utility/ColorHelper.java index 027bd66..0886b94 100644 --- a/src/main/java/net/knarcraft/stargate/utility/ColorHelper.java +++ b/src/main/java/net/knarcraft/stargate/utility/ColorHelper.java @@ -6,7 +6,14 @@ import org.bukkit.Color; import java.util.regex.Matcher; 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 diff --git a/src/main/java/net/knarcraft/stargate/utility/PermissionHelper.java b/src/main/java/net/knarcraft/stargate/utility/PermissionHelper.java index 2a8c1ce..f87dc41 100644 --- a/src/main/java/net/knarcraft/stargate/utility/PermissionHelper.java +++ b/src/main/java/net/knarcraft/stargate/utility/PermissionHelper.java @@ -387,7 +387,7 @@ public final class PermissionHelper { if (!entrancePortal.getOptions().isSilent()) { Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("denyMsg")); } - new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event); + new PlayerTeleporter(entrancePortal, player).teleportPlayer(entrancePortal, event); return true; } @@ -401,7 +401,7 @@ public final class PermissionHelper { if (!entrancePortal.getOptions().isSilent()) { 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); return true; } diff --git a/src/main/java/net/knarcraft/stargate/utility/TeleportHelper.java b/src/main/java/net/knarcraft/stargate/utility/TeleportHelper.java new file mode 100644 index 0000000..97d7868 --- /dev/null +++ b/src/main/java/net/knarcraft/stargate/utility/TeleportHelper.java @@ -0,0 +1,247 @@ +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.Location; +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 + * + *

The teleport helper mainly helps with passengers and leashed creatures

+ */ +public final class TeleportHelper { + + private TeleportHelper() { + + } + + /** + * Checks whether a player has leashed creatures that block the teleportation + * + * @param player

The player trying to teleport

+ * @return

False if the player has leashed any creatures that cannot go through the portal

+ */ + public static boolean noLeashedCreaturesPreventTeleportation(Player player) { + //Find any nearby leashed entities to teleport with the player + List 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

The player to check

+ * @return

A list of all creatures the player is holding in a leash (lead)

+ */ + public static List getLeashedCreatures(Player player) { + List leashedCreatures = new ArrayList<>(); + //Find any nearby leashed entities to teleport with the player + List 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 + * + *

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.

+ * + * @param targetVehicle

The entity to add the passenger to

+ * @param passenger

The passenger to teleport and add

+ * @param exitDirection

The direction of any passengers exiting the stargate

+ * @param newVelocity

The new velocity of the teleported passenger

+ */ + public static void teleportAndAddPassenger(Entity targetVehicle, Entity passenger, Vector exitDirection, + Vector newVelocity) { + Location passengerExit = targetVehicle.getLocation().clone().setDirection(exitDirection); + if (!passenger.teleport(passengerExit)) { + Stargate.debug("TeleportHelper::handleVehiclePassengers", "Failed to teleport passenger" + + passenger); + } else { + Stargate.debug("TeleportHelper::handleVehiclePassengers", "Teleported " + passenger + + " to " + passengerExit); + } + if (!targetVehicle.addPassenger(passenger)) { + Stargate.debug("TeleportHelper::handleVehiclePassengers", "Failed to add passenger" + + passenger); + } else { + Stargate.debug("TeleportHelper::handleVehiclePassengers", "Added passenger " + passenger + + " to " + targetVehicle); + } + Stargate.debug("VehicleTeleporter::teleportVehicle", "Setting velocity " + newVelocity + + " for passenger " + passenger); + passenger.setVelocity(newVelocity); + } + + /** + * Ejects, teleports and adds all passengers to the target entity + * + * @param passengers

The passengers to handle

+ * @param entity

The entity the passengers should be put into

The portal the entity teleported from

+ * @param target

The portal the entity is teleporting to

+ * @param exitRotation

The rotation of any passengers exiting the stargate

+ * @param newVelocity

The new velocity of the teleported passengers

+ */ + public static void handleEntityPassengers(List passengers, Entity entity, Portal origin, Portal target, + Vector exitRotation, Vector newVelocity) { + for (Entity passenger : passengers) { + List 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, newVelocity); + } + 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); + } + teleportAndAddPassenger(entity, passenger, exitRotation, newVelocity); + }, passenger instanceof Player ? Stargate.getGateConfig().waitForPlayerAfterTeleportDelay() : 0); + } + } + + /** + * Teleports any creatures leashed by the player + * + *

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.

+ * + * @param player

The player which is teleported

+ * @param origin

The portal the player is teleporting from

+ * @param target

The portal the player is teleporting to

+ */ + 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 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

The list of entities to check

+ * @return

True if at least one entity is not a player

+ */ + public static boolean containsNonPlayer(List 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

The list of entities to check

+ * @return

True if at least one player is present among the passengers

+ */ + public static boolean containsPlayer(List 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

The entities to check for players

+ * @return

The found players

+ */ + public static List getPlayers(List entities) { + List 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

The player trying to teleport

+ * @param entrancePortal

The portal the player is entering

+ * @param destinationPortal

The portal the player is to exit from

+ * @return

True if the player is allowed to teleport and is able to pay necessary fees

+ */ + 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); + } + +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 46d18d2..b8d6dbc 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,7 +1,7 @@ # stargate Configuration File # Main stargate config -# language - The language file to load for messages +# language - The language file to load for messages (de,en,es,fr,hu,it,ja,nb-no,nl,nn-no,pt-br,ru,zh_cn) language: en # adminUpdateAlert - Whether to alert admins about new plugin updates adminUpdateAlert: true @@ -15,6 +15,8 @@ gates: maxGatesEachNetwork: 0 # defaultGateNetwork - The default gate network defaultGateNetwork: central + # exitVelocity - The velocity to give players exiting stargates, relative to the entry velocity + exitVelocity: 0.1 cosmetic: # rememberDestination - Whether to remember the cursor location between uses rememberDestination: false @@ -38,7 +40,8 @@ gates: destroyedByExplosion: false # verifyPortals - Whether all the non-sign blocks are checked to match the gate layout when a stargate is loaded. verifyPortals: false - # protectEntrance - Whether to protect gate entrance material (More resource intensive. Only enable if using destroyable open/closed material) + # protectEntrance - Whether to protect gate entrance material (More resource intensive. Only enable if using + # destroyable open/closed material) protectEntrance: false functionality: enableBungee: false @@ -46,16 +49,21 @@ gates: handleVehicles: true # handleEmptyVehicles - Whether to allow empty vehicles through gates (chest/hopper/tnt/furnace minecarts included) handleEmptyVehicles: true - # handleCreatureTransportation - Whether to allow players to transport creatures by sending vehicles (minecarts, boats) through gates + # handleCreatureTransportation - Whether to allow players to transport creatures by sending vehicles (minecarts, + # boats) through gates handleCreatureTransportation: true - # handleNonPlayerVehicles - Whether to allow vehicles with a passenger which is not a player through gates. handleCreatureTransportation must be enabled + # handleNonPlayerVehicles - Whether to allow vehicles with a passenger which is not a player through gates. + # handleCreatureTransportation must be enabled handleNonPlayerVehicles: true # handleLeashedCreatures - Whether to allow creatures lead by a player to teleport with the player 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 # +# ######################## # # stargate economy options # -# I------------I-------------I # +# ######################## # economy: # useEconomy - Whether to use an economy plugin useEconomy: false @@ -74,11 +82,26 @@ economy: # freeGatesColor - The color to use for marking free gates freeGatesColor: DARK_GREEN -# I-------I-------I # +# ############# # # Debug options # -# I-------I-------I # +# ############# # debugging: # debug - Debug -- Only enable if you have issues, massive console output debug: false - # permissionDebug - This will output any and all Permissions checks to console, used for permissions debugging (Requires debug: true) - permissionDebug: false \ No newline at end of file + # permissionDebug - This will output any and all Permissions checks to console, used for permissions debugging + # (Requires debug: true) + 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 + +# ############## # +# Dynmap options # +# ############## # +dynmap: + # enableDynmap - Whether to display Stargates in Dynmap's map + enableDynmap: true + # dynmapIconsHiddenByDefault - Whether to hide the set of Stargate icons by default, requiring users to + # manually enable them with a checkbox. + dynmapIconsHiddenByDefault: true \ No newline at end of file diff --git a/src/main/resources/lang/ja.txt b/src/main/resources/lang/ja.txt new file mode 100644 index 0000000..56c2b1d --- /dev/null +++ b/src/main/resources/lang/ja.txt @@ -0,0 +1,44 @@ +author=furplag +prefix=[Stargate] +teleportMsg=テレポート +destroyMsg=ゲートが破壊されました +invalidMsg=無効な行き先 +blockMsg=ブロックされた行き先 +destEmpty=行き先リストが空です +denyMsg=アクセスが拒否されました +reloaded= Stargate をリロードしました + +ecoDeduct=%cost% の値引き +ecoRefund=%cost% の返金 +ecoObtain= Stargate %portal% から %cost% を得ました +ecoInFunds=資金の不足 +ecoLoadError= Vault が読み込まれましたが、Economy プラグインをフックできませんでした +vaultLoadError=Economy は有効になっていますが、Vault をロードできないため Economy は無効化されました +vaultLoaded= Vault v%version% が見つかりました + +createMsg=ゲートが作成されました +createNetDeny=対象のネットワークにアクセスできません +createGateDeny=対象のゲートレイアウトにアクセスできません +createPersonal=パーソナルネットワーク上にゲートを作成する +createNameLength=ゲート名が短すぎるか長すぎます +createExists=すでに存在するゲート名です +createFull=対象のネットワークはいっぱいです +createWorldDeny=あなたはその世界にアクセスできません +createConflict=ゲートが既存のゲートと競合しています + +signRightClick=右クリック +signToUse=ゲートを使用する +signRandom=ランダム +signDisconnected=切断 +signInvalidGate=無効なゲート + +bungeeDisabled=BungeeCord サポートは無効になっています +bungeeDeny=BungeeCord ゲートを作成する権限がありません +bungeeEmpty=BungeeCord ゲートには、行き先とネットワークの両方が必要です +bungeeSign=テレポート先: + +portalInfoTitle=[STARGATE INFO] +portalInfoName=ゲート名: %name% +portalInfoDestination=行き先: %destination% +portalInfoNetwork=ネットワーク: %network% +portalInfoServer=サーバー: %server% diff --git a/src/main/resources/lang/zh_cn.txt b/src/main/resources/lang/zh_cn.txt new file mode 100644 index 0000000..095fa24 --- /dev/null +++ b/src/main/resources/lang/zh_cn.txt @@ -0,0 +1,39 @@ +author=YKDZ +signRightClick=右键 +ecoLoadError=Vault 已加载, 但未检测到合适的经济插件 +createConflict=星门与现有星门冲突 +invalidMsg=无效的目的地 +prefix=[星门] +ecoObtain=从星门 %portal% 收取了 %cost% +vaultLoaded=检测到 Vault v%version% +reloaded=星门插件已重载 +bungeeDeny=你没有创建跨服星门的权限. +signToUse=以使用星门 +signInvalidGate=未知星门 +bungeeEmpty=跨服星门需要提供目的地和网络. +createMsg=星门已创建 +bungeeDisabled=跨服功能已被禁用. +blockMsg=目的地被阻挡 +ecoInFunds=余额不足 +createNameLength=名称过短或过长. +vaultLoadError=未检测到Vault. 经济模块已禁用 +denyMsg=访问被拒 +ecoDeduct=花费 %cost% +signDisconnected=已取消链接 +createNetDeny=你没有这个星门网络的许可 +bungeeSign=传送到 +portalInfoName=名称: %name% +destroyMsg=星门已被破坏 +portalInfoTitle=[星门信息] +createExists=与已有星门重名 +teleportMsg=已传送 +createGateDeny=你没有使用这个星门结构的权限 +signRandom=随机 +portalInfoServer=服务器: %server% +createWorldDeny=你没有链接这个世界的权限 +portalInfoDestination=目的地: %destination% +portalInfoNetwork=星门网络: %network% +destEmpty=目的地列表为空 +createPersonal=在私人网络中创建星门 +ecoRefund=退款 %cost% +createFull=此星门网络已满 \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 6460692..c717434 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,12 +1,12 @@ name: Stargate main: net.knarcraft.stargate.Stargate -version: 0.9.3.1 +version: 0.9.4.1 description: Stargate mod for Bukkit Revived author: EpicKnarvik97 authors: [ Drakia, PseudoKnight, EpicKnarvik97 ] website: https://git.knarcraft.net/EpicKnarvik97/Stargate api-version: 1.18 -softdepend: [ Vault ] +softdepend: [ Vault, dynmap ] commands: stargate: aliases: