Skip to content
Open
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 @@ -149,6 +149,23 @@ public class Console extends ConfigurationPart {
public boolean hasAllPermissions = false;
}

public WorldGeneration worldGeneration;

public class WorldGeneration extends ConfigurationPart {
@Comment(
"Whether seed overrides should also be applied to dimensions that already have saved world generation settings. " +
"Existing chunks are not regenerated, so changing a dimension's seed may create abrupt chunk borders."
)
public boolean applyToExistingDimensions = false;

@Comment(
"Overrides the seed used to generate a dimension. " +
"Keys are dimension identifiers, for example minecraft:overworld or example:custom_dimension. " +
"By default, overrides are only applied when a dimension is created for the first time."
)
public Map<Identifier, Long> dimensionSeedOverrides = Map.of();
}

public Watchdog watchdog;

public class Watchdog extends ConfigurationPart {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.papermc.paper.world.saveddata.PaperLevelOverrides;
import io.papermc.paper.world.saveddata.PaperWorldMetadata;
import io.papermc.paper.world.saveddata.PaperWorldPDC;
import io.papermc.paper.world.settings.PaperWorldGenSettings;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Locale;
Expand Down Expand Up @@ -144,13 +145,18 @@ private void loadInitialWorld(final LevelStem stem, final boolean hasWorldData)
return;
}

final WorldGenSettings worldGenSettings = !hasWorldData
final WorldGenSettings storedWorldGenSettings = !hasWorldData
? this.server.getWorldGenSettings()
: loadWorldGenSettings(
this.server.storageSource,
this.server.worldLoaderContext.datapackWorldgen(),
loading.info().dimensionKey()
);
final WorldGenSettings worldGenSettings = PaperWorldGenSettings.applySeedOverride(
storedWorldGenSettings,
loading.info().dimensionKey(),
hasWorldData
);
final var worldDataAndGenSettings = new LevelDataAndDimensions.WorldDataAndGenSettings(this.server.getWorldData(), worldGenSettings);

if (loading.info().dimensionKey() == Level.OVERWORLD) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.papermc.paper.world.settings;

import io.papermc.paper.configuration.GlobalConfiguration;
import java.util.Map;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.levelgen.WorldGenSettings;
import net.minecraft.world.level.levelgen.WorldOptions;
import org.jspecify.annotations.NullMarked;

@NullMarked
public final class PaperWorldGenSettings {

private PaperWorldGenSettings() {
}

public static WorldGenSettings applySeedOverride(
final WorldGenSettings settings,
final ResourceKey<Level> dimensionKey,
final boolean existingDimension
) {
final GlobalConfiguration.WorldGeneration configuration = GlobalConfiguration.get().worldGeneration;
return applySeedOverride(
settings,
dimensionKey,
existingDimension,
configuration.applyToExistingDimensions,
configuration.dimensionSeedOverrides
);
}

static WorldGenSettings applySeedOverride(
final WorldGenSettings settings,
final ResourceKey<Level> dimensionKey,
final boolean existingDimension,
final boolean applyToExistingDimensions,
final Map<Identifier, Long> seedOverrides
) {
if (existingDimension && !applyToExistingDimensions) {
return settings;
}

final Long seedOverride = seedOverrides.get(dimensionKey.identifier());
if (seedOverride == null) {
return settings;
}

final WorldOptions options = settings.options();
return new WorldGenSettings(
new WorldOptions(seedOverride, options.generateStructures(), options.generateBonusChest()),
settings.dimensions()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.papermc.paper.world.settings;

import java.util.Map;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.levelgen.WorldDimensions;
import net.minecraft.world.level.levelgen.WorldGenSettings;
import net.minecraft.world.level.levelgen.WorldOptions;
import org.bukkit.support.environment.Normal;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;

@Normal
class PaperWorldGenSettingsTest {

private static final Identifier DIMENSION_ID = Identifier.parse("test:dimension");
private static final ResourceKey<Level> DIMENSION_KEY = ResourceKey.create(Registries.DIMENSION, DIMENSION_ID);

@Test
void returnsOriginalSettingsWithoutOverride() {
final WorldGenSettings settings = settings(123L, true, false);

final WorldGenSettings result = PaperWorldGenSettings.applySeedOverride(settings, DIMENSION_KEY, false, false, Map.of());

assertSame(settings, result);
}

@Test
void overridesOnlySeedForNewDimension() {
final WorldGenSettings settings = settings(123L, false, true);

final WorldGenSettings result = PaperWorldGenSettings.applySeedOverride(settings, DIMENSION_KEY, false, false, Map.of(DIMENSION_ID, 456L));

assertEquals(456L, result.options().seed());
assertFalse(result.options().generateStructures());
assertTrue(result.options().generateBonusChest());
assertSame(settings.dimensions(), result.dimensions());
}

@Test
void doesNotOverrideExistingDimensionByDefault() {
final WorldGenSettings settings = settings(123L, true, false);

final WorldGenSettings result = PaperWorldGenSettings.applySeedOverride(settings, DIMENSION_KEY, true, false, Map.of(DIMENSION_ID, 456L));

assertSame(settings, result);
}

@Test
void overridesExistingDimensionWhenEnabled() {
final WorldGenSettings settings = settings(123L, true, false);

final WorldGenSettings result = PaperWorldGenSettings.applySeedOverride(settings, DIMENSION_KEY, true, true, Map.of(DIMENSION_ID, 456L));

assertEquals(456L, result.options().seed());
}

private static WorldGenSettings settings(final long seed, final boolean generateStructures, final boolean generateBonusChest) {
return new WorldGenSettings(
new WorldOptions(seed, generateStructures, generateBonusChest),
mock(WorldDimensions.class)
);
}
}