Finish up regen world

This commit is contained in:
Ben Woo 2023-09-07 11:38:29 +08:00
parent a0fa27f926
commit 9cb8724db2
No known key found for this signature in database
GPG Key ID: FB2A3645536E12C8
7 changed files with 264 additions and 17 deletions

View File

@ -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("<world> --seed [seed] --keep-gamerules")
@Description("{@@mv-core.regen.description}")
public void onRegenCommand(BukkitCommandIssuer issuer,
public void onRegenCommand(MVCommandIssuer issuer,
@Syntax("<world>")
@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,

View File

@ -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<RegenWorldResult.Success, RegenWorldResult.Failure> 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()));
}
/**

View File

@ -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 <T> The type of the object to store data from.
*/
@Service
public interface DataStore<T> {
/**
* 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<T> 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<T> pasteTo(T object);
class GameRulesStore implements DataStore<MVWorld> {
@ -23,6 +41,10 @@ public interface DataStore<T> {
private Map<GameRule<?>, Object> gameRuleMap;
/**
* {@inheritDoc}
*/
@Override
public GameRulesStore copyFrom(MVWorld world) {
this.gameRuleMap = new HashMap<>();
world.getBukkitWorld().peek(bukkitWorld -> {
@ -34,6 +56,10 @@ public interface DataStore<T> {
return this;
}
/**
* {@inheritDoc}
*/
@Override
public GameRulesStore pasteTo(MVWorld world) {
if (gameRuleMap == null) {
return this;
@ -65,6 +91,9 @@ public interface DataStore<T> {
private Map<String, Object> configMap;
/**
* {@inheritDoc}
*/
@Override
public WorldConfigStore copyFrom(OfflineWorld world) {
this.configMap = new HashMap<>();
@ -76,6 +105,9 @@ public interface DataStore<T> {
return this;
}
/**
* {@inheritDoc}
*/
@Override
public WorldConfigStore pasteTo(OfflineWorld world) {
if (configMap == null) {
@ -90,4 +122,54 @@ public interface DataStore<T> {
return this;
}
}
class WorldBorderStore implements DataStore<MVWorld> {
/**
* 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;
}
}
}

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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!