Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for creating worlds with single biome #3149

Merged
merged 2 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.dumptruckman.minecraft.util.Logging;
import com.google.common.collect.Lists;
import jakarta.inject.Inject;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.block.Biome;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;

Expand Down Expand Up @@ -59,6 +63,15 @@ class CreateCommand extends CoreCommand {
.addAlias("-a")
.build());

private final CommandValueFlag<Biome> BIOME_FLAG = flag(CommandValueFlag.builder("--biome", Biome.class)
.addAlias("-b")
.completion(input -> Lists.newArrayList(Registry.BIOME).stream()
.filter(biome -> biome !=Biome.CUSTOM)
.map(biome -> biome.getKey().getKey())
.toList())
.context(biomeStr -> Registry.BIOME.get(NamespacedKey.minecraft(biomeStr)))
.build());

@Inject
CreateCommand(
@NotNull MVCommandManager commandManager,
Expand All @@ -73,7 +86,7 @@ class CreateCommand extends CoreCommand {
@Subcommand("create")
@CommandPermission("multiverse.core.create")
@CommandCompletion("@empty @environments @flags:groupName=mvcreatecommand")
@Syntax("<name> <environment> --seed [seed] --generator [generator[:id]] --world-type [worldtype] --adjust-spawn --no-structures")
@Syntax("<name> <environment> [--seed <seed> --generator <generator[:id]> --world-type <worldtype> --adjust-spawn --no-structures --biome <biome>]")
@Description("{@@mv-core.create.description}")
void onCreateCommand(
MVCommandIssuer issuer,
Expand All @@ -87,7 +100,7 @@ void onCreateCommand(
World.Environment environment,

@Optional
@Syntax("--seed [seed] --generator [generator[:id]] --world-type [worldtype] --adjust-spawn --no-structures")
@Syntax("[--seed <seed> --generator <generator[:id]> --world-type <worldtype> --adjust-spawn --no-structures --biome <biome>]")
@Description("{@@mv-core.create.flags.description}")
String[] flags) {
ParsedCommandFlags parsedFlags = parseFlags(flags);
Expand All @@ -102,14 +115,21 @@ void onCreateCommand(
"{worldType}", parsedFlags.flagValue(WORLD_TYPE_FLAG, WorldType.NORMAL).name());
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_ADJUSTSPAWN,
"{adjustSpawn}", String.valueOf(!parsedFlags.hasFlag(NO_ADJUST_SPAWN_FLAG)));
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_GENERATOR,
"{generator}", parsedFlags.flagValue(GENERATOR_FLAG, ""));
if (parsedFlags.hasFlag(BIOME_FLAG)) {
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_BIOME,
"{biome}", parsedFlags.flagValue(BIOME_FLAG, Biome.CUSTOM).name());
}
if (parsedFlags.hasFlag(GENERATOR_FLAG)) {
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_GENERATOR,
"{generator}", parsedFlags.flagValue(GENERATOR_FLAG));
}
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_STRUCTURES,
"{structures}", String.valueOf(!parsedFlags.hasFlag(NO_STRUCTURES_FLAG)));

issuer.sendInfo(MVCorei18n.CREATE_LOADING);

worldManager.createWorld(CreateWorldOptions.worldName(worldName)
.biome(parsedFlags.flagValue(BIOME_FLAG, Biome.CUSTOM))
.environment(environment)
.seed(parsedFlags.flagValue(SEED_FLAG))
.worldType(parsedFlags.flagValue(WORLD_TYPE_FLAG, WorldType.NORMAL))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.dumptruckman.minecraft.util.Logging;
import com.google.common.collect.Lists;
import jakarta.inject.Inject;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;

Expand Down Expand Up @@ -42,6 +46,16 @@ class ImportCommand extends CoreCommand {
.addAlias("-n")
.build());

private final CommandValueFlag<Biome> BIOME_FLAG = flag(CommandValueFlag.builder("--biome", Biome.class)
.addAlias("-b")
//todo: Implement some default completions or smt to reduce duplication
.completion(input -> Lists.newArrayList(Registry.BIOME).stream()
.filter(biome -> biome !=Biome.CUSTOM)
.map(biome -> biome.getKey().getKey())
.toList())
.context(biomeStr -> Registry.BIOME.get(NamespacedKey.minecraft(biomeStr)))
.build());

@Inject
ImportCommand(
@NotNull MVCommandManager commandManager,
Expand All @@ -56,7 +70,7 @@ class ImportCommand extends CoreCommand {
@Subcommand("import")
@CommandPermission("multiverse.core.import")
@CommandCompletion("@mvworlds:scope=potential @environments @flags:groupName=mvimportcommand")
@Syntax("<name> <env> --generator [generator[:id]] --adjust-spawn")
@Syntax("<name> <env> [--generator <generator[:id]> --adjust-spawn --biome <biome>]")
@Description("{@@mv-core.import.description}")
void onImportCommand(
MVCommandIssuer issuer,
Expand All @@ -71,13 +85,14 @@ void onImportCommand(
World.Environment environment,

@Optional
@Syntax("--generator [generator[:id]] --adjust-spawn")
@Syntax("[--generator <generator[:id]> --adjust-spawn --biome <biome>]")
@Description("{@@mv-core.import.other.description}")
String[] flags) {
ParsedCommandFlags parsedFlags = parseFlags(flags);

issuer.sendInfo(MVCorei18n.IMPORT_IMPORTING, "{world}", worldName);
worldManager.importWorld(ImportWorldOptions.worldName(worldName)
.biome(parsedFlags.flagValue(BIOME_FLAG, Biome.CUSTOM))
.environment(environment)
.generator(parsedFlags.flagValue(GENERATOR_FLAG, String.class))
.useSpawnAdjust(!parsedFlags.hasFlag(NO_ADJUST_SPAWN_FLAG)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,14 @@ private Map<String, String> getInfo(LoadedMultiverseWorld world) {
outMap.put("World Name", world.getName());
outMap.put("World Alias", world.getAlias());
outMap.put("World UID", world.getUID().toString());
outMap.put("Game Mode: ", world.getGameMode().toString());
outMap.put("Game Mode", world.getGameMode().toString());
outMap.put("Difficulty", world.getDifficulty().toString());
outMap.put("Spawn Location", locationManipulation.strCoords(world.getSpawnLocation()));
outMap.put("Seed", String.valueOf(world.getSeed()));
getEntryFeeInfo(outMap, world); // Entry fee/reward
outMap.put("Respawn World", world.getRespawnWorldName());
outMap.put("World Type", world.getWorldType().get().toString());
outMap.put("Biome", world.getBiome() == null ? "@vanilla" : world.getBiome().getKey().getKey());
outMap.put("Generator", world.getGenerator());
outMap.put("Generate Structures", world.canGenerateStructures().get().toString());
outMap.put("World Scale", String.valueOf(world.getScale()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.dumptruckman.minecraft.util.Logging;
import com.google.common.collect.Lists;
import jakarta.inject.Inject;
import org.bukkit.Registry;
import org.bukkit.block.Biome;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
Expand Down Expand Up @@ -114,6 +117,7 @@ private void doWorldRegening(
LoadedMultiverseWorld world,
ParsedCommandFlags parsedFlags,
List<Player> worldPlayers) {
//todo: Change biome on regen
RegenWorldOptions regenWorldOptions = RegenWorldOptions.world(world)
.randomSeed(parsedFlags.hasFlag(SEED_FLAG))
.seed(parsedFlags.flagValue(SEED_FLAG))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public enum MVCorei18n implements MessageKeyProvider {
CREATE_PROPERTIES_SEED,
CREATE_PROPERTIES_WORLDTYPE,
CREATE_PROPERTIES_ADJUSTSPAWN,
CREATE_PROPERTIES_BIOME,
CREATE_PROPERTIES_GENERATOR,
CREATE_PROPERTIES_STRUCTURES,
CREATE_LOADING,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.entity.Player;
import org.bukkit.generator.BiomeProvider;
import org.jetbrains.annotations.NotNull;

import org.mvplugins.multiverse.core.api.BlockSafety;
Expand Down Expand Up @@ -47,6 +48,10 @@ public class LoadedMultiverseWorld extends MultiverseWorld {
private void setupWorldConfig(World world) {
worldConfig.setMVWorld(this);
worldConfig.load();
BiomeProvider biomeProvider = world.getBiomeProvider();
if (biomeProvider instanceof SingleBiomeProvider singleBiomeProvider) {
worldConfig.setBiome(singleBiomeProvider.getBiome());
}
worldConfig.setEnvironment(world.getEnvironment());
worldConfig.setSeed(world.getSeed());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.generator.BiomeProvider;
import org.jetbrains.annotations.Nullable;

import org.mvplugins.multiverse.core.configuration.handle.StringPropertyHandle;
Expand Down Expand Up @@ -195,6 +197,16 @@ public boolean getBedRespawn() {
return worldConfig.getBedRespawn();
}

/**
* Gets the single biome used for this world. This may be null, in which case the biome from the generator will be used.
* If no generator is specified, the "natural" biome behaviour for this environment will be used.
*
* @return The biome used for this world
*/
public @Nullable Biome getBiome() {
return worldConfig.getBiome();
}

/**
* Sets whether or not a player who dies in this world will respawn in their
* bed or follow the normal respawn pattern.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.mvplugins.multiverse.core.world;

import org.bukkit.WorldCreator;
import org.bukkit.block.Biome;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.WorldInfo;
import org.jetbrains.annotations.NotNull;

import java.util.List;

/**
* Helps create a world with only 1 type of Biome specified. Used in {@link WorldCreator#biomeProvider(BiomeProvider)}
*/
public class SingleBiomeProvider extends BiomeProvider {

private final Biome biome;
private final List<Biome> biomes;

public SingleBiomeProvider(Biome biome) {
this.biome = biome;
this.biomes = List.of(biome);
}

@Override
public @NotNull Biome getBiome(@NotNull WorldInfo worldInfo, int x, int y, int z) {
return this.biome;
}

@Override
public @NotNull List<Biome> getBiomes(@NotNull WorldInfo worldInfo) {
return this.biomes;
}

public Biome getBiome() {
return biome;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.WorldType;
import org.bukkit.block.Biome;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.plugin.PluginManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -199,6 +201,7 @@ private Attempt<LoadedMultiverseWorld, CreateFailureReason> createValidatedWorld
CreateWorldOptions options) {
String parsedGenerator = parseGenerator(options.worldName(), options.generator());
WorldCreator worldCreator = WorldCreator.name(options.worldName())
.biomeProvider(createSingleBiomeProvider(options.biome()))
.environment(options.environment())
.generateStructures(options.generateStructures())
.generator(parsedGenerator)
Expand Down Expand Up @@ -246,6 +249,7 @@ private Attempt<LoadedMultiverseWorld, ImportFailureReason> doImportWorld(
ImportWorldOptions options) {
String parsedGenerator = parseGenerator(options.worldName(), options.generator());
WorldCreator worldCreator = WorldCreator.name(options.worldName())
.biomeProvider(createSingleBiomeProvider(options.biome()))
.environment(options.environment())
.generator(parsedGenerator);
return createBukkitWorld(worldCreator).fold(
Expand Down Expand Up @@ -337,6 +341,7 @@ private Attempt<MultiverseWorld, LoadFailureReason> validateWorldToLoad(

private Attempt<LoadedMultiverseWorld, LoadFailureReason> doLoadWorld(@NotNull MultiverseWorld mvWorld) {
return createBukkitWorld(WorldCreator.name(mvWorld.getName())
.biomeProvider(createSingleBiomeProvider(mvWorld.getBiome()))
.environment(mvWorld.getEnvironment())
.generator(Strings.isNullOrEmpty(mvWorld.getGenerator()) ? null : mvWorld.getGenerator())
.seed(mvWorld.getSeed())).fold(
Expand All @@ -355,6 +360,18 @@ private Attempt<LoadedMultiverseWorld, LoadFailureReason> doLoadWorld(@NotNull M
});
}

/**
* Creates a single biome provider for the specified biome.
* @param biome The biome
* @return The single biome provider or null if biome is null or custom
*/
private @Nullable BiomeProvider createSingleBiomeProvider(@Nullable Biome biome) {
if (biome == null || biome == Biome.CUSTOM) {
return null;
}
return new SingleBiomeProvider(biome);
}

/**
* Unloads an existing multiverse world. It will still remain as an unloaded world.
*
Expand Down Expand Up @@ -516,6 +533,7 @@ public Attempt<LoadedMultiverseWorld, CloneFailureReason> cloneWorld(@NotNull Cl
.mapAttempt(validatedOptions -> {
ImportWorldOptions importWorldOptions = ImportWorldOptions
.worldName(validatedOptions.newWorldName())
.biome(validatedOptions.world().getBiome())
.environment(validatedOptions.world().getEnvironment())
.generator(validatedOptions.world().getGenerator());
return importWorld(importWorldOptions).transform(CloneFailureReason.IMPORT_FAILED);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.mvplugins.multiverse.core.world.config;

import org.bukkit.block.Biome;
import org.mvplugins.multiverse.core.configuration.functions.NodeSerializer;

Check warning on line 4 in src/main/java/org/mvplugins/multiverse/core/world/config/BiomeSerializer.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 'org.mvplugins.multiverse.core.configuration.functions.NodeSerializer' should be separated from previous imports. Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/world/config/BiomeSerializer.java:4:1: warning: 'org.mvplugins.multiverse.core.configuration.functions.NodeSerializer' should be separated from previous imports. (com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderCheck)

public class BiomeSerializer implements NodeSerializer<Biome> {

Check warning on line 6 in src/main/java/org/mvplugins/multiverse/core/world/config/BiomeSerializer.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Missing a Javadoc comment. Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/world/config/BiomeSerializer.java:6:1: warning: Missing a Javadoc comment. (com.puppycrawl.tools.checkstyle.checks.javadoc.MissingJavadocTypeCheck)

static final String VANILLA_BIOME_BEHAVIOUR = "@vanilla";

Check warning on line 8 in src/main/java/org/mvplugins/multiverse/core/world/config/BiomeSerializer.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Missing a Javadoc comment. Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/world/config/BiomeSerializer.java:8:5: warning: Missing a Javadoc comment. (com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocVariableCheck)

@Override

Check warning on line 10 in src/main/java/org/mvplugins/multiverse/core/world/config/BiomeSerializer.java

View workflow job for this annotation

GitHub Actions / checkstyle / checkstyle

[checkstyle] reported by reviewdog 🐶 Return count is 4 (max allowed for non-void methods/lambdas is 2). Raw Output: /github/workspace/./src/main/java/org/mvplugins/multiverse/core/world/config/BiomeSerializer.java:10:5: info: Return count is 4 (max allowed for non-void methods/lambdas is 2). (com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheck)
public Biome deserialize(Object object, Class<Biome> type) {
if (object instanceof Biome) {
return (Biome) object;
}
try {
String biomeStr = String.valueOf(object);
if (biomeStr.equalsIgnoreCase(VANILLA_BIOME_BEHAVIOUR)) {
return null;
}
return Biome.valueOf(biomeStr.toUpperCase());
} catch (IllegalArgumentException e) {
return null;
}
}

@Override
public Object serialize(Biome biome, Class<Biome> type) {
if (biome == null || biome == Biome.CUSTOM) {
return VANILLA_BIOME_BEHAVIOUR;
}
return biome.name().toLowerCase();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -165,6 +166,14 @@ public Try<Void> setAutoLoad(boolean autoLoad) {
return configHandle.set(configNodes.AUTO_LOAD, autoLoad);
}

public Biome getBiome() {
return configHandle.get(configNodes.BIOME);
}

public Try<Void> setBiome(Biome biome) {
return configHandle.set(configNodes.BIOME, biome);
}

public boolean getBedRespawn() {
return configHandle.get(configNodes.BED_RESPAWN);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.jetbrains.annotations.NotNull;

import org.mvplugins.multiverse.core.MultiverseCore;
Expand Down Expand Up @@ -95,6 +96,12 @@ private <N extends Node> N node(N node) {
.defaultValue(true)
.build());

final ConfigNode<Biome> BIOME = node(ConfigNode.builder("biome", Biome.class)
.defaultValue(Biome.CUSTOM)
.name(null)
.serializer(new BiomeSerializer())
.build());

final ConfigNode<Difficulty> DIFFICULTY = node(ConfigNode.builder("difficulty", Difficulty.class)
.defaultValue(Difficulty.NORMAL)
.onSetValue((oldValue, newValue) -> {
Expand Down
Loading
Loading