diff --git a/api/src/main/java/net/thenextlvl/worlds/api/WorldsProvider.java b/api/src/main/java/net/thenextlvl/worlds/api/WorldsProvider.java index aaf840aa..3dcb1981 100644 --- a/api/src/main/java/net/thenextlvl/worlds/api/WorldsProvider.java +++ b/api/src/main/java/net/thenextlvl/worlds/api/WorldsProvider.java @@ -1,13 +1,18 @@ package net.thenextlvl.worlds.api; import net.thenextlvl.worlds.api.link.LinkController; +import net.thenextlvl.worlds.api.model.LevelBuilder; import net.thenextlvl.worlds.api.view.GeneratorView; import net.thenextlvl.worlds.api.view.LevelView; import org.bukkit.plugin.Plugin; +import java.io.File; + public interface WorldsProvider extends Plugin { GeneratorView generatorView(); + LevelBuilder levelBuilder(File level); + LevelView levelView(); LinkController linkController(); diff --git a/api/src/main/java/net/thenextlvl/worlds/api/model/Level.java b/api/src/main/java/net/thenextlvl/worlds/api/model/Level.java new file mode 100644 index 00000000..599f3ced --- /dev/null +++ b/api/src/main/java/net/thenextlvl/worlds/api/model/Level.java @@ -0,0 +1,39 @@ +package net.thenextlvl.worlds.api.model; + +import core.nbt.file.NBTFile; +import core.nbt.tag.CompoundTag; +import net.kyori.adventure.key.Keyed; +import net.thenextlvl.worlds.api.preset.Preset; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public interface Level extends Keyed { + @Nullable Generator generator(); + + @Nullable Preset preset(); + + NBTFile levelData(); + + NamespacedKey key(); + + Optional create(); + + String name(); + + World.Environment environment(); + + WorldPreset type(); + + boolean enabled(); + + boolean hardcore(); + + boolean importedBefore(); + + boolean structures(); + + long seed(); +} diff --git a/api/src/main/java/net/thenextlvl/worlds/api/model/LevelBuilder.java b/api/src/main/java/net/thenextlvl/worlds/api/model/LevelBuilder.java new file mode 100644 index 00000000..0467f114 --- /dev/null +++ b/api/src/main/java/net/thenextlvl/worlds/api/model/LevelBuilder.java @@ -0,0 +1,50 @@ +package net.thenextlvl.worlds.api.model; + +import net.thenextlvl.worlds.api.preset.Preset; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +public interface LevelBuilder { + @Nullable Boolean hardcore(); + + @Nullable Boolean structures(); + + @Nullable Generator generator(); + + @Nullable Long seed(); + + @Nullable NamespacedKey key(); + + @Nullable Preset preset(); + + @Nullable String name(); + + @Nullable World.Environment environment(); + + @Nullable WorldPreset type(); + + File level(); + + LevelBuilder environment(@Nullable World.Environment environment); + + LevelBuilder generator(@Nullable Generator generator); + + LevelBuilder hardcore(@Nullable Boolean hardcore); + + LevelBuilder key(@Nullable NamespacedKey key); + + LevelBuilder name(@Nullable String name); + + LevelBuilder preset(@Nullable Preset preset); + + LevelBuilder seed(@Nullable Long seed); + + LevelBuilder structures(@Nullable Boolean structures); + + LevelBuilder type(@Nullable WorldPreset type); + + Level build(); +} diff --git a/api/src/main/java/net/thenextlvl/worlds/api/view/LevelView.java b/api/src/main/java/net/thenextlvl/worlds/api/view/LevelView.java index fa1f6183..d5f600a8 100644 --- a/api/src/main/java/net/thenextlvl/worlds/api/view/LevelView.java +++ b/api/src/main/java/net/thenextlvl/worlds/api/view/LevelView.java @@ -5,28 +5,13 @@ import net.thenextlvl.worlds.api.model.LevelExtras; import net.thenextlvl.worlds.api.model.WorldPreset; import net.thenextlvl.worlds.api.preset.Preset; -import org.bukkit.NamespacedKey; import org.bukkit.World; -import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.Optional; -import java.util.function.Predicate; import java.util.stream.Stream; public interface LevelView { - @Nullable World loadLevel(File level); - - @Nullable World loadLevel(File level, @Nullable NamespacedKey key, Predicate> predicate); - - @Nullable World loadLevel(File level, Predicate> predicate); - - @Nullable World loadLevel(File level, World.Environment environment); - - @Nullable World loadLevel(File level, World.Environment environment, @Nullable NamespacedKey key, Predicate> predicate); - - @Nullable World loadLevel(File level, World.Environment environment, Predicate> predicate); - NBTFile getLevelDataFile(File level); Optional getExtras(CompoundTag data); diff --git a/plugin/src/main/java/net/thenextlvl/worlds/WorldsPlugin.java b/plugin/src/main/java/net/thenextlvl/worlds/WorldsPlugin.java index d90cce68..ecc620d4 100644 --- a/plugin/src/main/java/net/thenextlvl/worlds/WorldsPlugin.java +++ b/plugin/src/main/java/net/thenextlvl/worlds/WorldsPlugin.java @@ -11,6 +11,7 @@ import net.thenextlvl.worlds.api.WorldsProvider; import net.thenextlvl.worlds.api.link.LinkController; import net.thenextlvl.worlds.api.model.Generator; +import net.thenextlvl.worlds.api.model.LevelBuilder; import net.thenextlvl.worlds.api.preset.Presets; import net.thenextlvl.worlds.api.view.GeneratorView; import net.thenextlvl.worlds.api.view.LevelView; @@ -18,6 +19,7 @@ import net.thenextlvl.worlds.controller.WorldLinkController; import net.thenextlvl.worlds.listener.PortalListener; import net.thenextlvl.worlds.listener.ServerListener; +import net.thenextlvl.worlds.model.PaperLevelBuilder; import net.thenextlvl.worlds.version.PluginVersionChecker; import net.thenextlvl.worlds.view.PaperLevelView; import net.thenextlvl.worlds.view.PluginGeneratorView; @@ -78,6 +80,11 @@ public void onDisable() { unloadWorlds(); } + @Override + public LevelBuilder levelBuilder(File level) { + return new PaperLevelBuilder(this, level); + } + private void unloadWorlds() { getServer().getWorlds().stream().filter(world -> !world.isAutoSave()).forEach(world -> { world.getPlayers().forEach(player -> player.kick(getServer().shutdownMessage())); diff --git a/plugin/src/main/java/net/thenextlvl/worlds/command/WorldCreateCommand.java b/plugin/src/main/java/net/thenextlvl/worlds/command/WorldCreateCommand.java index b7488e72..065733e9 100644 --- a/plugin/src/main/java/net/thenextlvl/worlds/command/WorldCreateCommand.java +++ b/plugin/src/main/java/net/thenextlvl/worlds/command/WorldCreateCommand.java @@ -12,15 +12,15 @@ import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.thenextlvl.worlds.WorldsPlugin; import net.thenextlvl.worlds.api.model.Generator; +import net.thenextlvl.worlds.api.model.WorldPreset; import net.thenextlvl.worlds.api.preset.Preset; import net.thenextlvl.worlds.command.argument.*; import org.bukkit.NamespacedKey; import org.bukkit.World; -import org.bukkit.WorldCreator; -import org.bukkit.WorldType; import org.bukkit.entity.Entity; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.util.concurrent.ThreadLocalRandom; import static org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.COMMAND; @@ -50,7 +50,7 @@ class WorldCreateCommand { true, ThreadLocalRandom.current().nextLong())) .then(tree(this::createType)))) .executes(context -> create(context, World.Environment.NORMAL, true, - ThreadLocalRandom.current().nextLong(), WorldType.NORMAL, null, null))); + ThreadLocalRandom.current().nextLong(), WorldPreset.NORMAL, null, null))); } private RequiredArgumentBuilder tree(Creator creator) { @@ -76,25 +76,25 @@ private RequiredArgumentBuilder tree(Crea } private int create(CommandContext context, World.Environment environment, boolean structures, - long seed, WorldType worldType, @Nullable Preset preset, @Nullable Generator generator) { + long seed, WorldPreset type, @Nullable Preset preset, @Nullable Generator generator) { var key = context.getArgument("key", NamespacedKey.class); var name = key.getKey(); - var creator = new WorldCreator(name, key) - .environment(environment) - .generateStructures(structures) - .seed(seed) - .type(worldType); - if (preset != null) creator.generatorSettings(preset.serialize().toString()); + var levelFolder = new File(plugin.getServer().getWorldContainer(), name); - if (generator != null) { - creator.generator(generator.plugin().getDefaultWorldGenerator(name, generator.id())); - creator.biomeProvider(generator.plugin().getDefaultBiomeProvider(name, generator.id())); - } + var level = plugin.levelBuilder(levelFolder) + .environment(environment) + .generator(generator) + .preset(preset) + .seed(seed) + .name(name) + .structures(structures) + .type(type) + .build(); - var world = plugin.getServer().getWorld(creator.key()) == null - && plugin.getServer().getWorld(name) == null - ? creator.createWorld() : null; + var world = plugin.getServer().getWorld(level.key()) == null + && plugin.getServer().getWorld(level.name()) == null + ? level.create().orElse(null) : null; var message = world != null ? "world.create.success" : "world.create.failed"; plugin.bundle().sendMessage(context.getSource().getSender(), message, @@ -114,16 +114,16 @@ private int create(CommandContext context, World.Environment private int createGenerator(CommandContext context, World.Environment environment, boolean structures, long seed) { var generator = context.getArgument("generator", Generator.class); - return create(context, environment, structures, seed, WorldType.NORMAL, null, generator); + return create(context, environment, structures, seed, WorldPreset.NORMAL, null, generator); } private int createPreset(CommandContext context, World.Environment environment, boolean structures, long seed) { var preset = context.getArgument("preset", Preset.class); - return create(context, environment, structures, seed, WorldType.FLAT, preset, null); + return create(context, environment, structures, seed, WorldPreset.FLAT, preset, null); } private int createType(CommandContext context, World.Environment environment, boolean structures, long seed) { - var type = context.getArgument("type", WorldType.class); + var type = context.getArgument("type", WorldPreset.class); return create(context, environment, structures, seed, type, null, null); } diff --git a/plugin/src/main/java/net/thenextlvl/worlds/command/WorldImportCommand.java b/plugin/src/main/java/net/thenextlvl/worlds/command/WorldImportCommand.java index 24d5f781..a082d731 100644 --- a/plugin/src/main/java/net/thenextlvl/worlds/command/WorldImportCommand.java +++ b/plugin/src/main/java/net/thenextlvl/worlds/command/WorldImportCommand.java @@ -10,7 +10,10 @@ import lombok.RequiredArgsConstructor; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.thenextlvl.worlds.WorldsPlugin; +import net.thenextlvl.worlds.api.model.Generator; +import net.thenextlvl.worlds.api.model.Level; import net.thenextlvl.worlds.command.argument.DimensionArgument; +import net.thenextlvl.worlds.command.argument.GeneratorArgument; import net.thenextlvl.worlds.command.suggestion.LevelSuggestionProvider; import org.bukkit.NamespacedKey; import org.bukkit.World; @@ -33,31 +36,39 @@ class WorldImportCommand { .then(Commands.argument("world", StringArgumentType.string()) .suggests(new LevelSuggestionProvider<>(plugin)) .then(Commands.argument("key", ArgumentTypes.namespacedKey()) - .executes(context -> { - var key = context.getArgument("key", NamespacedKey.class); - return execute(context, null, key); - })) - .then(Commands.argument("dimension", new DimensionArgument(plugin)) - .then(Commands.argument("key", ArgumentTypes.namespacedKey()) + .then(Commands.argument("dimension", new DimensionArgument(plugin)) + .then(Commands.argument("generator", new GeneratorArgument(plugin)) + .executes(context -> { + var environment = context.getArgument("dimension", World.Environment.class); + var generator = context.getArgument("generator", Generator.class); + var key = context.getArgument("key", NamespacedKey.class); + return execute(context, key, environment, generator); + })) .executes(context -> { var environment = context.getArgument("dimension", World.Environment.class); var key = context.getArgument("key", NamespacedKey.class); - return execute(context, environment, key); + return execute(context, key, environment, null); })) .executes(context -> { - var environment = context.getArgument("dimension", World.Environment.class); - return execute(context, environment, null); + var key = context.getArgument("key", NamespacedKey.class); + return execute(context, key, null, null); })) - .executes(context -> execute(context, null, null))); + .executes(context -> execute(context, null, null, null))); } - private int execute(CommandContext context, @Nullable World.Environment environment, @Nullable NamespacedKey key) { + private int execute(CommandContext context, @Nullable NamespacedKey key, + @Nullable World.Environment environment, @Nullable Generator generator) { var name = context.getArgument("world", String.class); - var level = new File(plugin.getServer().getWorldContainer(), name); + var levelFolder = new File(plugin.getServer().getWorldContainer(), name); + + var build = plugin.levelView().isLevel(levelFolder) + ? plugin.levelBuilder(levelFolder).environment(environment) + .generator(generator).key(key).build() : null; - var world = plugin.levelView().isLevel(level) ? environment != null - ? plugin.levelView().loadLevel(level, environment, key, Optional::isEmpty) - : plugin.levelView().loadLevel(level, key, Optional::isEmpty) : null; + var world = Optional.ofNullable(build) + .filter(level -> !level.importedBefore()) + .flatMap(Level::create) + .orElse(null); var message = world != null ? "world.import.success" : "world.import.failed"; plugin.bundle().sendMessage(context.getSource().getSender(), message, @@ -69,6 +80,7 @@ private int execute(CommandContext context, @Nullable World. if (world != null) { plugin.persistWorld(world, true); + if (generator != null) plugin.persistGenerator(world, generator); plugin.levelView().saveLevelData(world, true); } diff --git a/plugin/src/main/java/net/thenextlvl/worlds/command/WorldLoadCommand.java b/plugin/src/main/java/net/thenextlvl/worlds/command/WorldLoadCommand.java index efa4671e..280ca3e6 100644 --- a/plugin/src/main/java/net/thenextlvl/worlds/command/WorldLoadCommand.java +++ b/plugin/src/main/java/net/thenextlvl/worlds/command/WorldLoadCommand.java @@ -9,10 +9,12 @@ import lombok.RequiredArgsConstructor; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.thenextlvl.worlds.WorldsPlugin; +import net.thenextlvl.worlds.api.model.Level; import net.thenextlvl.worlds.command.suggestion.LevelSuggestionProvider; import org.bukkit.entity.Entity; import java.io.File; +import java.util.Optional; import static org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.COMMAND; @@ -33,8 +35,8 @@ private int load(CommandContext context) { var name = context.getArgument("world", String.class); var level = new File(plugin.getServer().getWorldContainer(), name); - var world = plugin.levelView().isLevel(level) ? plugin.levelView().loadLevel(level, - optional -> optional.map(extras -> !extras.enabled()).isPresent()) : null; + var build = plugin.levelView().isLevel(level) ? plugin.levelBuilder(level).build(): null; + var world = Optional.ofNullable(build).filter(Level::importedBefore).flatMap(Level::create).orElse(null); var message = world != null ? "world.load.success" : "world.load.failed"; plugin.bundle().sendMessage(context.getSource().getSender(), message, diff --git a/plugin/src/main/java/net/thenextlvl/worlds/command/argument/WorldTypeArgument.java b/plugin/src/main/java/net/thenextlvl/worlds/command/argument/WorldTypeArgument.java index a6fceff5..25f05a9e 100644 --- a/plugin/src/main/java/net/thenextlvl/worlds/command/argument/WorldTypeArgument.java +++ b/plugin/src/main/java/net/thenextlvl/worlds/command/argument/WorldTypeArgument.java @@ -4,19 +4,20 @@ import io.papermc.paper.command.brigadier.argument.ArgumentTypes; import net.kyori.adventure.key.Key; import net.thenextlvl.worlds.WorldsPlugin; +import net.thenextlvl.worlds.api.model.WorldPreset; import net.thenextlvl.worlds.command.suggestion.WorldTypeSuggestionProvider; -import org.bukkit.WorldType; @SuppressWarnings("UnstableApiUsage") -public class WorldTypeArgument extends WrappedArgumentType { +public class WorldTypeArgument extends WrappedArgumentType { public WorldTypeArgument(WorldsPlugin plugin) { - super(ArgumentTypes.key(), (reader, type) -> switch (type.asString()) { - case "minecraft:amplified" -> WorldType.AMPLIFIED; - case "minecraft:flat" -> WorldType.FLAT; - case "minecraft:large_biomes" -> WorldType.LARGE_BIOMES; - case "minecraft:normal" -> WorldType.NORMAL; - // case "minecraft:single_biome" -> ; - // case "minecraft:debug_world" -> ; + super(ArgumentTypes.key(), (reader, type) -> switch (type.asMinimalString()) { + case "amplified" -> WorldPreset.AMPLIFIED; + case "checkerboard" -> WorldPreset.CHECKERBOARD; + case "debug_all_block_states", "debug_world", "debug" -> WorldPreset.DEBUG; + case "fixed", "single_biome" -> WorldPreset.SINGLE_BIOME; + case "flat" -> WorldPreset.FLAT; + case "large_biomes" -> WorldPreset.LARGE_BIOMES; + case "noise", "normal", "default" -> WorldPreset.NORMAL; default -> throw new IllegalArgumentException("Custom dimensions are not yet supported"); }, new WorldTypeSuggestionProvider(plugin)); } diff --git a/plugin/src/main/java/net/thenextlvl/worlds/command/suggestion/WorldTypeSuggestionProvider.java b/plugin/src/main/java/net/thenextlvl/worlds/command/suggestion/WorldTypeSuggestionProvider.java index dcbc6fc6..86231c75 100644 --- a/plugin/src/main/java/net/thenextlvl/worlds/command/suggestion/WorldTypeSuggestionProvider.java +++ b/plugin/src/main/java/net/thenextlvl/worlds/command/suggestion/WorldTypeSuggestionProvider.java @@ -18,9 +18,12 @@ public class WorldTypeSuggestionProvider implements SuggestionProvider { private final Map dimensions = Map.of( "minecraft:amplified", "world.type.amplified", + "minecraft:checkerboard", "world.type.checkerboard", + "minecraft:debug", "world.type.debug", "minecraft:flat", "world.type.flat", "minecraft:large_biomes", "world.type.large_biomes", - "minecraft:normal", "world.type.normal" + "minecraft:normal", "world.type.normal", + "minecraft:single_biome", "world.type.single_biome" ); @Override diff --git a/plugin/src/main/java/net/thenextlvl/worlds/listener/ServerListener.java b/plugin/src/main/java/net/thenextlvl/worlds/listener/ServerListener.java index e4ad97a1..b91cab64 100644 --- a/plugin/src/main/java/net/thenextlvl/worlds/listener/ServerListener.java +++ b/plugin/src/main/java/net/thenextlvl/worlds/listener/ServerListener.java @@ -17,18 +17,22 @@ public void onWorldLoad(WorldLoadEvent event) { if (!event.getWorld().key().asString().equals("minecraft:overworld")) return; plugin.levelView().listLevels() .filter(plugin.levelView()::canLoad) - .forEach(level -> { + .forEach(levelDirectory -> { try { - var world = plugin.levelView().loadLevel(level); - if (world != null) plugin.getComponentLogger().debug("Loaded dimension {} at {}", - world.key().asString(), level.getPath()); + var level = plugin.levelBuilder(levelDirectory).build(); + if (!level.enabled()) return; + level.create().ifPresent(world -> plugin.getComponentLogger().debug( + "Loaded dimension {} ({}) from {}", + world.key().asString(), level.type().key().asString(), + world.getWorldFolder().getPath() + )); } catch (GeneratorException e) { var generator = e.getId() != null ? e.getPlugin() + e.getId() : e.getPlugin(); - plugin.getComponentLogger().error("Skip loading dimension {}", level.getName()); + plugin.getComponentLogger().error("Skip loading dimension {}", levelDirectory.getName()); plugin.getComponentLogger().error("Cannot use generator {}: {}", generator, e.getMessage()); } catch (Exception e) { plugin.getComponentLogger().error("An unexpected error occurred while loading the level {}", - level.getPath(), e); + levelDirectory.getName(), e); plugin.getComponentLogger().error("Please report the error above on GitHub: {}", "https://github.com/TheNextLvl-net/worlds/issues/new/choose"); } diff --git a/plugin/src/main/java/net/thenextlvl/worlds/model/PaperLevel.java b/plugin/src/main/java/net/thenextlvl/worlds/model/PaperLevel.java new file mode 100644 index 00000000..8b19919e --- /dev/null +++ b/plugin/src/main/java/net/thenextlvl/worlds/model/PaperLevel.java @@ -0,0 +1,132 @@ +package net.thenextlvl.worlds.model; + +import com.google.gson.JsonObject; +import core.nbt.file.NBTFile; +import core.nbt.tag.ByteTag; +import core.nbt.tag.CompoundTag; +import core.nbt.tag.LongTag; +import lombok.Getter; +import lombok.experimental.Accessors; +import net.thenextlvl.worlds.WorldsPlugin; +import net.thenextlvl.worlds.api.model.*; +import net.thenextlvl.worlds.api.preset.Preset; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.WorldType; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; +import java.util.concurrent.ThreadLocalRandom; + +@Getter +@Accessors(fluent = true) +public class PaperLevel implements Level { + private final NBTFile levelData; + private final WorldsPlugin plugin; + + private final NamespacedKey key; + private final String name; + private final World.Environment environment; + private final WorldPreset type; + + private final @Nullable Generator generator; + private final @Nullable Preset preset; + + private final boolean enabled; + private final boolean hardcore; + private final boolean importedBefore; + private final boolean structures; + private final long seed; + + public PaperLevel(WorldsPlugin plugin, LevelBuilder builder) { + this.levelData = plugin.levelView().getLevelDataFile(builder.level()); + this.plugin = plugin; + + this.name = Optional.ofNullable(builder.name()) + .orElseGet(() -> builder.level().getName()); + + var data = levelData().getRoot().optional("Data"); + var extras = data.flatMap(plugin.levelView()::getExtras); + + this.importedBefore = extras.isPresent(); + this.enabled = extras.filter(LevelExtras::enabled).isPresent(); + + this.environment = Optional.ofNullable(builder.environment()) + .orElseGet(() -> plugin.levelView().getEnvironment(builder.level())); + + this.key = Optional.ofNullable(builder.key()) + .or(() -> extras.map(LevelExtras::key)) + .orElseGet(() -> { + var namespace = builder.level().getName().toLowerCase() + .replace("(", "").replace(")", "") + .replace(" ", "_"); + return new NamespacedKey("worlds", namespace); + }); + + var settings = data.flatMap(tag -> tag.optional("WorldGenSettings")); + var dimensions = settings.flatMap(tag -> tag.optional("dimensions")); + var dimension = dimensions.flatMap(tag -> tag.optional( + plugin.levelView().getDimension(tag, environment))); + var generator = dimension.flatMap(tag -> tag.optional("generator")); + + + this.hardcore = Optional.ofNullable(builder.hardcore()) + .or(() -> settings.flatMap(tag -> tag.optional("hardcore")) + .map(ByteTag::getAsBoolean)) + .orElse(true); + this.seed = Optional.ofNullable(builder.seed()) + .or(() -> settings.flatMap(tag -> tag.optional("seed")) + .map(LongTag::getAsLong)) + .orElse(ThreadLocalRandom.current().nextLong()); + this.structures = Optional.ofNullable(builder.structures()) + .or(() -> settings.flatMap(tag -> tag.optional("generate_features")) + .map(ByteTag::getAsBoolean)) + .orElse(true); + + + var worldPreset = generator.flatMap(plugin.levelView()::getWorldPreset); + + this.preset = Optional.ofNullable(builder.preset()) + .or(() -> worldPreset.filter(preset -> preset.equals(WorldPreset.FLAT)) + .flatMap(worldType -> generator.flatMap(plugin.levelView()::getFlatPreset))) + .orElse(null); + + this.type = Optional.ofNullable(builder.type()).orElseGet(() -> + worldPreset.orElse(WorldPreset.NORMAL)); + + this.generator = Optional.ofNullable(builder.generator()) + .or(() -> extras.map(LevelExtras::generator)) + .orElse(null); + } + + private WorldType typeOf(WorldPreset worldPreset) { + if (worldPreset.equals(WorldPreset.AMPLIFIED)) return WorldType.AMPLIFIED; + if (worldPreset.equals(WorldPreset.FLAT)) return WorldType.FLAT; + if (worldPreset.equals(WorldPreset.LARGE_BIOMES)) return WorldType.LARGE_BIOMES; + if (worldPreset.equals(WorldPreset.NORMAL)) return WorldType.NORMAL; + plugin.getComponentLogger().warn("Custom world presets do not work yet, defaulting to normal"); + return WorldType.NORMAL; + } + + @Override + public Optional create() { + var generatorSettings = Optional.ofNullable(preset()) + .map(Preset::serialize) + .map(JsonObject::toString) + .orElse(""); + + var creator = new WorldCreator(name(), key()) + .environment(environment()) + .generateStructures(structures()) + .generatorSettings(generatorSettings) + .hardcore(hardcore()) + .seed(seed()) + .type(typeOf(type())); + + if (generator() != null) creator.generator(generator().generator(creator.name())); + if (generator() != null) creator.biomeProvider(generator().biomeProvider(creator.name())); + + return Optional.ofNullable(creator.createWorld()); + } +} diff --git a/plugin/src/main/java/net/thenextlvl/worlds/model/PaperLevelBuilder.java b/plugin/src/main/java/net/thenextlvl/worlds/model/PaperLevelBuilder.java new file mode 100644 index 00000000..45665f22 --- /dev/null +++ b/plugin/src/main/java/net/thenextlvl/worlds/model/PaperLevelBuilder.java @@ -0,0 +1,41 @@ +package net.thenextlvl.worlds.model; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.experimental.Accessors; +import net.thenextlvl.worlds.WorldsPlugin; +import net.thenextlvl.worlds.api.model.Generator; +import net.thenextlvl.worlds.api.model.Level; +import net.thenextlvl.worlds.api.model.LevelBuilder; +import net.thenextlvl.worlds.api.model.WorldPreset; +import net.thenextlvl.worlds.api.preset.Preset; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +@Getter +@Setter +@RequiredArgsConstructor +@Accessors(fluent = true) +public class PaperLevelBuilder implements LevelBuilder { + private final WorldsPlugin plugin; + private final File level; + + private @Nullable Boolean hardcore; + private @Nullable Boolean structures; + private @Nullable Generator generator; + private @Nullable Long seed; + private @Nullable NamespacedKey key; + private @Nullable Preset preset; + private @Nullable String name; + private @Nullable World.Environment environment; + private @Nullable WorldPreset type; + + @Override + public Level build() { + return new PaperLevel(plugin, this); + } +} diff --git a/plugin/src/main/java/net/thenextlvl/worlds/view/PaperLevelView.java b/plugin/src/main/java/net/thenextlvl/worlds/view/PaperLevelView.java index e4185a9e..7b467128 100644 --- a/plugin/src/main/java/net/thenextlvl/worlds/view/PaperLevelView.java +++ b/plugin/src/main/java/net/thenextlvl/worlds/view/PaperLevelView.java @@ -3,7 +3,10 @@ import core.io.IO; import core.io.PathIO; import core.nbt.file.NBTFile; -import core.nbt.tag.*; +import core.nbt.tag.CompoundTag; +import core.nbt.tag.ListTag; +import core.nbt.tag.StringTag; +import core.nbt.tag.Tag; import io.papermc.paper.plugin.provider.classloader.ConfiguredPluginClassLoader; import lombok.RequiredArgsConstructor; import net.thenextlvl.worlds.WorldsPlugin; @@ -17,17 +20,12 @@ import net.thenextlvl.worlds.api.view.LevelView; import org.bukkit.NamespacedKey; import org.bukkit.World; -import org.bukkit.WorldCreator; -import org.bukkit.WorldType; import org.bukkit.craftbukkit.CraftWorld; -import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.Arrays; import java.util.LinkedHashSet; -import java.util.NoSuchElementException; import java.util.Optional; -import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -74,89 +72,6 @@ public World.Environment getEnvironment(File level) { return World.Environment.NORMAL; } - @Override - public @Nullable World loadLevel(File level) { - return loadLevel(level, getEnvironment(level)); - } - - @Override - public @Nullable World loadLevel(File level, @Nullable NamespacedKey key, Predicate> predicate) { - return loadLevel(level, getEnvironment(level), key, predicate); - } - - @Override - public @Nullable World loadLevel(File level, World.Environment environment) { - return loadLevel(level, environment, extras -> extras.filter(LevelExtras::enabled).isPresent()); - } - - @Override - public @Nullable World loadLevel(File level, Predicate> predicate) { - return loadLevel(level, getEnvironment(level), predicate); - } - - @Override - public @Nullable World loadLevel(File level, World.Environment environment, Predicate> predicate) { - return loadLevel(level, environment, null, predicate); - } - - @Override - public @Nullable World loadLevel(File level, World.Environment environment, @Nullable NamespacedKey key, Predicate> predicate) { - var data = getLevelDataFile(level).getRoot().optional("Data"); - var extras = data.flatMap(this::getExtras); - - if (!predicate.test(extras)) return null; - - var settings = data.flatMap(tag -> tag.optional("WorldGenSettings")); - var dimensions = settings.flatMap(tag -> tag.optional("dimensions")); - var dimension = dimensions.flatMap(tag -> tag.optional(getDimension(tag, environment))); - var generator = dimension.flatMap(tag -> tag.optional("generator")); - - var worldPreset = generator.flatMap(this::getWorldPreset); - - var generatorSettings = worldPreset.filter(preset -> preset.equals(WorldPreset.FLAT)) - .flatMap(worldType -> generator.flatMap(this::getFlatPreset)); - - var hardcore = data.flatMap(tag -> tag.optional("hardcore")) - .orElseThrow(() -> new NoSuchElementException("hardcore")) - .getAsBoolean(); - var seed = settings.flatMap(tag -> tag.optional("seed")) - .orElseThrow(() -> new NoSuchElementException("seed")) - .getAsInt(); - var structures = settings.flatMap(tag -> tag.optional("generate_features")) - .orElseThrow(() -> new NoSuchElementException("generate_features")) - .getAsBoolean(); - - var worldKey = Optional.ofNullable(key).orElseGet(() -> - extras.map(LevelExtras::key).orElseGet(() -> { - var namespace = level.getName().toLowerCase() - .replace("(", "").replace(")", "") - .replace(" ", "_"); - return new NamespacedKey("worlds", namespace); - })); - - var creator = new WorldCreator(level.getName(), worldKey) - .environment(environment) - .generateStructures(structures) - .hardcore(hardcore) - .seed(seed) - .type(typeOf(worldPreset.orElse(WorldPreset.NORMAL))); - - generatorSettings.ifPresent(preset -> creator.generatorSettings(preset.serialize().toString())); - - var generatorExtras = extras.map(LevelExtras::generator); - generatorExtras.map(gen -> gen.biomeProvider(creator.name())).ifPresent(creator::biomeProvider); - generatorExtras.map(gen -> gen.generator(creator.name())).ifPresent(creator::generator); - - return creator.createWorld(); - } - - private WorldType typeOf(WorldPreset worldPreset) { - if (worldPreset.equals(WorldPreset.AMPLIFIED)) return WorldType.AMPLIFIED; - if (worldPreset.equals(WorldPreset.FLAT)) return WorldType.FLAT; - if (worldPreset.equals(WorldPreset.LARGE_BIOMES)) return WorldType.LARGE_BIOMES; - return WorldType.NORMAL; - } - @Override public Optional getExtras(CompoundTag data) { return data.optional("BukkitValues") diff --git a/plugin/src/main/resources/worlds.properties b/plugin/src/main/resources/worlds.properties index 943d1621..7b9b4338 100644 --- a/plugin/src/main/resources/worlds.properties +++ b/plugin/src/main/resources/worlds.properties @@ -47,9 +47,12 @@ world.teleport.other= Teleported to Teleported entities to world.teleport.self= You got teleported to world.type.amplified=Amplified +world.type.checkerboard=Checkerboard +world.type.debug=Debug all block states world.type.flat=Superflat world.type.large_biomes=Large biomes world.type.normal=Default +world.type.single_biome=Single biome world.unlink.failed= Failed to unlink from world.unlink.success= Unlinked from world.unload.disallowed= The overworld cannot be unloaded diff --git a/plugin/src/main/resources/worlds_german.properties b/plugin/src/main/resources/worlds_german.properties index 9f5b26bb..06aaea30 100644 --- a/plugin/src/main/resources/worlds_german.properties +++ b/plugin/src/main/resources/worlds_german.properties @@ -44,9 +44,12 @@ world.teleport.other= wurde zu Objekte wurden zu teleportiert world.teleport.self= Du wurdest zu teleportiert world.type.amplified=Erweitert +world.type.checkerboard=Schachbrett +world.type.debug=Debuggen aller Blockzustände world.type.flat=Superflach world.type.large_biomes=Große Biome world.type.normal=Standard +world.type.single_biome=Einzelnes Biom world.unlink.failed= konnte nicht von getrennt werden world.unlink.success= wurde von getrennt world.unload.disallowed= Die Oberwelt kann nicht entladen werden