Skip to content

Commit

Permalink
Add support for creating worlds with single biome
Browse files Browse the repository at this point in the history
  • Loading branch information
benwoo1110 committed Jan 5, 2025
1 parent 08ac65e commit 71e47d2
Show file tree
Hide file tree
Showing 17 changed files with 242 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,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.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 +62,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(Biome::valueOf)
.build());

@Inject
CreateCommand(
@NotNull MVCommandManager commandManager,
Expand All @@ -73,7 +85,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 +99,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 +114,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,11 @@
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.World;
import org.bukkit.block.Biome;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;

Expand Down Expand Up @@ -42,6 +45,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(Biome::valueOf)
.build());

@Inject
ImportCommand(
@NotNull MVCommandManager commandManager,
Expand All @@ -56,7 +69,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 +84,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

0 comments on commit 71e47d2

Please sign in to comment.