diff --git a/build.gradle b/build.gradle index 2bf5bdae..eebbb6bc 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,10 @@ repositories { name = 'enginehub' url = uri('https://maven.enginehub.org/repo/') } + maven { + name = 'benthecat' + url = uri('https://repo.c0ding.party/multiverse-beta') + } } configure(apiDependencies) { @@ -21,7 +25,7 @@ configure(apiDependencies) { dependencies { // Multiverse - externalPlugin 'org.mvplugins.multiverse.core:multiverse-core:5.0.0-SNAPSHOT' + externalPlugin 'org.mvplugins.multiverse.core:multiverse-core:5.1.0-SNAPSHOT' // WorldEdit externalPlugin('com.sk89q.worldedit:worldedit-bukkit:7.2.9') { diff --git a/src/main/java/org/mvplugins/multiverse/portals/MVPortal.java b/src/main/java/org/mvplugins/multiverse/portals/MVPortal.java index 5cf58aa3..455b9832 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/MVPortal.java +++ b/src/main/java/org/mvplugins/multiverse/portals/MVPortal.java @@ -16,19 +16,24 @@ import java.util.regex.Pattern; import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; +import org.mvplugins.multiverse.core.config.handle.MemoryConfigurationHandle; +import org.mvplugins.multiverse.core.config.handle.StringPropertyHandle; +import org.mvplugins.multiverse.core.config.migration.ConfigMigrator; +import org.mvplugins.multiverse.core.config.migration.VersionMigrator; +import org.mvplugins.multiverse.core.config.migration.action.MoveMigratorAction; import org.mvplugins.multiverse.core.destination.DestinationInstance; import org.mvplugins.multiverse.core.destination.DestinationsProvider; import org.mvplugins.multiverse.core.teleportation.BlockSafety; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; -import org.mvplugins.multiverse.core.world.MultiverseWorld; import org.mvplugins.multiverse.core.world.WorldManager; -import org.mvplugins.multiverse.core.utils.MaterialConverter; -import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; +import org.mvplugins.multiverse.portals.config.PortalsConfig; import org.mvplugins.multiverse.portals.enums.PortalType; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionDefault; @@ -38,26 +43,21 @@ public class MVPortal { private final MultiversePortals plugin; + private final PortalsConfig portalsConfig; private final WorldManager worldManager; private final DestinationsProvider destinationsProvider; private final BlockSafety blockSafety; private final String name; - private final String portalConfigString; - private final FileConfiguration config; + private final MVPortalNodes configNodes; + private final MemoryConfigurationHandle configHandle; + private final StringPropertyHandle stringPropertyHandle; private PortalLocation location; - private DestinationInstance destination; - private String owner; + private Permission permission; private Permission fillPermission; private Permission exempt; - private Material currency = null; - private double price = 0.0; - private boolean safeTeleporter; - private boolean teleportNonPlayers; - private boolean allowSave; - private String handlerScript; private static final Collection INTERIOR_MATERIALS = Arrays.asList(Material.NETHER_PORTAL, Material.GRASS, Material.VINE, Material.SNOW, Material.AIR, Material.WATER, Material.LAVA); @@ -68,10 +68,6 @@ public static boolean isPortalInterior(Material material) { return INTERIOR_MATERIALS.contains(material); } - public MVPortal(@NotNull MultiversePortals plugin, @NotNull String name) { - this(plugin, name, true); - } - public MVPortal(LoadedMultiverseWorld world, MultiversePortals instance, String name, String owner, String location) { this(instance, name); this.setOwner(owner); @@ -86,26 +82,42 @@ public MVPortal(MultiversePortals plugin, String name, String owner, PortalLocat // If this is called with allowSave=false, the caller needs to be sure to // call allowSave() when they're finished modifying it. - private MVPortal( - MultiversePortals plugin, - String name, - boolean allowSave) { - // Disallow saving until initialization is finished. - this.allowSave = false; - + private MVPortal(MultiversePortals plugin, String name) { this.plugin = plugin; + this.portalsConfig = this.plugin.getServiceLocator().getService(PortalsConfig.class); this.worldManager = this.plugin.getServiceLocator().getService(WorldManager.class); this.destinationsProvider = this.plugin.getServiceLocator().getService(DestinationsProvider.class); this.blockSafety = this.plugin.getServiceLocator().getService(BlockSafety.class); - this.config = this.plugin.getPortalsConfig(); this.name = name; - this.portalConfigString = "portals." + this.name; - this.setCurrency(MaterialConverter.stringToMaterial(this.config.getString(this.portalConfigString + ".entryfee.currency"))); - this.setPrice(this.config.getDouble(this.portalConfigString + ".entryfee.amount", 0.0)); - this.setUseSafeTeleporter(this.config.getBoolean(this.portalConfigString + ".safeteleport", true)); - this.setTeleportNonPlayers(this.config.getBoolean(this.portalConfigString + ".teleportnonplayers", false)); - this.setHandlerScript(this.config.getString(this.portalConfigString + ".handlerscript", "")); + + var config = this.plugin.getPortalsConfig(); + this.configNodes = new MVPortalNodes(plugin, this); + var portalSection = config.getConfigurationSection("portals." + this.name); + if (portalSection == null) { + portalSection = config.createSection("portals." + this.name); + } + this.configHandle = MemoryConfigurationHandle.builder(portalSection, configNodes.getNodes()) + .migrator(ConfigMigrator.builder(configNodes.version) + .addVersionMigrator(VersionMigrator.builder(1.0) + .addAction(MoveMigratorAction.of("safeteleport", "safe-teleport")) + .addAction(MoveMigratorAction.of("teleportnonplayers", "teleport-non-players")) + .build()) + .addVersionMigrator(VersionMigrator.builder(1.1) + .addAction(configSection -> { + // combine world and location into 1 string + var world = configSection.getString("world"); + var location = configSection.getString("location"); + if (world != null && location != null) { + configSection.set("location", world + ":" + location); + } + }) + .build()) + .build()) + .build(); + this.stringPropertyHandle = new StringPropertyHandle(this.configHandle); + configHandle.load(); + this.permission = this.plugin.getServer().getPluginManager().getPermission("multiverse.portal.access." + this.name); if (this.permission == null) { this.permission = new Permission("multiverse.portal.access." + this.name, "Allows access to the " + this.name + " portal", PermissionDefault.OP); @@ -122,127 +134,65 @@ private MVPortal( this.exempt = new Permission("multiverse.portal.exempt." + this.name, "A player who has this permission will not pay to use this portal " + this.name + " portal", PermissionDefault.FALSE); this.plugin.getServer().getPluginManager().addPermission(this.exempt); } - this.addToUpperLists(); + } - if (allowSave) { - this.allowSave = true; - saveConfig(); - } + ConfigurationSection save() { + this.configHandle.save(); + return this.configHandle.getConfig(); } - private void allowSave() { - this.allowSave = true; + /** + * Gets the string property handle + * + * @return The string property handle + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public StringPropertyHandle getStringPropertyHandle() { + return this.stringPropertyHandle; } - private void setTeleportNonPlayers(boolean b) { - this.teleportNonPlayers = b; - this.config.set(this.portalConfigString + ".teleportnonplayers", this.teleportNonPlayers); - saveConfig(); + /** + * Set the value of teleportNonPlayers + * + * @param teleportNonPlayers The new value + */ + @ApiStatus.AvailableSince("5.1") + public void setTeleportNonPlayers (boolean teleportNonPlayers) { + this.configHandle.set(configNodes.teleportNonPlayers, teleportNonPlayers); } public boolean getTeleportNonPlayers() { - return teleportNonPlayers; + return this.configHandle.get(configNodes.teleportNonPlayers); } private void setUseSafeTeleporter(boolean teleport) { - this.safeTeleporter = teleport; - this.config.set(this.portalConfigString + ".safeteleport", teleport); - saveConfig(); + this.configHandle.set(configNodes.safeTeleport, teleport); } public boolean useSafeTeleporter() { - return this.safeTeleporter; - } - - private void addToUpperLists() { - Permission all = this.plugin.getServer().getPluginManager().getPermission("multiverse.*"); - Permission allPortals = this.plugin.getServer().getPluginManager().getPermission("multiverse.portal.*"); - Permission allPortalAccess = this.plugin.getServer().getPluginManager().getPermission("multiverse.portal.access.*"); - Permission allPortalExempt = this.plugin.getServer().getPluginManager().getPermission("multiverse.portal.exempt.*"); - Permission allPortalFill = this.plugin.getServer().getPluginManager().getPermission("multiverse.portal.fill.*"); - if (allPortalAccess == null) { - allPortalAccess = new Permission("multiverse.portal.access.*"); - this.plugin.getServer().getPluginManager().addPermission(allPortalAccess); - } - if (allPortalExempt == null) { - allPortalExempt = new Permission("multiverse.portal.exempt.*"); - this.plugin.getServer().getPluginManager().addPermission(allPortalExempt); - } - if (allPortalFill == null) { - allPortalFill = new Permission("multiverse.portal.fill.*"); - this.plugin.getServer().getPluginManager().addPermission(allPortalFill); - } - if (allPortals == null) { - allPortals = new Permission("multiverse.portal.*"); - this.plugin.getServer().getPluginManager().addPermission(allPortals); - } - - if (all == null) { - all = new Permission("multiverse.*"); - this.plugin.getServer().getPluginManager().addPermission(all); - } - all.getChildren().put("multiverse.portal.*", true); - allPortals.getChildren().put("multiverse.portal.access.*", true); - allPortals.getChildren().put("multiverse.portal.exempt.*", true); - allPortals.getChildren().put("multiverse.portal.fill.*", true); - allPortalAccess.getChildren().put(this.permission.getName(), true); - allPortalExempt.getChildren().put(this.exempt.getName(), true); - allPortalFill.getChildren().put(this.fillPermission.getName(), true); - - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(all); - for(Player player : this.plugin.getServer().getOnlinePlayers()){ - player.recalculatePermissions(); - } + return this.configHandle.get(configNodes.safeTeleport); } public static MVPortal loadMVPortalFromConfig(MultiversePortals instance, String name) { - boolean allowSave = false; - MVPortal portal = new MVPortal(instance, name, allowSave); - - // Don't load portals from configs, as we have a linked list issue - // Have to load all portals first, then resolve their destinations. - - String portalLocString = portal.config.getString(portal.portalConfigString + ".location", ""); - String worldString = portal.config.getString(portal.portalConfigString + ".world", ""); - portal.setPortalLocation(portalLocString, worldString); - - portal.setOwner(portal.config.getString(portal.portalConfigString + ".owner", "")); - portal.setCurrency(MaterialConverter.stringToMaterial(portal.config.getString(portal.portalConfigString + ".entryfee.currency"))); - portal.setPrice(portal.config.getDouble(portal.portalConfigString + ".entryfee.amount", 0.0)); - - // We've finished reading the portal from the config file. Any further - // changes to this portal should be saved. - portal.allowSave(); - - return portal; + return new MVPortal(instance, name); } public Material getCurrency() { - return this.currency; + return this.configHandle.get(configNodes.currency); } public double getPrice() { - return this.price; + return this.configHandle.get(configNodes.price); } private boolean setCurrency(Material currency) { - this.currency = currency; - config.set(this.portalConfigString + ".entryfee.currency", currency == null ? null : currency.name()); - saveConfig(); - return true; + return this.configHandle.set(configNodes.currency, currency).isSuccess(); } private boolean setPrice(double price) { - this.price = price; - config.set(this.portalConfigString + ".entryfee.amount", price); - saveConfig(); - return true; - } - - private void saveConfig() { - if (this.allowSave) { - this.plugin.savePortalsConfig(); - } + return this.configHandle.set(configNodes.price, price).isSuccess(); } public boolean setPortalLocation(String locationString, String worldString) { @@ -253,34 +203,40 @@ public boolean setPortalLocation(String locationString, String worldString) { return this.setPortalLocation(locationString, world); } + /** + * + * @param locationString + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public boolean setPortalLocation(String locationString) { + return this.setPortalLocation(PortalLocation.parseLocation(locationString)); + } + public boolean setPortalLocation(String locationString, LoadedMultiverseWorld world) { return this.setPortalLocation(PortalLocation.parseLocation(locationString, world, this.name)); } public boolean setPortalLocation(PortalLocation location) { - this.location = location; - if (!this.location.isValidLocation()) { - Logging.warning("Portal " + this.name + " has an invalid LOCATION!"); + if (!setPortalLocationInternal(location)) { return false; } - this.config.set(this.portalConfigString + ".location", this.location.toString()); - MultiverseWorld world = this.location.getMVWorld(); - if (world != null) { + return configHandle.set(configNodes.location, this.location.toString()).isSuccess(); + } - this.config.set(this.portalConfigString + ".world", world.getName()); - } else { - Logging.warning("Portal " + this.name + " has an invalid WORLD"); + boolean setPortalLocationInternal(PortalLocation location) { + this.location = location; + if (!this.location.isValidLocation()) { + Logging.warning("Portal " + this.name + " has an invalid LOCATION!"); return false; } - saveConfig(); return true; } private boolean setOwner(String owner) { - this.owner = owner; - this.config.set(this.portalConfigString + ".owner", this.owner); - saveConfig(); - return true; + return this.configHandle.set(configNodes.owner, owner).isSuccess(); } public boolean setDestination(String destinationString) { @@ -293,10 +249,11 @@ public boolean setDestination(DestinationInstance newDestination) { Logging.warning("Portal " + this.name + " has an invalid DESTINATION!"); return false; } - this.destination = newDestination; - this.config.set(this.portalConfigString + ".destination", this.destination.toString()); - saveConfig(); - return true; + return this.configHandle.set(configNodes.destination, newDestination.toString()).isSuccess(); + } + + public DestinationInstance getDestination() { + return this.destinationsProvider.parseDestination(this.configHandle.get(configNodes.destination)).getOrNull(); } public String getName() { @@ -304,6 +261,9 @@ public String getName() { } public PortalLocation getLocation() { + if (this.location == null) { + this.location = PortalLocation.parseLocation(this.configHandle.get(configNodes.location)); + } return this.location; } @@ -319,17 +279,6 @@ public Location getSafePlayerSpawnLocation() { return new Location(this.getWorld(), finalX, finalY, finalZ); } - /** - * Allows us to check the column first but only when doing portals - * - * @param finalX - * @param finalZ - * @param y - * @param yMax - * @param w - * - * @return - */ private double getMinimumWith2Air(int finalX, int finalZ, int y, int yMax, World w) { // If this class exists, then this Multiverse-Core MUST exist! // TODO there really ought to be a better way! @@ -371,9 +320,7 @@ public PortalType getPortalType() throws IllegalStateException { if (this.getFillMaterial() == Material.NETHER_PORTAL) { return PortalType.Normal; } - // TODO in 5.0.0: Catch IllegalStateException and return a new PortalType, INVALID. - return PortalType.Legacy; } @@ -395,53 +342,7 @@ public boolean playerCanFillPortal(Player player) { return player.hasPermission(this.fillPermission); } - public DestinationInstance getDestination() { - return this.destination; - } - - public boolean setProperty(String property, String value) { - if (property.equalsIgnoreCase("dest") || property.equalsIgnoreCase("destination")) { - return this.setDestination(value); - } - - - if (property.equalsIgnoreCase("curr") || property.equalsIgnoreCase("currency")) { - return this.setCurrency(Material.matchMaterial(value)); - } - - if (property.equalsIgnoreCase("price")) { - try { - return this.setPrice(Double.parseDouble(value)); - } catch (NumberFormatException e) { - return false; - } - } - - if (property.equalsIgnoreCase("owner")) { - return this.setOwner(value); - } - if (property.equalsIgnoreCase("safe")) { - try { - this.setUseSafeTeleporter(Boolean.parseBoolean(value)); - return true; - } catch (Exception e) { - - } - } - if (property.equalsIgnoreCase("telenonplayers")) { - try { - this.setTeleportNonPlayers(Boolean.parseBoolean(value)); - return true; - } catch (Exception e) { - } - } - if (property.equalsIgnoreCase("handlerscript")) { - this.setHandlerScript(value); - return true; - } - return false; - } - + @Nullable public World getWorld() { LoadedMultiverseWorld mvWorld = this.location.getMVWorld(); if (mvWorld == null) { @@ -450,16 +351,6 @@ public World getWorld() { return mvWorld.getBukkitWorld().getOrNull(); } - public String getHandlerScript() { - return handlerScript; - } - - public void setHandlerScript(String handlerScript) { - this.handlerScript = handlerScript; - this.config.set(this.portalConfigString + ".handlerscript", this.handlerScript); - saveConfig(); - } - public Permission getPermission() { return this.permission; } @@ -468,39 +359,12 @@ public Permission getFillPermission() { return this.fillPermission; } - public void removePermission() { - this.removeFromUpperLists(this.permission); - this.plugin.getServer().getPluginManager().removePermission(permission); + public Permission getExempt() { + return this.exempt; } - private void removeFromUpperLists(Permission permission) { - Permission all = this.plugin.getServer().getPluginManager().getPermission("multiverse.*"); - Permission allPortals = this.plugin.getServer().getPluginManager().getPermission("multiverse.portal.*"); - Permission allPortalAccess = this.plugin.getServer().getPluginManager().getPermission("multiverse.portal.access.*"); - Permission allPortalExempt = this.plugin.getServer().getPluginManager().getPermission("multiverse.portal.exempt.*"); - Permission allPortalFill = this.plugin.getServer().getPluginManager().getPermission("multiverse.portal.fill.*"); - if (all != null) { - all.getChildren().remove(this.permission.getName()); - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(all); - } - - if (allPortals != null) { - allPortals.getChildren().remove(this.permission.getName()); - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(allPortals); - } - - if (allPortalAccess != null) { - allPortalAccess.getChildren().remove(this.permission.getName()); - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(allPortalAccess); - } - if (allPortalExempt != null) { - allPortalExempt.getChildren().remove(this.exempt.getName()); - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(allPortalExempt); - } - if (allPortalFill != null) { - allPortalFill.getChildren().remove(this.fillPermission.getName()); - this.plugin.getServer().getPluginManager().recalculatePermissionDefaults(allPortalFill); - } + public void removePermission() { + this.plugin.getServer().getPluginManager().removePermission(permission); } /** @@ -510,7 +374,7 @@ private void removeFromUpperLists(Permission permission) { * @return true if the frame around the location is valid; false otherwise */ public boolean isFrameValid(Location l) { - List validMaterials = MultiversePortals.FrameMaterials; + List validMaterials = portalsConfig.getFrameMaterials(); if (validMaterials == null || validMaterials.isEmpty()) { // All frame materials are valid. return true; @@ -629,20 +493,9 @@ else if (commonMaterial != material) { } Logging.finer(String.format("frame has common material %s", commonMaterial)); - return MultiversePortals.FrameMaterials.contains(commonMaterial); - } - - @Deprecated - public boolean isExempt(Player player) { - return false; - } - - public Permission getExempt() { - return this.exempt; + return portalsConfig.getFrameMaterials().contains(commonMaterial); } - - private MultiverseRegion expandedRegion(MultiverseRegion r, int x, int y, int z) { Vector min = new Vector().copy(r.getMinimumPoint()); Vector max = new Vector().copy(r.getMaximumPoint()); @@ -651,4 +504,40 @@ private MultiverseRegion expandedRegion(MultiverseRegion r, int x, int y, int z) return new MultiverseRegion(min, max, r.getWorld()); } + // deprecated island + + /** + * @deprecated Use {@link MVPortal#getStringPropertyHandle()} instead. + */ + @Deprecated(since = "5.1" , forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public boolean setProperty(String property, String value) { + return this.stringPropertyHandle.setPropertyString(property, value).isSuccess(); + } + + /** + * @deprecated Busscript feature has been removed. + */ + @Deprecated(since = "5.1" , forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") public String getHandlerScript() { + Logging.warning("handle script is deprecated"); + return ""; + } + + /** + * @deprecated Busscript feature has been removed. + */ + @Deprecated(since = "5.1" , forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") public void setHandlerScript(String handlerScript) { + Logging.warning("handle script is deprecated"); + } + + /** + * @deprecated This feature has been removed. + */ + @Deprecated(since = "5.1" , forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public boolean isExempt(Player player) { + return false; + } } diff --git a/src/main/java/org/mvplugins/multiverse/portals/MVPortalNodes.java b/src/main/java/org/mvplugins/multiverse/portals/MVPortalNodes.java new file mode 100644 index 00000000..3d494fa7 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/portals/MVPortalNodes.java @@ -0,0 +1,106 @@ +package org.mvplugins.multiverse.portals; + +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.mvplugins.multiverse.core.MultiverseCoreApi; +import org.mvplugins.multiverse.core.config.node.ConfigNode; +import org.mvplugins.multiverse.core.config.node.Node; +import org.mvplugins.multiverse.core.config.node.NodeGroup; +import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.destination.DestinationsProvider; +import org.mvplugins.multiverse.core.exceptions.MultiverseException; +import org.mvplugins.multiverse.external.vavr.control.Try; +import org.mvplugins.multiverse.portals.utils.MultiverseRegion; + +import java.util.Collections; +import java.util.List; + +final class MVPortalNodes { + + private final NodeGroup nodes = new NodeGroup(); + + private MultiversePortals plugin; + private MVPortal portal; + private DestinationsProvider destinationsProvider; + + MVPortalNodes(MultiversePortals plugin, MVPortal portal) { + this.plugin = plugin; + this.portal = portal; + this.destinationsProvider = MultiverseCoreApi.get().getDestinationsProvider(); + } + + NodeGroup getNodes() { + return nodes; + } + + private N node(N node) { + nodes.add(node); + return node; + } + + final ConfigNode currency = node(ConfigNode.builder("currency", Material.class) + .defaultValue(Material.AIR) + .aliases("curr") + .build()); + + final ConfigNode price = node(ConfigNode.builder("price", Double.class) + .defaultValue(0.0) + .build()); + + final ConfigNode safeTeleport = node(ConfigNode.builder("safe-teleport", Boolean.class) + .defaultValue(true) + .aliases("safe") + .build()); + + final ConfigNode teleportNonPlayers = node(ConfigNode.builder("teleport-non-players", Boolean.class) + .defaultValue(false) + .aliases("telenonplayers") + .build()); + + final ConfigNode owner = node(ConfigNode.builder("owner", String.class) + .defaultValue("") + .build()); + + final ConfigNode location = node(ConfigNode.builder("location", String.class) + .defaultValue("") + .aliases("loc") + .suggester((sender, input) -> { + if (sender instanceof Player player && plugin.getPortalSession(player).getSelectedRegion() != null) { + return List.of("@selected-region"); + } + return Collections.emptyList(); + }) + .stringParser((sender, input, type) -> { + if (input.equals("@selected-region")) { + if (!(sender instanceof Player player)) { + return Try.failure(new MultiverseException("You can only use '@selected-region' as a player.")); + } + MultiverseRegion region = plugin.getPortalSession(player).getSelectedRegion(); + if (region == null) { + return Try.failure(new MultiverseException("You must select a region first. See `/mvp wand` for more info.")); + } + return Try.success(region.toString()); + } + PortalLocation portalLocation = PortalLocation.parseLocation(input); + if (!portalLocation.isValidLocation()) { + return Try.failure(new MultiverseException("Invalid location format. The portal location must be in the format `WORLD:X,Y,Z:X,Y,Z`.")); + } + return Try.success(portalLocation.toString()); + }) + .onSetValue((oldValue, newValue) -> portal.setPortalLocationInternal(PortalLocation.parseLocation(newValue))) + .build()); + + final ConfigNode destination = node(ConfigNode.builder("destination", String.class) + .defaultValue("") + .aliases("dest") + .suggester((sender, input) -> destinationsProvider.suggestDestinationStrings(sender, input)) + .stringParser((sender, input, type) -> destinationsProvider.parseDestination(sender, input) + .map(DestinationInstance::toString) + .toTry()) + .build()); + + final ConfigNode version = node(ConfigNode.builder("version", Double.class) + .defaultValue(0.0) + .hidden() + .build()); +} diff --git a/src/main/java/org/mvplugins/multiverse/portals/MultiversePortals.java b/src/main/java/org/mvplugins/multiverse/portals/MultiversePortals.java index 33936873..7ea441a6 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/MultiversePortals.java +++ b/src/main/java/org/mvplugins/multiverse/portals/MultiversePortals.java @@ -7,27 +7,20 @@ package org.mvplugins.multiverse.portals; -import java.io.BufferedReader; import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.logging.Level; -import java.util.stream.Collectors; import com.dumptruckman.minecraft.util.Logging; +import org.jetbrains.annotations.ApiStatus; import org.mvplugins.multiverse.core.config.CoreConfig; import org.mvplugins.multiverse.core.destination.DestinationsProvider; import org.mvplugins.multiverse.core.command.MVCommandManager; import org.mvplugins.multiverse.core.module.MultiverseModule; -import org.mvplugins.multiverse.core.utils.MaterialConverter; import org.mvplugins.multiverse.core.utils.StringFormatter; import org.mvplugins.multiverse.external.jakarta.inject.Inject; import org.mvplugins.multiverse.external.jakarta.inject.Provider; @@ -36,12 +29,11 @@ import org.mvplugins.multiverse.portals.commands.PortalsCommand; import org.mvplugins.multiverse.portals.command.PortalsCommandCompletions; import org.mvplugins.multiverse.portals.command.PortalsCommandContexts; +import org.mvplugins.multiverse.portals.config.PortalsConfig; import org.mvplugins.multiverse.portals.destination.PortalDestination; import org.mvplugins.multiverse.portals.destination.RandomPortalDestination; import org.mvplugins.multiverse.portals.listeners.*; import org.bukkit.Material; -import org.bukkit.configuration.Configuration; -import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; @@ -52,7 +44,6 @@ import org.bukkit.event.server.PluginDisableEvent; import org.bukkit.event.server.PluginEnableEvent; import org.bukkit.event.vehicle.VehicleMoveEvent; -import org.bukkit.permissions.Permission; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; @@ -61,7 +52,7 @@ @Service public class MultiversePortals extends MultiverseModule { - private static final double TARGET_CORE_API_VERSION = 5.0; + private static final double TARGET_CORE_API_VERSION = 5.1; @Inject private Provider portalManager; @@ -76,34 +67,21 @@ public class MultiversePortals extends MultiverseModule { @Inject private Provider coreConfig; @Inject + private Provider portalsConfigProvider; + @Inject private Provider metricsConfiguratorProvider; private FileConfiguration MVPPortalConfig; - private FileConfiguration MVPConfig; - private WorldEditConnection worldEditConnection; - private Map portalSessions; - private static final Material DEFAULT_WAND = Material.WOODEN_AXE; - private long portalCooldown = 0; - public static boolean UseOnMove = true; - public static boolean bucketFilling = true; - public static boolean EnforcePortalAccess = true; - public static boolean WandEnabled = true; - public static boolean ClearOnRemove = false; - public static boolean TeleportVehicles = true; - public static boolean NetherAnimation = true; - - // Restricts the materials that can be used for the frames of portals. - // An empty or null list means all materials are okay. - public static List FrameMaterials = null; - + @Override public void onLoad() { super.onLoad(); getDataFolder().mkdirs(); } + @Override public void onEnable() { super.onEnable(); Logging.init(this); @@ -115,17 +93,18 @@ public void onEnable() { // Register our commands this.registerCommands(); - // Ensure permissions are created - // todo: Should we still have this? Luckperms does the wildcards for us now - // this.createDefaultPerms(); - this.portalSessions = new HashMap<>(); this.destinationsProvider.get().registerDestination(this.serviceLocator.getService(PortalDestination.class)); this.destinationsProvider.get().registerDestination(this.serviceLocator.getService(RandomPortalDestination.class)); + if (!this.setupConfig()) { + Logging.severe("Your configs were not loaded."); + Logging.severe("Please check your configs and restart the server."); + this.getServer().getPluginManager().disablePlugin(this); + return; + } this.loadPortals(); - this.loadConfig(); this.setupMetrics(); // Register our events AFTER the config. @@ -136,6 +115,19 @@ public void onEnable() { Logging.log(true, Level.INFO, " Enabled - By %s", StringFormatter.joinAnd(getDescription().getAuthors())); } + @Override + public void onDisable() { + this.savePortalsConfig(); + MultiversePortalsApi.shutdown(); + shutdownDependencyInjection(); + } + + private boolean setupConfig() { + var portalsConfig = portalsConfigProvider.get(); + var loadSuccess = portalsConfig.load().andThenTry(portalsConfig::save).isSuccess(); + return loadSuccess && portalsConfig.isLoaded(); + } + private void registerEvents() { var pluginManager = getServer().getPluginManager(); @@ -144,10 +136,10 @@ private void registerEvents() { .onFailure(e -> { throw new RuntimeException("Failed to register listeners. Terminating...", e); }); - if (MultiversePortals.TeleportVehicles) { + if (portalsConfigProvider.get().getTeleportVehicles()) { pluginManager.registerEvents(serviceLocator.getService(MVPVehicleListener.class), this); } - if (MultiversePortals.UseOnMove) { + if (portalsConfigProvider.get().getUseOnMove()) { pluginManager.registerEvents(serviceLocator.getService(MVPPlayerMoveListener.class), this); } } @@ -163,36 +155,6 @@ private void setupMetrics() { }); } - /** Create the higher level permissions so we can add finer ones to them. */ - private void createDefaultPerms() { - if (this.getServer().getPluginManager().getPermission("multiverse.portal.*") == null) { - Permission perm = new Permission("multiverse.portal.*"); - this.getServer().getPluginManager().addPermission(perm); - } - if (this.getServer().getPluginManager().getPermission("multiverse.portal.access.*") == null) { - Permission perm = new Permission("multiverse.portal.access.*"); - this.getServer().getPluginManager().addPermission(perm); - } - if (this.getServer().getPluginManager().getPermission("multiverse.portal.fill.*") == null) { - Permission perm = new Permission("multiverse.portal.fill.*"); - this.getServer().getPluginManager().addPermission(perm); - } - if (this.getServer().getPluginManager().getPermission("multiverse.portal.exempt.*") == null) { - Permission perm = new Permission("multiverse.portal.exempt.*"); - this.getServer().getPluginManager().addPermission(perm); - } - // Now add these to our parent one. - Permission allPortals = this.getServer().getPluginManager().getPermission("multiverse.portal.*"); - allPortals.getChildren().put("multiverse.portal.access.*", true); - allPortals.getChildren().put("multiverse.portal.exempt.*", true); - allPortals.getChildren().put("multiverse.portal.fill.*", true); - this.getServer().getPluginManager().recalculatePermissionDefaults(allPortals); - - Permission all = this.getServer().getPluginManager().getPermission("multiverse.*"); - all.getChildren().put("multiverse.portal.*", true); - this.getServer().getPluginManager().recalculatePermissionDefaults(all); - } - /** * Gets a PortalSession for a give player. A new instance is created if not present. * @@ -229,103 +191,14 @@ private void loadPortals() { } Logging.info(keys.size() + " - Portals(s) loaded"); } - - // Now Resolve destinations - for (MVPortal portal : this.portalManager.get().getAllPortals()) { - String dest = this.MVPPortalConfig.getString("portals." + portal.getName() + ".destination", ""); - if (!dest.equals("")) { - portal.setDestination(dest); - } - } - this.savePortalsConfig(); - - } - - public void loadConfig() { - - this.MVPConfig = YamlConfiguration.loadConfiguration(new File(getDataFolder(), "config.yml")); - InputStream resourceURL = this.getClass().getResourceAsStream("/defaults/config.yml"); - - // Read in our default config with UTF-8 now - Configuration portalsDefaults = null; - try { - portalsDefaults = YamlConfiguration.loadConfiguration(new BufferedReader(new InputStreamReader(resourceURL, "UTF-8"))); - this.MVPConfig.setDefaults(portalsDefaults); - } catch (UnsupportedEncodingException e) { - Logging.severe("Couldn't load default config with UTF-8 encoding. Details follow:"); - e.printStackTrace(); - Logging.severe("Default configs NOT loaded."); - } - - - this.MVPConfig.options().copyDefaults(true); - this.saveMainConfig(); - this.MVPConfig = YamlConfiguration.loadConfiguration(new File(getDataFolder(), "config.yml")); - - MultiversePortals.UseOnMove = this.MVPConfig.getBoolean("useonmove", true); - MultiversePortals.bucketFilling = this.MVPConfig.getBoolean("bucketfilling", true); - MultiversePortals.EnforcePortalAccess = this.MVPConfig.getBoolean("enforceportalaccess", true); - this.portalCooldown = this.MVPConfig.getInt("portalcooldown", 1000); - MultiversePortals.ClearOnRemove = this.MVPConfig.getBoolean("clearonremove", false); - MultiversePortals.TeleportVehicles = this.MVPConfig.getBoolean("teleportvehicles", true); - MultiversePortals.NetherAnimation = this.MVPConfig.getBoolean("netheranimation", true); - MultiversePortals.FrameMaterials = migrateFrameMaterials(this.MVPConfig); - - // Migrate useportalaccess -> enforceportalaccess - if (this.MVPConfig.get("useportalaccess") != null) { - this.MVPConfig.set("enforceportalaccess", this.MVPConfig.getBoolean("useportalaccess", true)); - Logging.info("Migrating useportalaccess -> enforceportalaccess..."); - } - - if (this.MVPConfig.get("mvportals_default_to_nether") != null) { - this.MVPConfig.set("portalsdefaulttonether", this.MVPConfig.getBoolean("mvportals_default_to_nether", false)); - Logging.info("Migrating mvportals_default_to_nether -> portalsdefaulttonether..."); - } - - if (this.MVPConfig.get("use_onmove") != null) { - this.MVPConfig.set("useonmove", this.MVPConfig.getBoolean("use_onmove", false)); - Logging.info("Migrating use_onmove -> useonmove..."); - } - - if (this.MVPConfig.get("portal_cooldown") != null) { - this.MVPConfig.set("portalcooldown", this.MVPConfig.getInt("portal_cooldown", 1000)); - Logging.info("Migrating portal_cooldown -> portalcooldown..."); - } - - // Remove old properties - this.MVPConfig.set("mvportals_default_to_nether", null); - this.MVPConfig.set("useportalaccess", null); - this.MVPConfig.set("use_onmove", null); - this.MVPConfig.set("portal_cooldown", null); - // Update the version - if (portalsDefaults != null) { - this.MVPConfig.set("version", portalsDefaults.get("version")); - } - - this.saveMainConfig(); - } - - private List migrateFrameMaterials(ConfigurationSection config) { - return config.getList("framematerials", Collections.emptyList()).stream() - .map(Object::toString) - .map(MaterialConverter::stringToMaterial) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - } - - public boolean saveMainConfig() { - try { - this.MVPConfig.save(new File(this.getDataFolder(), "config.yml")); - return true; - } catch (IOException e) { - Logging.severe("Failed to save Portals config.yml."); - return false; - } } public boolean savePortalsConfig() { try { + for (MVPortal portal : this.portalManager.get().getAllPortals()) { + this.MVPPortalConfig.set("portals." + portal.getName(), portal.save()); + } this.MVPPortalConfig.save(new File(this.getDataFolder(), "portals.yml")); return true; } catch (IOException e) { @@ -334,11 +207,6 @@ public boolean savePortalsConfig() { } } - public void onDisable() { - MultiversePortalsApi.shutdown(); - shutdownDependencyInjection(); - } - /** Register commands to Multiverse's CommandHandler so we get a super sexy single menu */ private void registerCommands() { Try.run(() -> { @@ -368,10 +236,6 @@ public FileConfiguration getPortalsConfig() { return this.MVPPortalConfig; } - public FileConfiguration getMainConfig() { - return this.MVPConfig; - } - public void reloadConfigs() { this.reloadConfigs(true); } @@ -383,21 +247,21 @@ public void reloadConfigs(boolean reloadPortals) { } PluginManager pm = this.getServer().getPluginManager(); - boolean previousTeleportVehicles = MultiversePortals.TeleportVehicles; - boolean previousUseOnMove = MultiversePortals.UseOnMove; + boolean previousTeleportVehicles = portalsConfigProvider.get().getTeleportVehicles(); + boolean previousUseOnMove = portalsConfigProvider.get().getUseOnMove(); - this.loadConfig(); + this.setupConfig(); - if (MultiversePortals.TeleportVehicles != previousTeleportVehicles) { - if (MultiversePortals.TeleportVehicles) { + if (portalsConfigProvider.get().getTeleportVehicles() != previousTeleportVehicles) { + if (portalsConfigProvider.get().getTeleportVehicles()) { pm.registerEvents(serviceLocator.getService(MVPVehicleListener.class), this); } else { VehicleMoveEvent.getHandlerList().unregister(this); } } - if (MultiversePortals.UseOnMove != previousUseOnMove) { - if (MultiversePortals.UseOnMove) { + if (portalsConfigProvider.get().getUseOnMove() != previousUseOnMove) { + if (portalsConfigProvider.get().getUseOnMove()) { pm.registerEvents(serviceLocator.getService(MVPPlayerMoveListener.class), this);; } else { BlockFromToEvent.getHandlerList().unregister(this); @@ -406,25 +270,6 @@ public void reloadConfigs(boolean reloadPortals) { } } - public String getVersionInfo() { - return "[Multiverse-Portals] Multiverse-Portals Version: " + this.getDescription().getVersion() + '\n' - + "[Multiverse-Portals] Loaded Portals: " + this.portalManager.get().getAllPortals().size() + '\n' - + "[Multiverse-Portals] Dumping Portal Values: (version " + this.getMainConfig().getDouble("version", -1) + ')' + '\n' - + "[Multiverse-Portals] wand: " + this.getMainConfig().get("wand", "NOT SET") + '\n' - + "[Multiverse-Portals] useonmove: " + this.getMainConfig().get("useonmove", "NOT SET") + '\n' - + "[Multiverse-Portals] bucketfilling: " + this.getMainConfig().get("bucketfilling", "NOT SET") + '\n' - + "[Multiverse-Portals] portalsdefaulttonether: " + this.getMainConfig().get("portalsdefaulttonether", "NOT SET") + '\n' - + "[Multiverse-Portals] enforceportalaccess: " + this.getMainConfig().get("enforceportalaccess", "NOT SET") + '\n' - + "[Multiverse-Portals] portalcooldown: " + this.getMainConfig().get("portalcooldown", "NOT SET") + '\n' - + "[Multiverse-Portals] clearonremove: " + this.getMainConfig().get("clearonremove", "NOT SET") + '\n' - + "[Multiverse-Portals] framematerials: " + this.getMainConfig().get("framematerials", "NOT SET") + '\n' - + "[Multiverse-Portals] Special Code: FRN001" + '\n'; - } - - public long getCooldownTime() { - return this.portalCooldown; - } - public boolean isWandEnabled() { return WandEnabled; } @@ -433,14 +278,6 @@ public void setWandEnabled(boolean enabled) { WandEnabled = enabled; } - public Material getWandMaterial() { - Material m = MaterialConverter.stringToMaterial(getMainConfig().getString("wand")); - if (m == null) { - m = MultiversePortals.DEFAULT_WAND; - } - return m; - } - private class WorldEditPluginListener implements Listener { private WorldEditPluginListener() { @@ -477,4 +314,118 @@ private void pluginDisableEvent(PluginDisableEvent event) { } } } + + // Start of deprecated methods + + /** + * @deprecated Use {@link PortalsConfig#getUseOnMove()} instead + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public static boolean UseOnMove = true; + + /** + * @deprecated Use {@link PortalsConfig#getBucketFilling()} instead + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public static boolean bucketFilling = true; + + /** + * @deprecated Use {@link PortalsConfig#getPortalsDefaultToNether()} instead + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public static boolean EnforcePortalAccess = true; + + /** + * @deprecated Use {@link MultiversePortals#isWandEnabled()} instead + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public static boolean WandEnabled = true; + + /** + * @deprecated Use {@link PortalsConfig#getClearOnRemove()} instead + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public static boolean ClearOnRemove = false; + + /** + * @deprecated Use {@link PortalsConfig#getTeleportVehicles()} instead + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public static boolean TeleportVehicles = true; + + /** + * @deprecated Use {@link PortalsConfig#getNetherAnimation()} instead + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public static boolean NetherAnimation = true; + + /** + * @deprecated Use {@link PortalsConfig#getFrameMaterials()} instead + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public static List FrameMaterials = null; + + /** + * @deprecated Use {@link PortalsConfig#load()} instead + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public void loadConfig() { + this.setupConfig(); + } + + /** + * @deprecated Use {@link PortalsConfig#save()} instead + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public boolean saveMainConfig() { + return portalsConfigProvider.get().save().isSuccess(); + } + + /** + * @deprecated Use {@link PortalsConfig} methods instead. Do not edit the config file object itself. + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public FileConfiguration getMainConfig() { + return portalsConfigProvider.get().getConfig(); + } + + /** + * @deprecated Use {@link PortalsConfig#getPortalCooldown()} instead + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public long getCooldownTime() { + return portalsConfigProvider.get().getPortalCooldown(); + } + + /** + * @deprecated Use {@link PortalsConfig#getWandMaterial()} instead + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public Material getWandMaterial() { + return portalsConfigProvider.get().getWandMaterial(); + } + + /** + * @deprecated This is a debug method that should not be exposed. + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public String getVersionInfo() { + return "[Multiverse-Portals] Multiverse-Portals Version: " + this.getDescription().getVersion() + '\n' + + "[Multiverse-Portals] Loaded Portals: " + this.portalManager.get().getAllPortals().size() + '\n' + + "[Multiverse-Portals] Special Code: FRN001" + '\n'; + } } diff --git a/src/main/java/org/mvplugins/multiverse/portals/MultiversePortalsApi.java b/src/main/java/org/mvplugins/multiverse/portals/MultiversePortalsApi.java index f2ca3f3e..0686fe36 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/MultiversePortalsApi.java +++ b/src/main/java/org/mvplugins/multiverse/portals/MultiversePortalsApi.java @@ -2,14 +2,16 @@ import org.bukkit.Bukkit; import org.bukkit.plugin.ServicePriority; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.mvplugins.multiverse.core.inject.PluginServiceLocator; +import org.mvplugins.multiverse.portals.config.PortalsConfig; import org.mvplugins.multiverse.portals.utils.PortalFiller; import org.mvplugins.multiverse.portals.utils.PortalManager; import java.util.Objects; -public class MultiversePortalsApi { +public final class MultiversePortalsApi { private static MultiversePortalsApi instance; @@ -62,6 +64,18 @@ private MultiversePortalsApi(@NotNull PluginServiceLocator serviceProvider) { return Objects.requireNonNull(serviceLocator.getService(PortalManager.class)); } + /** + * Gets the instance of the PortalsConfig. + * + * @return The PortalsConfig instance + * + * @since 5.1 + */ + @ApiStatus.AvailableSince( "5.1") + public @NotNull PortalsConfig getPortalsConfig() { + return Objects.requireNonNull(serviceLocator.getService(PortalsConfig.class)); + } + /** * Gets the instance of Multiverse-Portals's PluginServiceLocator. *
diff --git a/src/main/java/org/mvplugins/multiverse/portals/PortalLocation.java b/src/main/java/org/mvplugins/multiverse/portals/PortalLocation.java index 8d3c42c5..88c01a04 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/PortalLocation.java +++ b/src/main/java/org/mvplugins/multiverse/portals/PortalLocation.java @@ -14,29 +14,35 @@ import com.sk89q.worldedit.math.BlockVector3; import org.bukkit.util.Vector; +import org.mvplugins.multiverse.core.MultiverseCoreApi; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; import org.mvplugins.multiverse.portals.utils.MultiverseRegion; -public class PortalLocation { +import javax.annotation.Nullable; - private MultiverseRegion region; - private boolean validLocation = false; +public class PortalLocation { - public PortalLocation(Vector pos1, Vector pos2, LoadedMultiverseWorld world) { - this.validLocation = this.setLocation(pos1, pos2, world); - } + public static PortalLocation parseLocation(String locationString) { + String[] split = locationString.split(":"); + if (split.length != 3) { + Logging.warning("Failed Parsing Location (Format Error, was expecting: `WORLD:X,Y,Z:X,Y,Z`, but got: `" + locationString + "`)"); + return getInvalidPortalLocation(); + } - public PortalLocation() { - } + String worldName = split[0]; + LoadedMultiverseWorld world = MultiverseCoreApi.get().getWorldManager().getLoadedWorld(worldName).getOrNull(); + if (world == null) { + Logging.warning("Failed Parsing World (World Error, World did not exist or was not imported into Multiverse-Core!)"); + return getInvalidPortalLocation(); + } - /** - * This constructor takes the Vectors from WorldEdit and converts them to Bukkit vectors. - * - * @param minPt - * @param maxPt - */ - public PortalLocation(BlockVector3 minPt, BlockVector3 maxPt, LoadedMultiverseWorld world) { - this(new Vector(minPt.getX(), minPt.getY(), minPt.getZ()), new Vector(maxPt.getX(), maxPt.getY(), maxPt.getZ()), world); + Vector pos1 = parseVector(split[1]); + Vector pos2 = parseVector(split[2]); + if (pos1 == null || pos2 == null) { + Logging.warning("Failed Parsing Location (Vector Error, was expecting: `WORLD:X,Y,Z:X,Y,Z`, but got: `" + locationString + "`)"); + return getInvalidPortalLocation(); + } + return new PortalLocation(pos1, pos2, world); } public static PortalLocation parseLocation(String locationString, LoadedMultiverseWorld world, String portalName) { @@ -58,13 +64,32 @@ public static PortalLocation parseLocation(String locationString, LoadedMultiver return getInvalidPortalLocation(); } return new PortalLocation(pos1, pos2, world); - } private static PortalLocation getInvalidPortalLocation() { return new PortalLocation(); } + private MultiverseRegion region; + private boolean validLocation = false; + + public PortalLocation() { + } + + public PortalLocation(Vector pos1, Vector pos2, LoadedMultiverseWorld world) { + this.validLocation = this.setLocation(pos1, pos2, world); + } + + /** + * This constructor takes the Vectors from WorldEdit and converts them to Bukkit vectors. + * + * @param minPt + * @param maxPt + */ + public PortalLocation(BlockVector3 minPt, BlockVector3 maxPt, LoadedMultiverseWorld world) { + this(new Vector(minPt.getX(), minPt.getY(), minPt.getZ()), new Vector(maxPt.getX(), maxPt.getY(), maxPt.getZ()), world); + } + private static Vector parseVector(String vectorString) { String[] stringCoords = vectorString.split(","); double[] coords = new double[3]; @@ -117,29 +142,19 @@ public Vector getMaximum() { return this.region.getMaximumPoint(); } - @Override - public String toString() { - if (this.region == null) { - return ""; - } - StringBuilder sb = new StringBuilder(); - sb.append(this.region.getMinimumPoint().getX() + ","); - sb.append(this.region.getMinimumPoint().getY() + ","); - sb.append(this.region.getMinimumPoint().getZ() + ":"); - sb.append(this.region.getMaximumPoint().getX() + ","); - sb.append(this.region.getMaximumPoint().getY() + ","); - sb.append(this.region.getMaximumPoint().getZ()); - return sb.toString(); - } - - public LoadedMultiverseWorld getMVWorld() { + public @Nullable LoadedMultiverseWorld getMVWorld() { if (this.region == null) { return null; } return this.region.getWorld(); } - public MultiverseRegion getRegion() { + public @Nullable MultiverseRegion getRegion() { return this.region; } + + @Override + public String toString() { + return this.region == null ? "" : this.region.toString(); + } } diff --git a/src/main/java/org/mvplugins/multiverse/portals/PortalPlayerSession.java b/src/main/java/org/mvplugins/multiverse/portals/PortalPlayerSession.java index 6f63e9bf..b50307f6 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/PortalPlayerSession.java +++ b/src/main/java/org/mvplugins/multiverse/portals/PortalPlayerSession.java @@ -13,6 +13,7 @@ import org.mvplugins.multiverse.core.economy.MVEconomist; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; import org.mvplugins.multiverse.core.world.WorldManager; +import org.mvplugins.multiverse.portals.config.PortalsConfig; import org.mvplugins.multiverse.portals.enums.MoveType; import org.mvplugins.multiverse.portals.utils.DisplayUtils; import org.bukkit.ChatColor; @@ -25,6 +26,7 @@ public class PortalPlayerSession { private final MultiversePortals plugin; + private final PortalsConfig portalsConfig; private final PortalManager portalManager; private final WorldManager worldManager; private final DisplayUtils displayUtils; @@ -45,13 +47,14 @@ public class PortalPlayerSession { public PortalPlayerSession(MultiversePortals plugin, Player p) { this.plugin = plugin; + this.portalsConfig = plugin.getServiceLocator().getService(PortalsConfig.class); this.portalManager = plugin.getServiceLocator().getService(PortalManager.class); this.worldManager = plugin.getServiceLocator().getService(WorldManager.class); this.displayUtils = plugin.getServiceLocator().getService(DisplayUtils.class); this.economist = plugin.getServiceLocator().getService(MVEconomist.class); this.player = p; this.setLocation(p.getLocation()); - this.lastTeleportTime = new Date(new Date().getTime() - this.plugin.getCooldownTime()); + this.lastTeleportTime = new Date(new Date().getTime() - this.portalsConfig.getPortalCooldown()); } public boolean selectPortal(MVPortal portal) { @@ -288,7 +291,7 @@ public boolean checkAndSendCooldownMessage() { * negative value may be returned if the cooldown is no longer in effect. */ private long getRemainingTeleportCooldown() { - long cooldownEndMs = this.lastTeleportTime.getTime() + this.plugin.getCooldownTime(); + long cooldownEndMs = this.lastTeleportTime.getTime() + this.portalsConfig.getPortalCooldown(); long timeMs = (new Date()).getTime(); return cooldownEndMs - timeMs; } diff --git a/src/main/java/org/mvplugins/multiverse/portals/command/PortalsCommandCompletions.java b/src/main/java/org/mvplugins/multiverse/portals/command/PortalsCommandCompletions.java index bc6ec4e0..43083eaf 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/command/PortalsCommandCompletions.java +++ b/src/main/java/org/mvplugins/multiverse/portals/command/PortalsCommandCompletions.java @@ -2,39 +2,74 @@ import org.mvplugins.multiverse.core.command.MVCommandCompletions; import org.mvplugins.multiverse.core.command.MVCommandManager; +import org.mvplugins.multiverse.core.config.handle.PropertyModifyAction; import org.mvplugins.multiverse.external.acf.commands.BukkitCommandCompletionContext; import org.mvplugins.multiverse.external.jakarta.inject.Inject; import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.external.vavr.control.Try; import org.mvplugins.multiverse.portals.MVPortal; -import org.mvplugins.multiverse.portals.enums.PortalConfigProperty; -import org.mvplugins.multiverse.portals.enums.SetProperties; +import org.mvplugins.multiverse.portals.config.PortalsConfig; import org.mvplugins.multiverse.portals.utils.PortalManager; import java.util.Collection; +import java.util.Collections; @Service public class PortalsCommandCompletions { private final PortalManager portalManager; + private final PortalsConfig portalsConfig; @Inject - PortalsCommandCompletions(@NotNull PortalManager portalManager, @NotNull MVCommandManager commandManager) { + PortalsCommandCompletions(@NotNull PortalManager portalManager, + @NotNull PortalsConfig portalsConfig, + @NotNull MVCommandManager commandManager) { this.portalManager = portalManager; + this.portalsConfig = portalsConfig; registerCompletions(commandManager.getCommandCompletions()); } private void registerCompletions(MVCommandCompletions commandCompletions) { commandCompletions.registerAsyncCompletion("mvportals", this::suggestPortals); - commandCompletions.registerStaticCompletion("setproperties", commandCompletions.suggestEnums(SetProperties.class)); - commandCompletions.registerStaticCompletion("portalconfigproperty", commandCompletions.suggestEnums(PortalConfigProperty.class)); + commandCompletions.registerStaticCompletion("portalconfigproperties", portalsConfig.getStringPropertyHandle().getAllPropertyNames()); + commandCompletions.registerAsyncCompletion("portalconfigvalues", this::suggestPortalConfigValues); + commandCompletions.registerAsyncCompletion("portalproperties", this::suggestPortalPropertyNames); + commandCompletions.registerAsyncCompletion("portalpropertynames", this::suggestPortalPropertyNames); + commandCompletions.registerAsyncCompletion("portalpropertyvalues", this::suggestPortalPropertyValues); commandCompletions.setDefaultCompletion("mvportals", MVPortal.class); } + private Collection suggestPortalConfigValues(BukkitCommandCompletionContext context) { + return Try.of(() -> context.getContextValue(String.class)) + .map(propertyName -> portalsConfig.getStringPropertyHandle() + .getSuggestedPropertyValue(propertyName, context.getInput(), PropertyModifyAction.SET)) + .getOrElse(Collections.emptyList()); + } + private Collection suggestPortals(BukkitCommandCompletionContext context) { return this.portalManager.getPortals(context.getSender()).stream() .map(MVPortal::getName) .toList(); } + + private Collection suggestPortalPropertyNames(BukkitCommandCompletionContext context) { + return Try.of(() -> context.getContextValue(MVPortal.class)) + .map(portal -> portal.getStringPropertyHandle().getAllPropertyNames()) + .getOrElse(Collections.emptyList()); + } + + private Collection suggestPortalPropertyValues(BukkitCommandCompletionContext context) { + return Try.of(() -> { + MVPortal portal = context.getContextValue(MVPortal.class); + String propertyName = context.getContextValue(String.class); + return portal.getStringPropertyHandle().getSuggestedPropertyValue( + propertyName, + context.getInput(), + PropertyModifyAction.SET, + context.getSender() + ); + }).getOrElse(Collections.emptyList()); + } } diff --git a/src/main/java/org/mvplugins/multiverse/portals/commands/ConfigCommand.java b/src/main/java/org/mvplugins/multiverse/portals/commands/ConfigCommand.java index 8ca46cbe..e1c8ea3d 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/commands/ConfigCommand.java +++ b/src/main/java/org/mvplugins/multiverse/portals/commands/ConfigCommand.java @@ -1,109 +1,91 @@ package org.mvplugins.multiverse.portals.commands; -import org.bukkit.ChatColor; import org.mvplugins.multiverse.core.command.LegacyAliasCommand; import org.mvplugins.multiverse.core.command.MVCommandIssuer; -import org.mvplugins.multiverse.core.command.MVCommandManager; +import org.mvplugins.multiverse.core.locale.MVCorei18n; +import org.mvplugins.multiverse.core.locale.message.MessageReplacement; import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion; import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission; import org.mvplugins.multiverse.external.acf.commands.annotation.Description; import org.mvplugins.multiverse.external.acf.commands.annotation.Optional; -import org.mvplugins.multiverse.external.acf.commands.annotation.Single; import org.mvplugins.multiverse.external.acf.commands.annotation.Subcommand; import org.mvplugins.multiverse.external.acf.commands.annotation.Syntax; import org.mvplugins.multiverse.external.jakarta.inject.Inject; import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; import org.jvnet.hk2.annotations.Service; -import org.mvplugins.multiverse.portals.MultiversePortals; -import org.mvplugins.multiverse.portals.enums.PortalConfigProperty; +import org.mvplugins.multiverse.portals.config.PortalsConfig; @Service class ConfigCommand extends PortalsCommand { - private final MultiversePortals plugin; + private final PortalsConfig portalsConfig; @Inject - ConfigCommand(@NotNull MultiversePortals plugin) { - this.plugin = plugin; + ConfigCommand(@NotNull PortalsConfig portalsConfig) { + this.portalsConfig = portalsConfig; } - @Subcommand("config|conf") + @Subcommand("config") @CommandPermission("multiverse.portal.config") - @CommandCompletion("@portalconfigproperty @empty") - @Syntax(" ") + @CommandCompletion("@portalconfigproperties @portalconfigvalues") + @Syntax(" [value]") @Description("Allows you to set Global MV Portals Variables.") void onConfigCommand( @NotNull MVCommandIssuer issuer, @Optional @Syntax("") - @Description("The property to set.") - PortalConfigProperty property, + @Description("The property to set or get info of.") + String property, @Optional - @Single - @Syntax("") + @Syntax("[value]") @Description("The value to set.") String value ) { - if (property == null) { - String[] allProps = PortalConfigProperty.getAllValues().split(" "); - StringBuilder currentvals = new StringBuilder(); - for (String prop : allProps) { - currentvals.append(ChatColor.GREEN); - currentvals.append(prop); - currentvals.append(ChatColor.WHITE); - currentvals.append(" = "); - currentvals.append(ChatColor.GOLD); - currentvals.append(this.plugin.getMainConfig().get(prop, "NOT SET")); - currentvals.append(ChatColor.WHITE); - currentvals.append(", "); - } - issuer.sendMessage(currentvals.substring(0,currentvals.length() - 2)); - return; - } - if (value == null) { - issuer.sendMessage(ChatColor.AQUA + property.name() + ChatColor.WHITE + " has value " - + ChatColor.GREEN + this.plugin.getMainConfig().get(property.name().toLowerCase())); + showConfigValue(issuer, property); return; } + updateConfigValue(issuer, property, value); + } - if (property.equals(PortalConfigProperty.wand) || property.equals(PortalConfigProperty.portalcooldown)) { - try { - this.plugin.getMainConfig().set(property.name(), Integer.parseInt(value)); - } catch (NumberFormatException e) { - issuer.sendMessage(ChatColor.RED + "Sorry, " + ChatColor.AQUA + property.name() + ChatColor.WHITE + " must be an integer!"); - return; - } - } else { - try { - this.plugin.getMainConfig().set(property.name().toLowerCase(), Boolean.parseBoolean(value)); - } catch (Exception e) { - issuer.sendMessage(ChatColor.RED + "Sorry, " + ChatColor.AQUA + property.name() + ChatColor.WHITE + " must be true or false!"); - return; - } - } + private void showConfigValue(MVCommandIssuer issuer, String name) { + portalsConfig.getStringPropertyHandle().getProperty(name) + .onSuccess(value -> issuer.sendMessage(MVCorei18n.CONFIG_SHOW_SUCCESS, + MessageReplacement.Replace.NAME.with(name), + MessageReplacement.Replace.VALUE.with(value))) + .onFailure(e -> issuer.sendMessage(MVCorei18n.CONFIG_SHOW_ERROR, + MessageReplacement.Replace.NAME.with(name), + MessageReplacement.Replace.ERROR.with(e))); + } - if (this.plugin.saveMainConfig()) { - issuer.sendMessage(ChatColor.GREEN + "SUCCESS!" + ChatColor.WHITE + " Values were updated successfully!"); - this.plugin.reloadConfigs(false); - } else { - issuer.sendMessage(ChatColor.RED + "FAIL!" + ChatColor.WHITE + " Check your console for details!"); - } + private void updateConfigValue(MVCommandIssuer issuer, String name, String value) { + portalsConfig.getStringPropertyHandle().setPropertyString(issuer.getIssuer(), name, value) + .onSuccess(ignore -> { + portalsConfig.save(); + issuer.sendMessage(MVCorei18n.CONFIG_SET_SUCCESS, + MessageReplacement.Replace.NAME.with(name), + MessageReplacement.Replace.VALUE.with(value)); + }) + .onFailure(e -> issuer.sendMessage(MVCorei18n.CONFIG_SET_ERROR, + MessageReplacement.Replace.NAME.with(name), + MessageReplacement.Replace.VALUE.with(value), + MessageReplacement.Replace.ERROR.with(e))); } @Service private final static class LegacyAlias extends ConfigCommand implements LegacyAliasCommand { @Inject - LegacyAlias(MultiversePortals plugin) { - super(plugin); + LegacyAlias(PortalsConfig portalsConfig) { + super(portalsConfig); } @Override @CommandAlias("mvpconfig|mvpconf") - void onConfigCommand(MVCommandIssuer issuer, PortalConfigProperty property, String value) { + @Subcommand("config|conf") + void onConfigCommand(MVCommandIssuer issuer, String property, String value) { super.onConfigCommand(issuer, property, value); } } diff --git a/src/main/java/org/mvplugins/multiverse/portals/commands/CreateCommand.java b/src/main/java/org/mvplugins/multiverse/portals/commands/CreateCommand.java index 240f36aa..ca228786 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/commands/CreateCommand.java +++ b/src/main/java/org/mvplugins/multiverse/portals/commands/CreateCommand.java @@ -83,6 +83,7 @@ void onCreateCommand( ps.selectPortal(portal); if (destination != null) { portal.setDestination(destination); + this.plugin.savePortalsConfig(); } else { player.sendMessage(ChatColor.RED + "Portal destination not set. Use " + ChatColor.DARK_AQUA + "/mvp modify destination " + ChatColor.RED + " to set one."); } diff --git a/src/main/java/org/mvplugins/multiverse/portals/commands/ModifyCommand.java b/src/main/java/org/mvplugins/multiverse/portals/commands/ModifyCommand.java index 201b81fe..b6ea5697 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/commands/ModifyCommand.java +++ b/src/main/java/org/mvplugins/multiverse/portals/commands/ModifyCommand.java @@ -2,12 +2,11 @@ import com.dumptruckman.minecraft.util.Logging; import org.bukkit.ChatColor; -import org.bukkit.entity.Player; import org.mvplugins.multiverse.core.command.LegacyAliasCommand; -import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; +import org.mvplugins.multiverse.core.locale.message.LocalizableMessage; import org.mvplugins.multiverse.core.world.WorldManager; import org.mvplugins.multiverse.core.command.MVCommandIssuer; -import org.mvplugins.multiverse.core.command.MVCommandManager;import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; +import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion; import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission; import org.mvplugins.multiverse.external.acf.commands.annotation.Description; @@ -20,10 +19,6 @@ import org.jvnet.hk2.annotations.Service; import org.mvplugins.multiverse.portals.MVPortal; import org.mvplugins.multiverse.portals.MultiversePortals; -import org.mvplugins.multiverse.portals.PortalLocation; -import org.mvplugins.multiverse.portals.PortalPlayerSession; -import org.mvplugins.multiverse.portals.enums.SetProperties; -import org.mvplugins.multiverse.portals.utils.MultiverseRegion; @Service class ModifyCommand extends PortalsCommand { @@ -39,7 +34,7 @@ class ModifyCommand extends PortalsCommand { @Subcommand("modify") @CommandPermission("multiverse.portal.modify") - @CommandCompletion("@mvportals @setproperties @empty") + @CommandCompletion("@mvportals @portalpropertynames @portalpropertyvalues") @Syntax("[portal] ") @Description("Allows you to modify all values that can be set.") public void onModifyCommand( @@ -52,47 +47,40 @@ public void onModifyCommand( @Syntax("") @Description("The property to modify.") - SetProperties property, + String property, @Single @Syntax("") @Description("The value to set.") String value ) { - Logging.info("Modifying portal: " + portal.getName() + " property: " + property + " value: " + value); - // Simply chop off the rest, if they have loc, that's good enough! - if (property == SetProperties.loc || property == SetProperties.location) { - if (!issuer.isPlayer()) { - issuer.sendMessage("You must be a player to use location property!"); - return; + //todo: remove this in 6.0 + if (property.equalsIgnoreCase("dest") || property.equalsIgnoreCase("destination")) { + if (value.equalsIgnoreCase("here") && !worldManager.isWorld("here")) { + Logging.warning("Using 'here' as a destination is deprecated and will be removed in a future version. Use 'e:@here' instead."); + issuer.sendError("Using 'here' as a destination is deprecated and will be removed in a future version. Use 'e:@here' instead."); + value = "e:@here"; } - this.setLocation(portal, issuer.getPlayer()); - return; } - String propertyString = property.toString().toLowerCase(); - if (this.setProperty(portal, propertyString, value)) { - issuer.sendMessage("Property " + property + " of Portal " + ChatColor.YELLOW + portal.getName() + ChatColor.GREEN + " was set to " + ChatColor.AQUA + value); - } else { - issuer.sendMessage("Property " + property + " of Portal " + ChatColor.YELLOW + portal.getName() + ChatColor.RED + " was NOT set to " + ChatColor.AQUA + value); - if (propertyString.equalsIgnoreCase("dest") || propertyString.equalsIgnoreCase("destination")) { - issuer.sendMessage("Multiverse could not find the destination: " + ChatColor.GOLD + value); - } - } - } - private boolean setProperty(MVPortal selectedPortal, String property, String value) { - return selectedPortal.setProperty(property, value); - } - - private void setLocation(MVPortal selectedPortal, Player player) { - PortalPlayerSession ps = this.plugin.getPortalSession(player); - MultiverseRegion r = ps.getSelectedRegion(); - if (r != null) { - LoadedMultiverseWorld world = this.worldManager.getLoadedWorld(player.getWorld().getName()).getOrNull(); - PortalLocation location = new PortalLocation(r.getMinimumPoint(), r.getMaximumPoint(), world); - selectedPortal.setPortalLocation(location); - player.sendMessage("Portal location has been set to your " + ChatColor.GREEN + "selection" + ChatColor.WHITE + "!"); - } + String finalValue = value; + var stringPropertyHandle = portal.getStringPropertyHandle(); + stringPropertyHandle.setPropertyString(issuer.getIssuer(), property, value) + .onSuccess(ignore -> { + this.plugin.savePortalsConfig(); + issuer.sendMessage(ChatColor.GREEN + "Property " + ChatColor.AQUA + property + ChatColor.GREEN + + " of Portal " + ChatColor.YELLOW + portal.getName() + ChatColor.GREEN + " was set to " + + ChatColor.AQUA + stringPropertyHandle.getProperty(property).getOrNull()); + }).onFailure(failure -> { + issuer.sendError("Property " + ChatColor.AQUA + property + ChatColor.RED + " of Portal " + + ChatColor.YELLOW + portal.getName() + ChatColor.RED + " was NOT set to " + + ChatColor.AQUA + finalValue); + if (failure instanceof LocalizableMessage localizableMessage) { + issuer.sendError(localizableMessage.getLocalizableMessage()); + } else { + issuer.sendError(failure.getMessage()); + } + }); } @Service @@ -104,7 +92,7 @@ private final static class LegacyAlias extends ModifyCommand implements LegacyAl @Override @CommandAlias("mvpmodify|mvpm") - public void onModifyCommand(MVCommandIssuer issuer, MVPortal portal, SetProperties property, String value) { + public void onModifyCommand(MVCommandIssuer issuer, MVPortal portal, String property, String value) { super.onModifyCommand(issuer, portal, property, value); } } diff --git a/src/main/java/org/mvplugins/multiverse/portals/commands/SelectCommand.java b/src/main/java/org/mvplugins/multiverse/portals/commands/SelectCommand.java index ea71855b..5fc9757c 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/commands/SelectCommand.java +++ b/src/main/java/org/mvplugins/multiverse/portals/commands/SelectCommand.java @@ -3,7 +3,6 @@ import org.bukkit.ChatColor; import org.bukkit.entity.Player; import org.mvplugins.multiverse.core.command.LegacyAliasCommand; -import org.mvplugins.multiverse.core.command.MVCommandManager; import org.mvplugins.multiverse.external.acf.commands.annotation.CommandAlias; import org.mvplugins.multiverse.external.acf.commands.annotation.CommandCompletion; import org.mvplugins.multiverse.external.acf.commands.annotation.CommandPermission; @@ -17,15 +16,18 @@ import org.jvnet.hk2.annotations.Service; import org.mvplugins.multiverse.portals.MVPortal; import org.mvplugins.multiverse.portals.MultiversePortals; +import org.mvplugins.multiverse.portals.config.PortalsConfig; @Service class SelectCommand extends PortalsCommand { private final MultiversePortals plugin; + private final PortalsConfig portalsConfig; @Inject - SelectCommand(@NotNull MultiversePortals plugin) { + SelectCommand(@NotNull MultiversePortals plugin, @NotNull PortalsConfig portalsConfig) { this.plugin = plugin; + this.portalsConfig = portalsConfig; } @Subcommand("select") @@ -46,7 +48,7 @@ void onSelectCommand( MVPortal selected = this.plugin.getPortalSession(player).getSelectedPortal(); if (this.plugin.getPortalSession(player).getSelectedPortal() == null) { player.sendMessage("You have not selected a portal yet!"); - player.sendMessage("Use a " + ChatColor.GREEN + plugin.getWandMaterial() + ChatColor.WHITE + " to do so!"); + player.sendMessage("Use a " + ChatColor.GREEN + portalsConfig.getWandMaterial() + ChatColor.WHITE + " to do so!"); return; } player.sendMessage("You have selected: " + ChatColor.DARK_AQUA + selected.getName()); @@ -60,8 +62,8 @@ void onSelectCommand( @Service private final static class LegacyAlias extends SelectCommand implements LegacyAliasCommand { @Inject - LegacyAlias(MultiversePortals plugin) { - super(plugin); + LegacyAlias(MultiversePortals plugin, PortalsConfig portalsConfig) { + super(plugin, portalsConfig); } @Override diff --git a/src/main/java/org/mvplugins/multiverse/portals/commands/WandCommand.java b/src/main/java/org/mvplugins/multiverse/portals/commands/WandCommand.java index e23fe1c4..ba9382be 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/commands/WandCommand.java +++ b/src/main/java/org/mvplugins/multiverse/portals/commands/WandCommand.java @@ -18,15 +18,18 @@ import org.jvnet.hk2.annotations.Service; import org.mvplugins.multiverse.portals.MultiversePortals; import org.mvplugins.multiverse.portals.WorldEditConnection; +import org.mvplugins.multiverse.portals.config.PortalsConfig; @Service class WandCommand extends PortalsCommand { private final MultiversePortals plugin; + private final PortalsConfig portalsConfig; @Inject - WandCommand(@NotNull MultiversePortals plugin) { + WandCommand(@NotNull MultiversePortals plugin, @NotNull PortalsConfig portalsConfig) { this.plugin = plugin; + this.portalsConfig = portalsConfig; } @Subcommand("wand") @@ -63,7 +66,7 @@ void onWandCommand( player.sendMessage("Just use " + ChatColor.GOLD + "the WorldEdit wand " + ChatColor.WHITE + "to perform portal selections!"); return; } - ItemStack wand = new ItemStack(plugin.getWandMaterial(), 1); + ItemStack wand = new ItemStack(portalsConfig.getWandMaterial(), 1); if (player.getInventory().getItemInMainHand().getAmount() == 0) { player.getInventory().setItemInMainHand(wand); @@ -81,8 +84,8 @@ void onWandCommand( @Service private final static class LegacyAlias extends WandCommand implements LegacyAliasCommand { @Inject - LegacyAlias(MultiversePortals plugin) { - super(plugin); + LegacyAlias(MultiversePortals plugin, PortalsConfig portalsConfig) { + super(plugin, portalsConfig); } @Override diff --git a/src/main/java/org/mvplugins/multiverse/portals/config/PortalsConfig.java b/src/main/java/org/mvplugins/multiverse/portals/config/PortalsConfig.java new file mode 100644 index 00000000..57fc206b --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/portals/config/PortalsConfig.java @@ -0,0 +1,335 @@ +package org.mvplugins.multiverse.portals.config; + +import com.dumptruckman.minecraft.util.Logging; +import org.bukkit.Material; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.config.handle.CommentedConfigurationHandle; +import org.mvplugins.multiverse.core.config.handle.StringPropertyHandle; +import org.mvplugins.multiverse.core.config.migration.ConfigMigrator; +import org.mvplugins.multiverse.core.config.migration.VersionMigrator; +import org.mvplugins.multiverse.core.config.migration.action.MoveMigratorAction; +import org.mvplugins.multiverse.external.jakarta.inject.Inject; +import org.mvplugins.multiverse.external.vavr.control.Try; +import org.mvplugins.multiverse.portals.MultiversePortals; + +import java.nio.file.Path; +import java.util.List; + +/** + * + * @since 5.1 + */ +@ApiStatus.AvailableSince("5.1") +@Service +public final class PortalsConfig { + public static final String CONFIG_FILENAME = "config.yml"; + + private final Path configPath; + private final PortalsConfigNodes configNodes; + private final CommentedConfigurationHandle configHandle; + private final StringPropertyHandle stringPropertyHandle; + + @Inject + PortalsConfig(@NotNull MultiversePortals plugin, @NotNull PortalsConfigNodes configNodes) { + this.configPath = Path.of(plugin.getDataFolder().getPath(), CONFIG_FILENAME); + this.configNodes = configNodes; + this.configHandle = CommentedConfigurationHandle.builder(configPath, configNodes.getNodes()) + .logger(Logging.getLogger()) + .migrator(ConfigMigrator.builder(configNodes.version) + .addVersionMigrator(VersionMigrator.builder(5.0) + .addAction(MoveMigratorAction.of("wand", "portal-creation.wand-material")) + .addAction(MoveMigratorAction.of("useronmove", "portal-usage.use-on-move")) + .addAction(MoveMigratorAction.of("bucketfilling", "portal-creation.bucket-filling")) + .addAction(MoveMigratorAction.of("portalsdefaulttonether", "portal-usage.portals-default-to-nether")) + .addAction(MoveMigratorAction.of("enforceportalaccess", "portal-usage.enforce-portal-access")) + .addAction(MoveMigratorAction.of("portalcooldown", "portal-usage.portal-cooldown")) + .addAction(MoveMigratorAction.of("clearonremove", "portal-creation.clear-on-remove")) + .addAction(MoveMigratorAction.of("teleportvehicles", "portal-usage.teleport-vehicles")) + .addAction(MoveMigratorAction.of("netheranimation", "portal-usage.nether-animation")) + .addAction(MoveMigratorAction.of("framematerials", "portal-creation.frame-materials")) + .build()) + .build()) + .build(); + this.stringPropertyHandle = new StringPropertyHandle(configHandle); + } + + public Try load() { + return configHandle.load() + .onFailure(e -> { + Logging.severe("Failed to load Multiverse-Core config.yml!"); + e.printStackTrace(); + }); + } + + public boolean isLoaded() { + return configHandle.isLoaded(); + } + + public Try save() { + return configHandle.save() + .onFailure(e -> { + Logging.severe("Failed to save Multiverse-Core config.yml!"); + e.printStackTrace(); + }); + } + + /** + * + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public StringPropertyHandle getStringPropertyHandle() { + return stringPropertyHandle; + } + + /** + * + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public Material getWandMaterial() { + return configHandle.get(configNodes.wandMaterial); + } + + /** + * + * @param wand + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public Try setWandMaterial(Material wand) { + return configHandle.set(configNodes.wandMaterial, wand); + } + + /** + * + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public boolean getBucketFilling() { + return configHandle.get(configNodes.bucketFilling); + } + + + /** + * + * @param bucketFilling + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public Try setBucketFilling(boolean bucketFilling) { + return configHandle.set(configNodes.bucketFilling, bucketFilling); + } + + /** + * + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public List getFrameMaterials() { + return configHandle.get(configNodes.frameMaterials); + } + + /** + * + * @param frameMaterials + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public Try setFrameMaterials(List frameMaterials) { + return configHandle.set(configNodes.frameMaterials, frameMaterials); + } + + /** + * + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public boolean getClearOnRemove() { + return configHandle.get(configNodes.clearOnRemove); + } + + /** + * + * @param clearOnRemove + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public Try setClearOnRemove(boolean clearOnRemove) { + return configHandle.set(configNodes.clearOnRemove, clearOnRemove); + } + + /** + * + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public boolean getEnforcePortalAccess() { + return configHandle.get(configNodes.enforcePortalAccess); + } + + /** + * + * @param enforcePortalAccess + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public Try setEnforcePortalAccess(boolean enforcePortalAccess) { + return configHandle.set(configNodes.enforcePortalAccess, enforcePortalAccess); + } + + /** + * + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public int getPortalCooldown() { + return configHandle.get(configNodes.portalCooldown); + } + + /** + * + * @param portalCooldown + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public Try setPortalCooldown(int portalCooldown) { + return configHandle.set(configNodes.portalCooldown, portalCooldown); + } + + /** + * + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public boolean getPortalsDefaultToNether() { + return configHandle.get(configNodes.portalsDefaultToNether); + } + + /** + * + * @param portalsDefaultToNether + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public Try setPortalsDefaultToNether(boolean portalsDefaultToNether) { + return configHandle.set(configNodes.portalsDefaultToNether, portalsDefaultToNether); + } + + /** + * + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public boolean getNetherAnimation() { + return configHandle.get(configNodes.netherAnimation); + } + + /** + * + * @param netherAnimation + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public Try setNetherAnimation(boolean netherAnimation) { + return configHandle.set(configNodes.netherAnimation, netherAnimation); + } + + /** + * + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public boolean getTeleportVehicles() { + return configHandle.get(configNodes.teleportVehicles); + } + + /** + * + * @param teleportVehicles + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public Try setTeleportVehicles(boolean teleportVehicles) { + return configHandle.set(configNodes.teleportVehicles, teleportVehicles); + } + + /** + * + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public boolean getUseOnMove() { + return configHandle.get(configNodes.useOnMove); + } + + /** + * + * @param useOnMove + * @return + * + * @since 5.1 + */ + @ApiStatus.AvailableSince("5.1") + public Try setUseOnMove(boolean useOnMove) { + return configHandle.set(configNodes.useOnMove, useOnMove); + } + + /** + * + * @return + * + * @since 5.1 + * + * @deprecated Only here to allow for backwards compatibility with old methods in {@link MultiversePortals} class. + */ + @ApiStatus.AvailableSince("5.1") + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") + public FileConfiguration getConfig() { + return configHandle.getConfig(); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/portals/config/PortalsConfigNodes.java b/src/main/java/org/mvplugins/multiverse/portals/config/PortalsConfigNodes.java new file mode 100644 index 00000000..98111c42 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/portals/config/PortalsConfigNodes.java @@ -0,0 +1,178 @@ +package org.mvplugins.multiverse.portals.config; + +import org.bukkit.Material; +import org.jvnet.hk2.annotations.Service; +import org.mvplugins.multiverse.core.config.node.ConfigHeaderNode; +import org.mvplugins.multiverse.core.config.node.ConfigNode; +import org.mvplugins.multiverse.core.config.node.ListConfigNode; +import org.mvplugins.multiverse.core.config.node.Node; +import org.mvplugins.multiverse.core.config.node.NodeGroup; +import org.mvplugins.multiverse.core.config.node.serializer.NodeSerializer; +import org.mvplugins.multiverse.core.utils.MaterialConverter; +import org.mvplugins.multiverse.portals.MultiversePortals; + +import java.util.ArrayList; + +@Service +final class PortalsConfigNodes { + + private final NodeGroup nodes = new NodeGroup(); + + NodeGroup getNodes() { + return nodes; + } + + private N node(N node) { + nodes.add(node); + return node; + } + + private final ConfigHeaderNode portalCreationHeader = node(ConfigHeaderNode.builder("portal-creation") + .comment("#-------------------------------------------------------------------------------------------------------#") + .comment("# #") + .comment("# __ __ _ _ _ _____ _____ _____ ___ ___ ___ ___ ___ ___ _____ _ _ ___ #") + .comment("# | \\/ | | | | ||_ _|_ _\\ \\ / / __| _ \\/ __| __| | _ \\/ _ \\| _ \\_ _/_\\ | | / __| #") + .comment("# | |\\/| | |_| | |__| | | | \\ V /| _|| /\\__ \\ _| | _/ (_) | / | |/ _ \\| |__\\__ \\ #") + .comment("# |_| |_|\\___/|____|_| |___| \\_/ |___|_|_\\|___/___| |_| \\___/|_|_\\ |_/_/ \\_\\____|___/ #") + .comment("# #") + .comment("# #") + .comment("# WIKI: https://mvplugins.org/portals/fundamentals/basic-usage/ #") + .comment("# DISCORD: https://discord.gg/NZtfKky #") + .comment("# BUG REPORTS: https://github.com/Multiverse/Multiverse-Portals/issues #") + .comment("# #") + .comment("# #") + .comment("# New options are added to this file automatically. If you manually made changes #") + .comment("# to this file while your server is running, please run `/mvp reload` command. #") + .comment("# #") + .comment("#-------------------------------------------------------------------------------------------------------#") + .comment("") + .comment("") + .build()); + + // todo: node serialization for materials + final ConfigNode wandMaterial = node(ConfigNode.builder("portal-creation.wand-material", Material.class) + .comment("The item used to select a region to create a mvportal. Run `/mv wand` to start selection.") + .comment("This will be ignore if worldedit is used for selection instead.") + .defaultValue(Material.WOODEN_PICKAXE) + .name("wand-material") + .serializer(new NodeSerializer<>() { + @Override + public Material deserialize(Object o, Class aClass) { + return MaterialConverter.stringToMaterial(String.valueOf(o)); + } + + @Override + public Object serialize(Material material, Class aClass) { + return material.name().toLowerCase(); + } + }) + .build()); + + final ConfigNode bucketFilling = node(ConfigNode.builder("portal-creation.bucket-filling", Boolean.class) + .comment("") + .comment("If enabled, water and lava bucket can be used to fill a mvportal.") + .defaultValue(true) + .name("bucket-filling") + .onSetValue((oldValue, newValue) -> MultiversePortals.bucketFilling = newValue) + .build()); + + // todo: node serialization for materials + final ListConfigNode frameMaterials = node(ListConfigNode.listBuilder("portal-creation.frame-materials", Material.class) + .comment("") + .comment("The allowed materials used to create the frame of the portal.") + .comment("If the list is empty, all materials are allowed.") + .defaultValue(ArrayList::new) + .name("frame-materials") + .itemSerializer(new NodeSerializer<>() { + @Override + public Material deserialize(Object o, Class aClass) { + return MaterialConverter.stringToMaterial(String.valueOf(o)); + } + + @Override + public Object serialize(Material material, Class aClass) { + return material.name().toLowerCase(); + } + }) + .onSetValue((oldValue, newValue) -> MultiversePortals.FrameMaterials = newValue) + .build()); + + final ConfigNode clearOnRemove = node(ConfigNode.builder("portal-creation.clear-on-remove", Boolean.class) + .comment("") + .comment("If enabled, nether/end fills will be cleared when the portal is removed.") + .comment("This keeps us from leaving behind portal blocks (which would take an unsuspecting player to the nether when trying to use the mvportal)") + .defaultValue(false) + .name("clear-on-remove") + .aliases("clearonremove") + .onSetValue((oldValue, newValue) -> MultiversePortals.ClearOnRemove = newValue) + .build()); + + private final ConfigHeaderNode portalUsageHeader = node(ConfigHeaderNode.builder("portal-usage") + .comment("") + .comment("") + .build()); + + final ConfigNode enforcePortalAccess = node(ConfigNode.builder("portal-usage.enforce-portal-access", Boolean.class) + .comment("If enabled, players will not be able to teleport to mvportals they do not have access to.") + .comment("The permission node is: `multiverse.portal.access.PORTALNAME`") + .defaultValue(true) + .name("enforce-portal-access") + .aliases("enforceportalaccess") + .onSetValue((oldValue, newValue) -> MultiversePortals.EnforcePortalAccess = newValue) + .build()); + + final ConfigNode portalCooldown = node(ConfigNode.builder("portal-usage.portal-cooldown", Integer.class) + .comment("") + .comment("The number of seconds a player must wait between using a mvportal.") + .defaultValue(1000) + .name("portal-cooldown") + .aliases("portalcooldown") + .build()); + + final ConfigNode portalsDefaultToNether = node(ConfigNode.builder("portal-usage.portals-default-to-nether", Boolean.class) + .comment("") + .comment("If enabled, when a mvportal with nether fill that is unusable, either due to invalid destination or lack of permissions,") + .comment("will fallback and behave as a normal nether portal teleporting between the nether world.") + .defaultValue(false) + .name("portals-default-to-nether") + .aliases("portalsdefaulttonether") + .build()); + + final ConfigNode netherAnimation = node(ConfigNode.builder("portal-usage.nether-animation", Boolean.class) + .comment("") + .comment("If enabled, the nether blobbing animation will be played before the player is teleported.") + .comment("Note: This does not work if the player is in creative mode due to server software limitations.") + .defaultValue(true) + .name("nether-animation") + .onSetValue((oldValue, newValue) -> MultiversePortals.NetherAnimation = newValue) + .build()); + + final ConfigNode teleportVehicles = node(ConfigNode.builder("portal-usage.teleport-vehicles", Boolean.class) + .comment("") + .comment("If enabled, mvportals will teleport all vehicles along with its passengers when the vehicle enters the portal.") + .comment("Vehicles are usually boats, minecarts, pigs and horses.") + .defaultValue(false) + .name("teleport-vehicles") + .aliases("teleportvehicles") + .onSetValue((oldValue, newValue) -> MultiversePortals.TeleportVehicles = newValue) + .build()); + + final ConfigNode useOnMove = node(ConfigNode.builder("portal-usage.use-on-move", Boolean.class) + .comment("") + .comment("If enabled, player movement will be tracked to determine if the player has entered a portal.") + .comment("Disabling this will cause mvportals without nether or end fill to not work.") + .defaultValue(true) + .name("use-on-move") + .aliases("useonmove") + .onSetValue((oldValue, newValue) -> MultiversePortals.UseOnMove = newValue) + .build()); + + final ConfigNode version = node(ConfigNode.builder("version", Double.class) + .comment("") + .comment("") + .comment("This just signifies the version number so we can see what version of config you have.") + .comment("NEVER TOUCH THIS VALUE") + .defaultValue(0.0) + .hidden() + .build()); +} diff --git a/src/main/java/org/mvplugins/multiverse/portals/enums/PortalConfigProperty.java b/src/main/java/org/mvplugins/multiverse/portals/enums/PortalConfigProperty.java index db0b3788..6faea623 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/enums/PortalConfigProperty.java +++ b/src/main/java/org/mvplugins/multiverse/portals/enums/PortalConfigProperty.java @@ -7,16 +7,29 @@ package org.mvplugins.multiverse.portals.enums; +import org.jetbrains.annotations.ApiStatus; +import org.mvplugins.multiverse.core.config.handle.StringPropertyHandle; +import org.mvplugins.multiverse.portals.config.PortalsConfig; + /** * Multiverse 2 * * @author fernferret + * + * @deprecated Use {@link PortalsConfig#getStringPropertyHandle()} instead. */ +@Deprecated(since = "5.1", forRemoval = true) +@ApiStatus.ScheduledForRemoval(inVersion = "6.0") public enum PortalConfigProperty { wand, useonmove, portalsdefaulttonether, enforceportalaccess, portalcooldown, clearonremove, teleportvehicles; + /** + * @deprecated Use {@link PortalsConfig#getStringPropertyHandle()} and {@link StringPropertyHandle#getAllPropertyNames()} instead. + */ + @Deprecated(since = "5.1", forRemoval = true) + @ApiStatus.ScheduledForRemoval(inVersion = "6.0") public static String getAllValues() { String buffer = ""; for (PortalConfigProperty c : PortalConfigProperty.values()) { diff --git a/src/main/java/org/mvplugins/multiverse/portals/enums/SetProperties.java b/src/main/java/org/mvplugins/multiverse/portals/enums/SetProperties.java index 6900ebd1..1c673488 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/enums/SetProperties.java +++ b/src/main/java/org/mvplugins/multiverse/portals/enums/SetProperties.java @@ -7,6 +7,14 @@ package org.mvplugins.multiverse.portals.enums; +import org.jetbrains.annotations.ApiStatus; +import org.mvplugins.multiverse.portals.MVPortal; + +/** + * @deprecated Use {@link MVPortal#getStringPropertyHandle()} instead to get property names. + */ +@Deprecated(since = "5.1", forRemoval = true) +@ApiStatus.ScheduledForRemoval(inVersion = "6.0") public enum SetProperties { destination, dest, owner, loc, location, price, currency, curr, safe, telenonplayers, handlerscript } diff --git a/src/main/java/org/mvplugins/multiverse/portals/listeners/MVPPlayerListener.java b/src/main/java/org/mvplugins/multiverse/portals/listeners/MVPPlayerListener.java index f8bd7357..3992d655 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/listeners/MVPPlayerListener.java +++ b/src/main/java/org/mvplugins/multiverse/portals/listeners/MVPPlayerListener.java @@ -21,6 +21,7 @@ import org.mvplugins.multiverse.portals.MultiversePortals; import org.mvplugins.multiverse.portals.PortalPlayerSession; import org.mvplugins.multiverse.portals.WorldEditConnection; +import org.mvplugins.multiverse.portals.config.PortalsConfig; import org.mvplugins.multiverse.portals.event.MVPortalEvent; import org.mvplugins.multiverse.portals.utils.PortalFiller; import org.mvplugins.multiverse.portals.utils.PortalManager; @@ -45,6 +46,7 @@ public class MVPPlayerListener implements PortalsListener { private final MultiversePortals plugin; + private final PortalsConfig portalsConfig; private final PortalFiller filler; private final PortalManager portalManager; private final PlayerListenerHelper helper; @@ -56,6 +58,7 @@ public class MVPPlayerListener implements PortalsListener { @Inject MVPPlayerListener( @NotNull MultiversePortals plugin, + @NotNull PortalsConfig portalsConfig, @NotNull PlayerListenerHelper helper, @NotNull PortalManager portalManager, @NotNull PortalFiller filler, @@ -64,6 +67,7 @@ public class MVPPlayerListener implements PortalsListener { @NotNull BlockSafety blockSafety, @NotNull MVEconomist economist) { this.plugin = plugin; + this.portalsConfig = portalsConfig; this.helper = helper; this.portalManager = portalManager; this.filler = filler; @@ -119,7 +123,7 @@ public void playerBucketEmpty(PlayerBucketEmptyEvent event) { return; } - if (!MultiversePortals.bucketFilling) { + if (!portalsConfig.getBucketFilling()) { Logging.fine("The bucket filling functionality has been disabled in config, doing nothing"); return; } @@ -205,7 +209,7 @@ public void playerInteract(PlayerInteractEvent event) { return; } - Material itemType = plugin.getWandMaterial(); + Material itemType = portalsConfig.getWandMaterial(); // If we Found WorldEdit, return, we're not needed here. // If the item is not the Wand we've setup we're not needed either // If the player doesn't have the perms, return also. @@ -299,7 +303,7 @@ public void playerPortal(PlayerPortalEvent event) { } // If they're using Access and they don't have permission and they're NOT exempt, return, they're not allowed to tp. // No longer checking exemption status - if (MultiversePortals.EnforcePortalAccess && !event.getPlayer().hasPermission(portal.getPermission())) { + if (portalsConfig.getEnforcePortalAccess() && !event.getPlayer().hasPermission(portal.getPermission())) { this.helper.stateFailure(p.getDisplayName(), portal.getName()); return; } @@ -337,7 +341,7 @@ public void playerPortal(PlayerPortalEvent event) { } event.getPlayer().teleport(event.getTo()); - } else if (!this.plugin.getMainConfig().getBoolean("portalsdefaulttonether", false)) { + } else if (!portalsConfig.getPortalsDefaultToNether()) { // If portals should not default to the nether, cancel the event event.getPlayer().sendMessage(String.format( "This portal %sdoesn't go anywhere. You should exit it now.", ChatColor.RED)); diff --git a/src/main/java/org/mvplugins/multiverse/portals/listeners/MVPPlayerMoveListener.java b/src/main/java/org/mvplugins/multiverse/portals/listeners/MVPPlayerMoveListener.java index 52f59cb0..94bfcefd 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/listeners/MVPPlayerMoveListener.java +++ b/src/main/java/org/mvplugins/multiverse/portals/listeners/MVPPlayerMoveListener.java @@ -18,6 +18,7 @@ import org.mvplugins.multiverse.portals.MVPortal; import org.mvplugins.multiverse.portals.MultiversePortals; import org.mvplugins.multiverse.portals.PortalPlayerSession; +import org.mvplugins.multiverse.portals.config.PortalsConfig; import org.mvplugins.multiverse.portals.enums.MoveType; import org.mvplugins.multiverse.portals.event.MVPortalEvent; import org.bukkit.ChatColor; @@ -34,6 +35,7 @@ public class MVPPlayerMoveListener implements Listener { private final MultiversePortals plugin; + private final PortalsConfig portalsConfig; private final PlayerListenerHelper helper; private final PortalManager portalManager; private final WorldManager worldManager; @@ -42,11 +44,13 @@ public class MVPPlayerMoveListener implements Listener { @Inject MVPPlayerMoveListener( @NotNull MultiversePortals plugin, + @NotNull PortalsConfig portalsConfig, @NotNull PlayerListenerHelper helper, @NotNull PortalManager portalManager, @NotNull WorldManager worldManager, @NotNull MVEconomist economist) { this.plugin = plugin; + this.portalsConfig = portalsConfig; this.helper = helper; this.portalManager = portalManager; this.worldManager = worldManager; @@ -71,7 +75,7 @@ public void blockFromTo(BlockFromToEvent event) { return; } // If something is trying to flow out, stop that too, unless bucketFilling has been disabled - if (portalManager.isPortal(event.getBlock().getLocation()) && MultiversePortals.bucketFilling) { + if (portalManager.isPortal(event.getBlock().getLocation()) && portalsConfig.getBucketFilling()) { event.setCancelled(true); } } @@ -96,7 +100,7 @@ public void playerMove(PlayerMoveEvent event) { // If the portal is not null, and it's a legacy portal, // and we didn't show debug info (the debug is meant to toggle), do the stuff. if (portal != null - && (!MultiversePortals.NetherAnimation || portal.isLegacyPortal()) + && (!portalsConfig.getNetherAnimation() || portal.isLegacyPortal()) && ps.doTeleportPlayer(MoveType.PLAYER_MOVE) && !ps.showDebugInfo()) { @@ -126,7 +130,7 @@ public void playerMove(PlayerMoveEvent event) { } // If they're using Access and they don't have permission and they're NOT excempt, return, they're not allowed to tp. // No longer checking exemption status - if (MultiversePortals.EnforcePortalAccess && !event.getPlayer().hasPermission(portal.getPermission())) { + if (portalsConfig.getEnforcePortalAccess() && !event.getPlayer().hasPermission(portal.getPermission())) { this.helper.stateFailure(p.getDisplayName(), portal.getName()); return; } diff --git a/src/main/java/org/mvplugins/multiverse/portals/listeners/MVPVehicleListener.java b/src/main/java/org/mvplugins/multiverse/portals/listeners/MVPVehicleListener.java index c6b935c7..fccba0ab 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/listeners/MVPVehicleListener.java +++ b/src/main/java/org/mvplugins/multiverse/portals/listeners/MVPVehicleListener.java @@ -7,29 +7,27 @@ package org.mvplugins.multiverse.portals.listeners; -import java.util.Date; +import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CompletableFuture; import com.dumptruckman.minecraft.util.Logging; -import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.event.Listener; -import org.mvplugins.multiverse.core.destination.DestinationInstance; +import org.mvplugins.multiverse.core.economy.MVEconomist; +import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker; import org.mvplugins.multiverse.core.teleportation.LocationManipulation; import org.mvplugins.multiverse.core.teleportation.AsyncSafetyTeleporter; +import org.mvplugins.multiverse.core.teleportation.PassengerModes; import org.mvplugins.multiverse.external.jakarta.inject.Inject; import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull; import org.jvnet.hk2.annotations.Service; -import org.mvplugins.multiverse.external.paperlib.PaperLib; -import org.mvplugins.multiverse.portals.destination.PortalDestinationInstance; +import org.mvplugins.multiverse.portals.config.PortalsConfig; import org.mvplugins.multiverse.portals.enums.MoveType; -import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.entity.Vehicle; import org.bukkit.event.EventHandler; import org.bukkit.event.vehicle.VehicleMoveEvent; -import org.bukkit.util.Vector; import org.mvplugins.multiverse.portals.MVPortal; import org.mvplugins.multiverse.portals.MultiversePortals; @@ -40,187 +38,109 @@ public class MVPVehicleListener implements Listener { private final MultiversePortals plugin; private final PortalManager portalManager; - private final LocationManipulation locationManipulation; private final AsyncSafetyTeleporter safetyTeleporter; + private final PortalsConfig portalsConfig; + private final MVEconomist economist; @Inject MVPVehicleListener( @NotNull MultiversePortals plugin, @NotNull PortalManager portalManager, - @NotNull LocationManipulation locationManipulation, - @NotNull AsyncSafetyTeleporter safetyTeleporter) { + @NotNull AsyncSafetyTeleporter safetyTeleporter, + @NotNull PortalsConfig portalsConfig, + @NotNull MVEconomist economist + ) { this.plugin = plugin; this.portalManager = portalManager; - this.locationManipulation = locationManipulation; this.safetyTeleporter = safetyTeleporter; + this.portalsConfig = portalsConfig; + this.economist = economist; } @EventHandler public void vehicleMove(VehicleMoveEvent event) { - // todo: add support for multiple passengers, e.g. boats - if (event.getVehicle().getPassenger() instanceof Player) { - Vehicle v = event.getVehicle(); - Player p = (Player) v.getPassenger(); - PortalPlayerSession ps = this.plugin.getPortalSession(p); - ps.setStaleLocation(v.getLocation(), MoveType.VEHICLE_MOVE); - - if (ps.isStaleLocation()) { - return; - } - - // Teleport the Player - if (!teleportVehicle(p, v, event.getTo())) { - // todo: revamp vehicle teleport in 5.1 - // Logging.warning("Failed to teleport vehicle: " + event.getVehicle()); - } - } else { - MVPortal portal = this.portalManager.getPortal(event.getFrom()); - if ((portal != null) && (portal.getTeleportNonPlayers())) { - DestinationInstance dest = portal.getDestination(); - if (dest == null) - return; - - // Check the portal's frame. - if (!portal.isFrameValid(event.getVehicle().getLocation())) { - return; - } - - Class vehicleClass = event.getVehicle().getType().getEntityClass(); - if (vehicleClass == null) { - Logging.warning("No vehicle class found for vehicle: " + event.getVehicle()); + Vehicle vehicle = event.getVehicle(); + List playerPassengers = new ArrayList<>(); + boolean hasNonPlayers = false; + for (Entity entity : vehicle.getPassengers()) { + if (entity instanceof Player player) { + PortalPlayerSession ps = this.plugin.getPortalSession(player); + ps.setStaleLocation(vehicle.getLocation(), MoveType.VEHICLE_MOVE); + if (ps.isStaleLocation()) { + Logging.finer("Player %s is stale, not teleporting vehicle", player.getName()); return; } + playerPassengers.add(player); + } else { + hasNonPlayers = true; + } + } - Vector vehicleVec = event.getVehicle().getVelocity(); - Location target = dest.getLocation(event.getVehicle()).getOrNull(); - if (dest instanceof PortalDestinationInstance pd) { - // Translate the direction of travel. - vehicleVec = this.locationManipulation.getTranslatedVector(vehicleVec, pd.getDirection()); - } + MVPortal portal = this.portalManager.getPortal(vehicle.getLocation()); + if (portal == null) { + return; + } - this.setVehicleVelocity(vehicleVec, dest, event.getVehicle()); - - List formerPassengers = event.getVehicle().getPassengers(); - event.getVehicle().eject(); - - Entity newVehicle = target.getWorld().spawn(target, vehicleClass); - Vector finalVehicleVec = vehicleVec; - CompletableFuture.allOf(formerPassengers.stream() - .map(passenger -> PaperLib.teleportAsync(passenger, target)) - .toArray(CompletableFuture[]::new)) - .thenRun(() -> { - Bukkit.getScheduler().runTask(plugin, () -> { - formerPassengers.forEach(newVehicle::addPassenger); - this.setVehicleVelocity(finalVehicleVec, dest, newVehicle); - }); - }); - event.getVehicle().remove(); - } + if (hasNonPlayers && !portal.getTeleportNonPlayers()) { + Logging.finer("Not teleporting vehicle using %s because it has non-player passengers", portal.getName()); + return; } - } - private boolean teleportVehicle(Player p, Vehicle v, Location to) { - PortalPlayerSession ps = this.plugin.getPortalSession(p); - MVPortal portal = ps.getStandingInPortal(); - // If the portal is not null - // AND if we did not show debug info, do the stuff - // The debug is meant to toggle. - if (portal != null && ps.doTeleportPlayer(MoveType.VEHICLE_MOVE) && !ps.showDebugInfo()) { - if (ps.checkAndSendCooldownMessage()) { - return false; - } - // TODO: Money - DestinationInstance d = portal.getDestination(); - if (d == null) { - return false; - } + // Check the portal's frame. + if (!portal.isFrameValid(vehicle.getLocation())) { + Logging.finer("Not teleporting vehicle using %s as the frame is made of an incorrect material.", + portal.getName()); + return; + } - // Check the portal's frame. - if (!portal.isFrameValid(v.getLocation())) { - return false; + for (Player player : playerPassengers) { + if (!checkPlayerCanUsePortal(portal, player)) { + Logging.finer("Player %s is not allowed to use portal %s, removing them from the vehicle.", + player.getName(), portal.getName()); + vehicle.removePassenger(player); } + } - Location l = d.getLocation(p).getOrNull(); - Vector vehicleVec = v.getVelocity(); + safetyTeleporter.to(portal.getDestination()) + .passengerMode(PassengerModes.RETAIN_ALL) + .teleportSingle(vehicle) + .onSuccess(() -> Logging.finer("Successfully teleported vehicle %s using portal %s", + vehicle.getName(), portal.getName())) + .onFailure(failures -> Logging.finer("Failed to teleport vehicle %s using portal %s. Failures: %s", + vehicle.getName(), portal.getName(), failures)); + } - // 0 Yaw in dest = 0,X - if (d instanceof PortalDestinationInstance pd) { - // Translate the direction of travel. - vehicleVec = this.locationManipulation.getTranslatedVector(vehicleVec, pd.getDirection()); - } + // todo: this logic is duplicated in multiple places + private boolean checkPlayerCanUsePortal(MVPortal portal, Player player) { + // If they're using Access and they don't have permission and they're NOT exempt, return, they're not allowed to tp. + // No longer checking exemption status + if (portalsConfig.getEnforcePortalAccess() && !player.hasPermission(portal.getPermission())) { - // Set the velocity - // Will set to the destination's velocity if one is present - // Or - this.setVehicleVelocity(vehicleVec, d, v); + return false; + } - p.setFallDistance(0); + boolean shouldPay = false; + double price = portal.getPrice(); + Material currency = portal.getCurrency(); - // The worlds are different! Ahhh! - if (!l.getWorld().equals(p.getWorld())) { - return teleportVehicleSeperately(p, v, d, ps); + // Stop the player if the portal costs and they can't pay + if (price != 0D && !player.hasPermission(portal.getExempt())) { + shouldPay = true; + if (price > 0D && !economist.isPlayerWealthyEnough(player, price, currency)) { + player.sendMessage(economist.getNSFMessage(currency, + "You need " + economist.formatPrice(price, currency) + " to enter the " + portal.getName() + " portal.")); + return false; } - - this.safetyTeleporter.to(d).by(p).teleport(v) - .onSuccess(() -> { - ps.playerDidTeleport(to); - ps.setTeleportTime(new Date()); - }); - return true; } - return false; - } - private boolean teleportVehicleSeperately( - Player player, - Vehicle vehicle, - DestinationInstance destination, - PortalPlayerSession ps) { - // Remove the player from the old one. - vehicle.eject(); - Location vehicleToLocation = destination.getLocation(vehicle).getOrNull(); - Location playerToLocation = destination.getLocation(player).getOrNull(); - // Add an offset to ensure the player is 1 higher than where the cart was. - playerToLocation.add(0, 0.5, 0); - - Class vehicleClass = vehicle.getType().getEntityClass(); - if (vehicleClass == null) { - Logging.warning("No vehicle class found for vehicle: " + vehicle); - return false; + if (shouldPay) { + if (price < 0D) { + economist.deposit(player, -price, currency); + } else { + economist.withdraw(player, price, currency); + } } - // Now create a new vehicle: - Vehicle newVehicle = (Vehicle) vehicleToLocation.getWorld().spawn(vehicleToLocation, vehicleClass); - - safetyTeleporter.to(destination).teleport(player) - .onSuccess(() -> { - Bukkit.getScheduler().runTask(plugin, () -> { - // Set the new player - if (newVehicle.addPassenger(player)) { - Logging.finer("Added passenger to new vehicle: " + newVehicle); - } else { - Logging.warning("Failed to add passenger to new vehicle: " + newVehicle); - } - // Set the vehicle's velocity to ours. - this.setVehicleVelocity(vehicle.getVelocity(), destination, newVehicle); - // They did teleport. Let's delete the old vehicle. - vehicle.remove(); - }); - }) - .onFailure(reason -> { - Logging.fine("Failed to teleport player '%s' to destination '%s'. Reason: %s", player.getDisplayName(), destination, reason); - vehicle.addPassenger(player); - }); return true; } - - private void setVehicleVelocity(Vector calculated, DestinationInstance to, Entity newVehicle) { - // If the destination has a non-zero velocity, use that, - // otherwise use the existing velocity, because velocities - // are preserved through portals... duh. - to.getVelocity(newVehicle) - .filter(v -> !v.equals(new Vector(0, 0, 0))) - .peek(newVehicle::setVelocity) - .onEmpty(() -> newVehicle.setVelocity(calculated)); - } } diff --git a/src/main/java/org/mvplugins/multiverse/portals/utils/MultiverseRegion.java b/src/main/java/org/mvplugins/multiverse/portals/utils/MultiverseRegion.java index 53a41b30..ac85be6b 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/utils/MultiverseRegion.java +++ b/src/main/java/org/mvplugins/multiverse/portals/utils/MultiverseRegion.java @@ -89,4 +89,14 @@ public boolean containsVector(Location l) { return true; } + @Override + public String toString() { + return this.world.getName() + ":" + + this.min.getX() + "," + + this.min.getY() + "," + + this.min.getZ() + ":" + + this.max.getX() + "," + + this.max.getY() + "," + + this.max.getZ(); + } } diff --git a/src/main/java/org/mvplugins/multiverse/portals/utils/PortalManager.java b/src/main/java/org/mvplugins/multiverse/portals/utils/PortalManager.java index a5c733ad..031d9ac4 100644 --- a/src/main/java/org/mvplugins/multiverse/portals/utils/PortalManager.java +++ b/src/main/java/org/mvplugins/multiverse/portals/utils/PortalManager.java @@ -31,6 +31,7 @@ import org.mvplugins.multiverse.portals.MVPortal; import org.mvplugins.multiverse.portals.MultiversePortals; import org.mvplugins.multiverse.portals.PortalLocation; +import org.mvplugins.multiverse.portals.config.PortalsConfig; /** @@ -42,6 +43,7 @@ public class PortalManager { private final MultiversePortals plugin; private final WorldManager worldManager; + private final PortalsConfig portalsConfig; private final Map portals; // For each world, keep a map of chunk hashes (see hashChunk()) to lists of @@ -52,9 +54,12 @@ public class PortalManager { private static final Collection emptyPortalSet = new ArrayList(); @Inject - PortalManager(@NotNull MultiversePortals plugin, @NotNull WorldManager worldManager) { + PortalManager(@NotNull MultiversePortals plugin, + @NotNull WorldManager worldManager, + @NotNull PortalsConfig portalsConfig) { this.plugin = plugin; this.worldManager = worldManager; + this.portalsConfig = portalsConfig; this.portals = new HashMap<>(); this.worldChunkPortals = new HashMap<>(); } @@ -90,7 +95,7 @@ public MVPortal getPortal(Player sender, Location l, boolean checkPermission) { for (MVPortal portal : getNearbyPortals(world, l)) { // Ignore portals the player can't use. - if (!checkPermission || !MultiversePortals.EnforcePortalAccess || portal.playerCanEnterPortal(sender)) { + if (!checkPermission || !portalsConfig.getEnforcePortalAccess() || portal.playerCanEnterPortal(sender)) { PortalLocation portalLoc = portal.getLocation(); if (portalLoc.isValidLocation() && portalLoc.getRegion().containsVector(l)) { return portal; @@ -157,6 +162,7 @@ public boolean addPortal(MultiverseWorld world, String name, String owner, Porta // Add a portal whose name is already known to be unique. private void addUniquePortal(MultiverseWorld world, String name, MVPortal portal) { this.portals.put(name, portal); + this.plugin.savePortalsConfig(); addToWorldChunkPortals(world, portal); } @@ -196,7 +202,7 @@ private MVPortal removePortal(String portalName, boolean removeFromConfigs, bool recalculatePermissions(); } - if (MultiversePortals.ClearOnRemove) { + if (portalsConfig.getClearOnRemove()) { // Replace portal blocks in the portal with air. This keeps us from // leaving behind portal blocks (which would take an unsuspecting // player to the nether instead of their expected destination). @@ -228,7 +234,7 @@ public List getPortals(CommandSender sender) { } List all = this.getAllPortals(); List validItems = new ArrayList(); - if (MultiversePortals.EnforcePortalAccess) { + if (portalsConfig.getEnforcePortalAccess()) { for (MVPortal p : all) { if (p.playerCanEnterPortal((Player) sender)) { validItems.add(p); @@ -258,7 +264,7 @@ public List getPortals(CommandSender sender, MultiverseWorld world) { } List all = this.getAllPortals(); List validItems = new ArrayList(); - if (MultiversePortals.EnforcePortalAccess) { + if (portalsConfig.getEnforcePortalAccess()) { for (MVPortal p : all) { if (p.getLocation().isValidLocation() && p.getLocation().getMVWorld().equals(world) && p.playerCanEnterPortal((Player) sender)) { diff --git a/src/main/resources/defaults/config.yml b/src/main/resources/defaults/config.yml deleted file mode 100644 index fd7bd4c7..00000000 --- a/src/main/resources/defaults/config.yml +++ /dev/null @@ -1,18 +0,0 @@ -# This is the MV-Portals Config. If you mess it up, copy the values out -# delete it, and it will be regenerated. Then use the ingame interface -# to add your values back via the "/mvp conf" command. -# When in-game, simply type: "/mvp conf ?" for help. -# A config with explanations can be found here: -# https://github.com/Multiverse/Multiverse-Core/wiki/config.yml-(Portals) - -wand: 271 -useonmove: true -bucketfilling: true -portalsdefaulttonether: false -enforceportalaccess: true -portalcooldown: 1000 -clearonremove: false -teleportvehicles: true -netheranimation: true -framematerials: [] -version: 2.7