diff --git a/src/main/java/com/onarandombox/MultiverseCore/commands/RegenCommand.java b/src/main/java/com/onarandombox/MultiverseCore/commands/RegenCommand.java index 2f0cebb7..daaa566e 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/commands/RegenCommand.java +++ b/src/main/java/com/onarandombox/MultiverseCore/commands/RegenCommand.java @@ -9,6 +9,8 @@ import co.aikar.commands.annotation.Description; import co.aikar.commands.annotation.Optional; import co.aikar.commands.annotation.Subcommand; import co.aikar.commands.annotation.Syntax; +import com.dumptruckman.minecraft.util.Logging; +import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer; import com.onarandombox.MultiverseCore.commandtools.MVCommandManager; import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand; import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlag; @@ -19,6 +21,7 @@ import com.onarandombox.MultiverseCore.commandtools.queue.QueuedCommand; import com.onarandombox.MultiverseCore.utils.MVCorei18n; import com.onarandombox.MultiverseCore.worldnew.MVWorld; import com.onarandombox.MultiverseCore.worldnew.WorldManager; +import com.onarandombox.MultiverseCore.worldnew.options.RegenWorldOptions; import jakarta.inject.Inject; import org.jetbrains.annotations.NotNull; import org.jvnet.hk2.annotations.Service; @@ -43,8 +46,14 @@ public class RegenCommand extends MultiverseCommand { .completion((input) -> Collections.singleton(String.valueOf(new Random().nextLong()))) .optional() .build()) - .add(CommandFlag.builder("--keep-gamerules") - .addAlias("-k") + .add(CommandFlag.builder("--reset-world-config") + .addAlias("-wc") + .build()) + .add(CommandFlag.builder("--reset-gamerules") + .addAlias("-gm") + .build()) + .add(CommandFlag.builder("--reset-world-border") + .addAlias("-wb") .build()) .build()); } @@ -54,14 +63,14 @@ public class RegenCommand extends MultiverseCommand { @CommandCompletion("@mvworlds:scope=loaded @flags:groupName=mvregen") @Syntax(" --seed [seed] --keep-gamerules") @Description("{@@mv-core.regen.description}") - public void onRegenCommand(BukkitCommandIssuer issuer, + public void onRegenCommand(MVCommandIssuer issuer, @Syntax("") @Description("{@@mv-core.regen.world.description}") MVWorld world, @Optional - @Syntax("--seed [seed] --keep-gamerules") + @Syntax("--seed [seed] --reset-gamerules") @Description("{@@mv-core.regen.other.description}") String[] flags ) { @@ -71,8 +80,19 @@ public class RegenCommand extends MultiverseCommand { issuer.getIssuer(), () -> { issuer.sendInfo(MVCorei18n.REGEN_REGENERATING, "{world}", world.getName()); - worldManager.regenWorld(world); - issuer.sendInfo(MVCorei18n.REGEN_SUCCESS, "{world}", world.getName()); + worldManager.regenWorld(RegenWorldOptions.world(world) + .randomSeed(!parsedFlags.hasFlagValue("--seed")) + .seed(parsedFlags.flagValue("--seed", String.class)) + .keepWorldConfig(!parsedFlags.hasFlag("--reset-world-config")) + .keepGameRule(!parsedFlags.hasFlag("--reset-gamerules")) + .keepWorldBorder(!parsedFlags.hasFlag("--reset-world-border")) + ).onSuccess((success) -> { + Logging.fine("World create success: " + success); + issuer.sendInfo(success.getReasonMessage()); + }).onFailure((failure) -> { + Logging.fine("World create failure: " + failure); + issuer.sendError(failure.getReasonMessage()); + }); }, this.commandManager.formatMessage( issuer, diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java index 18dc49e4..7fc9e2ed 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/WorldManager.java @@ -14,10 +14,12 @@ import com.onarandombox.MultiverseCore.worldnew.helpers.DataStore.GameRulesStore import com.onarandombox.MultiverseCore.worldnew.helpers.FilesManipulator; import com.onarandombox.MultiverseCore.worldnew.options.CreateWorldOptions; import com.onarandombox.MultiverseCore.worldnew.options.ImportWorldOptions; +import com.onarandombox.MultiverseCore.worldnew.options.RegenWorldOptions; import com.onarandombox.MultiverseCore.worldnew.results.CreateWorldResult; import com.onarandombox.MultiverseCore.worldnew.results.DeleteWorldResult; import com.onarandombox.MultiverseCore.worldnew.results.ImportWorldResult; import com.onarandombox.MultiverseCore.worldnew.results.LoadWorldResult; +import com.onarandombox.MultiverseCore.worldnew.results.RegenWorldResult; import com.onarandombox.MultiverseCore.worldnew.results.RemoveWorldResult; import com.onarandombox.MultiverseCore.worldnew.results.UnloadWorldResult; import io.vavr.control.Option; @@ -512,40 +514,38 @@ public class WorldManager { /** * Regenerates a world. * - * @param world The world to regenerate. + * @param options The options for customizing the regeneration of a world. */ - public void regenWorld(@NotNull MVWorld world) { + public Result regenWorld(@NotNull RegenWorldOptions options) { // TODO: Teleport players out of world, and back in after regen + MVWorld world = options.world(); GameRulesStore gameRulesStore = GameRulesStore.createAndCopyFrom(world); WorldConfigStore worldConfigStore = WorldConfigStore.createAndCopyFrom(world); - // TODO: Random/fixed seed option CreateWorldOptions createWorldOptions = CreateWorldOptions.worldName(world.getName()) .environment(world.getEnvironment()) .generateStructures(world.canGenerateStructures().getOrElse(true)) .generator(world.getGenerator()) - .seed(world.getSeed()) + .seed(options.seed()) .worldType(world.getWorldType().getOrElse(WorldType.NORMAL)); var deleteResult = deleteWorld(world); if (deleteResult.isFailure()) { - Logging.severe("Failed to delete world: " + world.getName()); - return; + return Result.failure(RegenWorldResult.Failure.DELETE_FAILED, deleteResult.getReasonMessage()); } var createResult = createWorld(createWorldOptions); if (createResult.isFailure()) { - Logging.severe("Failed to create world: " + world.getName()); - return; + return Result.failure(RegenWorldResult.Failure.CREATE_FAILED, createResult.getReasonMessage()); } - // TODO: Error handling getMVWorld(createWorldOptions.worldName()).peek(newWorld -> { gameRulesStore.pasteTo(newWorld); worldConfigStore.pasteTo(newWorld); saveWorldsConfig(); }); + return Result.success(RegenWorldResult.Success.REGENERATED, replace("{world}").with(world.getName())); } /** diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/helpers/DataStore.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/helpers/DataStore.java index 61095767..d8b86441 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/worldnew/helpers/DataStore.java +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/helpers/DataStore.java @@ -11,9 +11,27 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; +/** + * A data store for storing and restoring data from an object. + * + * @param The type of the object to store data from. + */ @Service public interface DataStore { + /** + * Stores the data from the given object in this {@link DataStore} instance. + * + * @param object The object to copy data from. + * @return This {@link DataStore} instance. + */ DataStore copyFrom(T object); + + /** + * Copies the data from this {@link DataStore} instance to the given object. + * + * @param object The object to paste data to. + * @return This {@link DataStore} instance. + */ DataStore pasteTo(T object); class GameRulesStore implements DataStore { @@ -23,6 +41,10 @@ public interface DataStore { private Map, Object> gameRuleMap; + /** + * {@inheritDoc} + */ + @Override public GameRulesStore copyFrom(MVWorld world) { this.gameRuleMap = new HashMap<>(); world.getBukkitWorld().peek(bukkitWorld -> { @@ -34,6 +56,10 @@ public interface DataStore { return this; } + /** + * {@inheritDoc} + */ + @Override public GameRulesStore pasteTo(MVWorld world) { if (gameRuleMap == null) { return this; @@ -65,6 +91,9 @@ public interface DataStore { private Map configMap; + /** + * {@inheritDoc} + */ @Override public WorldConfigStore copyFrom(OfflineWorld world) { this.configMap = new HashMap<>(); @@ -76,6 +105,9 @@ public interface DataStore { return this; } + /** + * {@inheritDoc} + */ @Override public WorldConfigStore pasteTo(OfflineWorld world) { if (configMap == null) { @@ -90,4 +122,54 @@ public interface DataStore { return this; } } + + class WorldBorderStore implements DataStore { + /** + * Creates a new {@link WorldBorderStore} instance and copies the world border data from the given world. + * + * @param world The world to copy the world border data from. + * @return A new {@link WorldBorderStore} instance. + */ + public static WorldBorderStore createAndCopyFrom(MVWorld world) { + return new WorldBorderStore().copyFrom(world); + } + + private double borderCenterX; + private double borderCenterZ; + private double borderDamageAmount; + private double borderDamageBuffer; + private double borderSize; + private int borderTimeRemaining; + + /** + * {@inheritDoc} + */ + @Override + public WorldBorderStore copyFrom(MVWorld world) { + world.getBukkitWorld().peek(bukkitWorld -> { + borderCenterX = bukkitWorld.getWorldBorder().getCenter().getX(); + borderCenterZ = bukkitWorld.getWorldBorder().getCenter().getZ(); + borderDamageAmount = bukkitWorld.getWorldBorder().getDamageAmount(); + borderDamageBuffer = bukkitWorld.getWorldBorder().getDamageBuffer(); + borderSize = bukkitWorld.getWorldBorder().getSize(); + borderTimeRemaining = bukkitWorld.getWorldBorder().getWarningTime(); + }); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public WorldBorderStore pasteTo(MVWorld world) { + world.getBukkitWorld().peek(bukkitWorld -> { + bukkitWorld.getWorldBorder().setCenter(borderCenterX, borderCenterZ); + bukkitWorld.getWorldBorder().setDamageAmount(borderDamageAmount); + bukkitWorld.getWorldBorder().setDamageBuffer(borderDamageBuffer); + bukkitWorld.getWorldBorder().setSize(borderSize); + bukkitWorld.getWorldBorder().setWarningTime(borderTimeRemaining); + }); + return this; + } + } } diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/CreateWorldOptions.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/CreateWorldOptions.java index 5f329700..114b9c1a 100644 --- a/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/CreateWorldOptions.java +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/CreateWorldOptions.java @@ -5,6 +5,8 @@ import org.bukkit.WorldType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Random; + /** * Options for customizing the creation of a new world. */ @@ -24,7 +26,7 @@ public class CreateWorldOptions { private World.Environment environment = World.Environment.NORMAL; private boolean generateStructures = true; private String generator = null; - private long seed = Long.MIN_VALUE; + private long seed; private boolean useSpawnAdjust = true; private WorldType worldType = WorldType.NORMAL; @@ -35,6 +37,7 @@ public class CreateWorldOptions { */ CreateWorldOptions(@NotNull String worldName) { this.worldName = worldName; + this.seed = (new Random()).nextLong(); } /** @@ -115,7 +118,6 @@ public class CreateWorldOptions { */ public @NotNull CreateWorldOptions seed(@Nullable String seed) { if (seed == null) { - this.seed = Long.MIN_VALUE; return this; } try { diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/RegenWorldOptions.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/RegenWorldOptions.java new file mode 100644 index 00000000..8cd3d86a --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/options/RegenWorldOptions.java @@ -0,0 +1,99 @@ +package com.onarandombox.MultiverseCore.worldnew.options; + +import com.onarandombox.MultiverseCore.worldnew.MVWorld; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Random; + +public class RegenWorldOptions { + public static @NotNull RegenWorldOptions world(@NotNull MVWorld world) { + return new RegenWorldOptions(world); + } + + private final MVWorld world; + private boolean keepGameRule = true; + private boolean keepWorldConfig = true; + + private boolean keepWorldBorder = true; + private boolean randomSeed = false; + private long seed = Long.MIN_VALUE; + + RegenWorldOptions(@NotNull MVWorld world) { + this.world = world; + } + + public @NotNull MVWorld world() { + return world; + } + + public @NotNull RegenWorldOptions keepGameRule(boolean keepGameRule) { + this.keepGameRule = keepGameRule; + return this; + } + + public boolean keepGameRule() { + return keepGameRule; + } + + public @NotNull RegenWorldOptions keepWorldConfig(boolean keepWorldConfig) { + this.keepWorldConfig = keepWorldConfig; + return this; + } + + public boolean keepWorldConfig() { + return keepWorldConfig; + } + + public @NotNull RegenWorldOptions keepWorldBorder(boolean keepWorldBorder) { + this.keepWorldBorder = keepWorldBorder; + return this; + } + + public boolean keepWorldBorder() { + return keepWorldBorder; + } + + public @NotNull RegenWorldOptions randomSeed(boolean randomSeed) { + if (randomSeed && seed != Long.MIN_VALUE) { + throw new IllegalStateException("Cannot set randomSeed to true when seed is set"); + } + this.randomSeed = randomSeed; + return this; + } + + public boolean randomSeed() { + return randomSeed; + } + + public @NotNull RegenWorldOptions seed(@Nullable String seed) { + if (seed == null) { + this.seed = Long.MIN_VALUE; + return this; + } + if (randomSeed) { + randomSeed(false); + } + try { + this.seed = Long.parseLong(seed); + } catch (NumberFormatException numberformatexception) { + this.seed = seed.hashCode(); + } + return this; + } + + public @NotNull RegenWorldOptions seed(long seed) { + this.seed = seed; + return this; + } + + public long seed() { + if (randomSeed) { + return new Random().nextLong(); + } + if (seed == Long.MIN_VALUE) { + return world.getSeed(); + } + return seed; + } +} diff --git a/src/main/java/com/onarandombox/MultiverseCore/worldnew/results/RegenWorldResult.java b/src/main/java/com/onarandombox/MultiverseCore/worldnew/results/RegenWorldResult.java new file mode 100644 index 00000000..f332f18f --- /dev/null +++ b/src/main/java/com/onarandombox/MultiverseCore/worldnew/results/RegenWorldResult.java @@ -0,0 +1,42 @@ +package com.onarandombox.MultiverseCore.worldnew.results; + +import co.aikar.locales.MessageKey; +import co.aikar.locales.MessageKeyProvider; +import com.onarandombox.MultiverseCore.utils.MVCorei18n; +import com.onarandombox.MultiverseCore.utils.result.FailureReason; +import com.onarandombox.MultiverseCore.utils.result.SuccessReason; + +public class RegenWorldResult { + public enum Success implements SuccessReason { + REGENERATED(MVCorei18n.GENERIC_SUCCESS) + ; + + private final MessageKeyProvider message; + + Success(MessageKeyProvider message) { + this.message = message; + } + + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } + } + + public enum Failure implements FailureReason { + DELETE_FAILED(null), + CREATE_FAILED(null), + ; + + private final MessageKeyProvider message; + + Failure(MessageKeyProvider message) { + this.message = message; + } + + @Override + public MessageKey getMessageKey() { + return message.getMessageKey(); + } + } +} diff --git a/src/main/resources/multiverse-core_en.properties b/src/main/resources/multiverse-core_en.properties index 9b43fa39..5c866fd6 100644 --- a/src/main/resources/multiverse-core_en.properties +++ b/src/main/resources/multiverse-core_en.properties @@ -155,6 +155,8 @@ mv-core.loadworld.worldexistfolder=World '{world}' exists in server folders, but mv-core.loadworld.worldexistloaded=World '{world}' is already loaded! mv-core.loadworld.bukkitcreationfailed=Bukkit failed to load world '{world}': {error}\n&fSee console for more details. +mv-core.regenworld.regenerated=&aWorld '{world}' regenerated! + mv-core.removeworld.removed=&aWorld '{world}' removed! mv-core.removeworld.worldnonexistent=World '{world}' not found!