diff --git a/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java b/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java index 3038e408..03b97e39 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java +++ b/src/main/java/com/onarandombox/MultiverseCore/MultiverseCore.java @@ -26,10 +26,13 @@ import com.onarandombox.MultiverseCore.utils.TestingMode; import com.onarandombox.MultiverseCore.utils.metrics.MetricsConfigurator; import com.onarandombox.MultiverseCore.world.WorldProperties; import com.onarandombox.MultiverseCore.worldnew.WorldManager; +import com.onarandombox.MultiverseCore.worldnew.config.NullLocation; +import com.onarandombox.MultiverseCore.worldnew.config.SpawnLocation; import io.vavr.control.Try; import jakarta.inject.Inject; import jakarta.inject.Provider; import me.main__.util.SerializationConfig.SerializationConfig; +import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPluginLoader; @@ -102,6 +105,8 @@ public class MultiverseCore extends JavaPlugin implements MVCore { @Override public void onEnable() { initializeDependencyInjection(); + ConfigurationSerialization.registerClass(NullLocation.class); + ConfigurationSerialization.registerClass(SpawnLocation.class); // Load our configs first as we need them for everything else. var config = configProvider.get(); diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java index 8c74f85a..3ed7d2d6 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java +++ b/src/main/java/com/onarandombox/MultiverseCore/commands/CreateCommand.java @@ -73,7 +73,7 @@ public class CreateCommand extends MultiverseCommand { .add(CommandValueFlag.enumBuilder("--world-type", WorldType.class) .addAlias("-t") .build()) - .add(CommandFlag.builder("--adjust-spawn") + .add(CommandFlag.builder("--no-adjust-spawn") .addAlias("-n") .build()) .add(CommandFlag.builder("--no-structures") @@ -109,7 +109,7 @@ public class CreateCommand extends MultiverseCommand { issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_ENVIRONMENT, "{environment}", environment.name()); issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_SEED, "{seed}", parsedFlags.flagValue("--seed", "RANDOM", String.class)); issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_WORLDTYPE, "{worldType}", parsedFlags.flagValue("--world-type", WorldType.NORMAL, WorldType.class).name()); - issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_ADJUSTSPAWN, "{adjustSpawn}", String.valueOf(parsedFlags.hasFlag("--adjust-spawn"))); + issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_ADJUSTSPAWN, "{adjustSpawn}", String.valueOf(!parsedFlags.hasFlag("--no-adjust-spawn"))); issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_GENERATOR, "{generator}", parsedFlags.flagValue("--generator", "", String.class)); issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_STRUCTURES, "{structures}", String.valueOf(!parsedFlags.hasFlag("--no-structures"))); @@ -119,7 +119,7 @@ public class CreateCommand extends MultiverseCommand { .environment(environment) .seed(parsedFlags.flagValue("--seed", String.class)) .worldType(parsedFlags.flagValue("--world-type", WorldType.NORMAL, WorldType.class)) - .useSpawnAdjust(parsedFlags.hasFlag("--adjust-spawn")) + .useSpawnAdjust(!parsedFlags.hasFlag("--no-adjust-spawn")) .generator(parsedFlags.flagValue("--generator", "", String.class)) .generateStructures(!parsedFlags.hasFlag("--no-structures")) ).onSuccess((success) -> { diff --git a/src/main/java/com/onarandombox/MultiverseCore/world/SimpleMVWorld.java b/src/main/java/com/onarandombox/MultiverseCore/world/SimpleMVWorld.java index 5247f985..ec5c5077 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/world/SimpleMVWorld.java +++ b/src/main/java/com/onarandombox/MultiverseCore/world/SimpleMVWorld.java @@ -24,7 +24,7 @@ import com.onarandombox.MultiverseCore.exceptions.PropertyDoesNotExistException; import com.onarandombox.MultiverseCore.listeners.MVPlayerListener; import com.onarandombox.MultiverseCore.world.configuration.AllowedPortalType; import com.onarandombox.MultiverseCore.world.configuration.EnglishChatColor; -import com.onarandombox.MultiverseCore.world.configuration.SpawnLocation; +import com.onarandombox.MultiverseCore.worldnew.config.SpawnLocation; import com.onarandombox.MultiverseCore.world.configuration.SpawnSettings; import com.onarandombox.MultiverseCore.world.configuration.WorldPropertyValidator; import me.main__.util.SerializationConfig.ChangeDeniedException; diff --git a/src/main/java/com/onarandombox/MultiverseCore/world/WorldProperties.java b/src/main/java/com/onarandombox/MultiverseCore/world/WorldProperties.java index 3fea6e38..bcca1d8a 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/world/WorldProperties.java +++ b/src/main/java/com/onarandombox/MultiverseCore/world/WorldProperties.java @@ -9,7 +9,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import com.onarandombox.MultiverseCore.world.configuration.EntryFee; -import com.onarandombox.MultiverseCore.world.configuration.SpawnLocation; +import com.onarandombox.MultiverseCore.worldnew.config.SpawnLocation; import com.onarandombox.MultiverseCore.world.configuration.SpawnSettings; import com.onarandombox.MultiverseCore.world.configuration.WorldPropertyValidator; import com.onarandombox.MultiverseCore.world.configuration.AllowedPortalType; diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/MVWorld.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/MVWorld.java index 08a2b865..53c8adf7 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/worldnew/MVWorld.java +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/MVWorld.java @@ -1,25 +1,45 @@ package com.onarandombox.MultiverseCore.worldnew; +import com.dumptruckman.minecraft.util.Logging; +import com.onarandombox.MultiverseCore.api.BlockSafety; +import com.onarandombox.MultiverseCore.api.LocationManipulation; +import com.onarandombox.MultiverseCore.api.SafeTTeleporter; +import com.onarandombox.MultiverseCore.worldnew.config.NullLocation; +import com.onarandombox.MultiverseCore.worldnew.config.SpawnLocation; import com.onarandombox.MultiverseCore.worldnew.config.WorldConfig; import io.vavr.control.Option; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.World; import org.jetbrains.annotations.NotNull; import java.util.UUID; public class MVWorld extends OfflineWorld { + private static final int SPAWN_LOCATION_SEARCH_TOLERANCE = 16; + private static final int SPAWN_LOCATION_SEARCH_RADIUS = 16; private final UUID worldUid; + private final BlockSafety blockSafety; + private final SafeTTeleporter safeTTeleporter; + private final LocationManipulation locationManipulation; + MVWorld( @NotNull World world, - @NotNull WorldConfig worldConfig + @NotNull WorldConfig worldConfig, + @NotNull BlockSafety blockSafety, + @NotNull SafeTTeleporter safeTTeleporter, + @NotNull LocationManipulation locationManipulation ) { super(world.getName(), worldConfig); this.worldUid = world.getUID(); + this.blockSafety = blockSafety; + this.safeTTeleporter = safeTTeleporter; + this.locationManipulation = locationManipulation; setupWorldConfig(world); + setupSpawnLocation(world); } private void setupWorldConfig(World world) { @@ -29,6 +49,56 @@ public class MVWorld extends OfflineWorld { worldConfig.setSeed(world.getSeed()); } + private void setupSpawnLocation(World world) { + Location spawnLocation = worldConfig.getSpawnLocation(); + if (spawnLocation == null || spawnLocation instanceof NullLocation) { + SpawnLocation newLocation = new SpawnLocation(readSpawnFromWorld(world)); + worldConfig.setSpawnLocation(newLocation); + world.setSpawnLocation(newLocation.getBlockX(), newLocation.getBlockY(), newLocation.getBlockZ()); + } + worldConfig.getSpawnLocation().setWorld(world); + } + + private Location readSpawnFromWorld(World world) { // TODO: Refactor... this is copy pasted and bad + Location location = world.getSpawnLocation(); + // Set the worldspawn to our configspawn + // Verify that location was safe + if (!blockSafety.playerCanSpawnHereSafely(location)) { + if (!this.getAdjustSpawn()) { + Logging.fine("Spawn location from world.dat file was unsafe!!"); + Logging.fine("NOT adjusting spawn for '" + this.getAlias() + "' because you told me not to."); + Logging.fine("To turn on spawn adjustment for this world simply type:"); + Logging.fine("/mvm set adjustspawn true " + this.getAlias()); + return location; + } + // If it's not, find a better one. + Logging.warning("Spawn location from world.dat file was unsafe. Adjusting..."); + Logging.warning("Original Location: " + locationManipulation.strCoordsRaw(location)); + Location newSpawn = safeTTeleporter.getSafeLocation(location, + SPAWN_LOCATION_SEARCH_TOLERANCE, SPAWN_LOCATION_SEARCH_RADIUS); + // I think we could also do this, as I think this is what Notch does. + // Not sure how it will work in the nether... + //Location newSpawn = this.spawnLocation.getWorld().getHighestBlockAt(this.spawnLocation).getLocation(); + if (newSpawn != null) { + Logging.info("New Spawn for '%s' is located at: %s", + this.getName(), locationManipulation.locationToString(newSpawn)); + return newSpawn; + } else { + // If it's a standard end world, let's check in a better place: + Location newerSpawn; + newerSpawn = blockSafety.getTopBlock(new Location(world, 0, 0, 0)); + if (newerSpawn != null) { + Logging.info("New Spawn for '%s' is located at: %s", + this.getName(), locationManipulation.locationToString(newerSpawn)); + return newerSpawn; + } else { + Logging.severe("Safe spawn NOT found!!!"); + } + } + } + return location; + } + public Option getBukkitWorld() { return Option.of(Bukkit.getWorld(worldUid)); } diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java index 9404865e..791a7ab4 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java @@ -2,6 +2,9 @@ package com.onarandombox.MultiverseCore.worldnew; import com.dumptruckman.minecraft.util.Logging; import com.google.common.base.Strings; +import com.onarandombox.MultiverseCore.api.BlockSafety; +import com.onarandombox.MultiverseCore.api.LocationManipulation; +import com.onarandombox.MultiverseCore.api.SafeTTeleporter; import com.onarandombox.MultiverseCore.utils.file.FileUtils; import com.onarandombox.MultiverseCore.utils.result.Result; import com.onarandombox.MultiverseCore.worldnew.config.WorldConfig; @@ -34,13 +37,25 @@ public class WorldManager { private final Map worldsMap; private final WorldsConfigManager worldsConfigManager; private final WorldNameChecker worldNameChecker; + private final BlockSafety blockSafety; + private final SafeTTeleporter safeTTeleporter; + private final LocationManipulation locationManipulation; @Inject - WorldManager(@NotNull WorldsConfigManager worldsConfigManager, @NotNull WorldNameChecker worldNameChecker) { + WorldManager( + @NotNull WorldsConfigManager worldsConfigManager, + @NotNull WorldNameChecker worldNameChecker, + @NotNull BlockSafety blockSafety, + @NotNull SafeTTeleporter safeTTeleporter, + @NotNull LocationManipulation locationManipulation + ) { this.offlineWorldsMap = new HashMap<>(); this.worldsMap = new HashMap<>(); this.worldsConfigManager = worldsConfigManager; this.worldNameChecker = worldNameChecker; + this.blockSafety = blockSafety; + this.safeTTeleporter = safeTTeleporter; + this.locationManipulation = locationManipulation; } public void initAllWorlds() { @@ -166,7 +181,7 @@ public class WorldManager { OfflineWorld offlineWorld = new OfflineWorld(world.getName(), worldConfig); offlineWorldsMap.put(offlineWorld.getName(), offlineWorld); - MVWorld mvWorld = new MVWorld(world, worldConfig); + MVWorld mvWorld = new MVWorld(world, worldConfig, blockSafety, safeTTeleporter, locationManipulation); worldsMap.put(mvWorld.getName(), mvWorld); return mvWorld; } @@ -197,7 +212,7 @@ public class WorldManager { // Our multiverse world WorldConfig worldConfig = worldsConfigManager.getWorldConfig(offlineWorld.getName()); - MVWorld mvWorld = new MVWorld(world, worldConfig); + MVWorld mvWorld = new MVWorld(world, worldConfig, blockSafety, safeTTeleporter, locationManipulation); worldsMap.put(mvWorld.getName(), mvWorld); saveWorldsConfig(); diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/config/NullLocation.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/config/NullLocation.java new file mode 100644 index 00000000..1e812834 --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/config/NullLocation.java @@ -0,0 +1,53 @@ +package com.onarandombox.MultiverseCore.worldnew.config; + +import com.onarandombox.MultiverseCore.world.SimpleMVWorld; +import org.bukkit.Location; +import org.bukkit.configuration.serialization.SerializableAs; +import org.bukkit.util.Vector; + +import java.util.Collections; +import java.util.Map; + +/** + * Null-location. + */ +@SerializableAs("MVNullLocation (It's a bug if you see this in your config file)") +public final class NullLocation extends SpawnLocation { + public NullLocation() { + super(0, -1, 0); + } + + @Override + public Location clone() { + throw new UnsupportedOperationException(); + }; + + @Override + public Map serialize() { + return Collections.emptyMap(); + } + + /** + * Let Bukkit be able to deserialize this. + * @param args The map. + * @return The deserialized object. + */ + public static SimpleMVWorld.NullLocation deserialize(Map args) { + return new SimpleMVWorld.NullLocation(); + } + + @Override + public Vector toVector() { + throw new UnsupportedOperationException(); + } + + @Override + public int hashCode() { + return -1; + }; + + @Override + public String toString() { + return "Location{null}"; + }; +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/world/configuration/SpawnLocation.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/config/SpawnLocation.java similarity index 98% rename from src/main/java/com/onarandombox/MultiverseCore/world/configuration/SpawnLocation.java rename to src/main/java/com/onarandombox/MultiverseCore/worldnew/config/SpawnLocation.java index b74ad7ed..824daad6 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/world/configuration/SpawnLocation.java +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/config/SpawnLocation.java @@ -1,4 +1,4 @@ -package com.onarandombox.MultiverseCore.world.configuration; +package com.onarandombox.MultiverseCore.worldnew.config; import java.lang.ref.Reference; import java.lang.ref.WeakReference; diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/config/WorldConfigNodes.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/config/WorldConfigNodes.java index 12105c41..0aee9fbd 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/worldnew/config/WorldConfigNodes.java +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/config/WorldConfigNodes.java @@ -154,6 +154,7 @@ public class WorldConfigNodes { .build()); public final ConfigNode SPAWN_LOCATION = node(ConfigNode.builder("spawn-location", Location.class) + .defaultValue(new NullLocation()) .name("spawn-location") .build()); diff --git a/src/old-test/java/com/onarandombox/MultiverseCore/TestWorldProperties.java b/src/old-test/java/com/onarandombox/MultiverseCore/TestWorldProperties.java index 4feb862e..e08a0ff8 100644 --- a/src/old-test/java/com/onarandombox/MultiverseCore/TestWorldProperties.java +++ b/src/old-test/java/com/onarandombox/MultiverseCore/TestWorldProperties.java @@ -7,25 +7,15 @@ package com.onarandombox.MultiverseCore; -import java.io.File; - -import com.onarandombox.MultiverseCore.api.MVWorldManager; import com.onarandombox.MultiverseCore.api.MVWorld; -import com.onarandombox.MultiverseCore.world.configuration.SpawnLocation; import com.onarandombox.MultiverseCore.utils.MockWorldFactory; import com.onarandombox.MultiverseCore.utils.TestInstanceCreator; -import com.onarandombox.MultiverseCore.world.WorldProperties; import org.bukkit.ChatColor; -import org.bukkit.Difficulty; -import org.bukkit.GameMode; import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.WorldType; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.entity.EntityRegainHealthEvent; @@ -47,9 +37,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/src/test/java/org/mvplugins/multiverse/core/world/WorldConfigMangerTest.kt b/src/test/java/org/mvplugins/multiverse/core/world/WorldConfigMangerTest.kt index 0a60122c..d59994ed 100644 --- a/src/test/java/org/mvplugins/multiverse/core/world/WorldConfigMangerTest.kt +++ b/src/test/java/org/mvplugins/multiverse/core/world/WorldConfigMangerTest.kt @@ -1,5 +1,6 @@ package org.mvplugins.multiverse.core.world +import com.onarandombox.MultiverseCore.worldnew.config.SpawnLocation import com.onarandombox.MultiverseCore.worldnew.config.WorldsConfigManager import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue @@ -47,6 +48,7 @@ class WorldConfigMangerTest : TestWithMockBukkit() { val worldConfig = worldConfigManager.getWorldConfig("world") worldConfig.setProperty("adjust-spawn", true) worldConfig.setProperty("alias", "newalias") + worldConfig.setProperty("spawn-location", SpawnLocation(-64.0, 64.0, 48.0)) worldConfigManager.save() compareConfigFile("worlds2.yml", "/properties_worlds.yml") } diff --git a/src/test/resources/default_worlds.yml b/src/test/resources/default_worlds.yml index bb55b54b..845ab4b8 100644 --- a/src/test/resources/default_worlds.yml +++ b/src/test/resources/default_worlds.yml @@ -22,6 +22,8 @@ world: respawn-world: '' scale: 1.0 seed: -9223372036854775808 + spawn-location: + ==: MVNullLocation (It's a bug if you see this in your config file) spawning: animals: spawn: true @@ -56,6 +58,8 @@ world_nether: respawn-world: '' scale: 1.0 seed: -9223372036854775808 + spawn-location: + ==: MVNullLocation (It's a bug if you see this in your config file) spawning: animals: spawn: true diff --git a/src/test/resources/delete_worlds.yml b/src/test/resources/delete_worlds.yml index 0c027652..32f82dc0 100644 --- a/src/test/resources/delete_worlds.yml +++ b/src/test/resources/delete_worlds.yml @@ -22,6 +22,8 @@ world_nether: respawn-world: '' scale: 1.0 seed: -9223372036854775808 + spawn-location: + ==: MVNullLocation (It's a bug if you see this in your config file) spawning: animals: spawn: true diff --git a/src/test/resources/newworld_worlds.yml b/src/test/resources/newworld_worlds.yml index f70e4c93..784404fa 100644 --- a/src/test/resources/newworld_worlds.yml +++ b/src/test/resources/newworld_worlds.yml @@ -22,6 +22,8 @@ world: respawn-world: '' scale: 1.0 seed: -9223372036854775808 + spawn-location: + ==: MVNullLocation (It's a bug if you see this in your config file) spawning: animals: spawn: true @@ -56,6 +58,8 @@ world_nether: respawn-world: '' scale: 1.0 seed: -9223372036854775808 + spawn-location: + ==: MVNullLocation (It's a bug if you see this in your config file) spawning: animals: spawn: true @@ -90,6 +94,8 @@ newworld: respawn-world: '' scale: 1.0 seed: -9223372036854775808 + spawn-location: + ==: MVNullLocation (It's a bug if you see this in your config file) spawning: animals: spawn: true diff --git a/src/test/resources/properties_worlds.yml b/src/test/resources/properties_worlds.yml index 3cebf260..36fe56ad 100644 --- a/src/test/resources/properties_worlds.yml +++ b/src/test/resources/properties_worlds.yml @@ -22,6 +22,13 @@ world: respawn-world: '' scale: 1.0 seed: -9223372036854775808 + spawn-location: + ==: MVSpawnLocation + x: -64.0 + y: 64.0 + z: 48.0 + pitch: 0.0 + yaw: 0.0 spawning: animals: spawn: true @@ -56,6 +63,8 @@ world_nether: respawn-world: '' scale: 1.0 seed: -9223372036854775808 + spawn-location: + ==: MVNullLocation (It's a bug if you see this in your config file) spawning: animals: spawn: true