WorldPreset serialization and better structure for BiomeGenerator builder;

This commit is contained in:
Frank 2022-06-19 19:59:29 +02:00
parent d63d773bb8
commit c74b7b2d28
20 changed files with 365 additions and 244 deletions

View file

@ -1,16 +1,8 @@
package org.betterx.bclib.api.v2;
import org.betterx.bclib.api.v2.dataexchange.DataExchangeAPI;
import org.betterx.bclib.api.v2.datafixer.DataFixerAPI;
import org.betterx.bclib.api.v2.levelgen.LevelGenUtil;
import org.betterx.bclib.api.v2.levelgen.biomes.InternalBiomeAPI;
import org.betterx.bclib.mixin.common.RegistryOpsAccessor;
import net.minecraft.client.gui.screens.worldselection.WorldGenSettingsComponent;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
@ -18,13 +10,11 @@ import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.world.level.CustomSpawner;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.levelgen.WorldGenSettings;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.ServerLevelData;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executor;
/**
@ -36,66 +26,6 @@ public class LifeCycleAPI {
private final static List<BeforeLevelLoadCall> beforeLoadLevel = new ArrayList<>(2);
private static void worldCreationStarted(RegistryAccess access) {
InternalBiomeAPI.initRegistry(access);
}
public static void newWorldSetup(
LevelStorageSource.LevelStorageAccess levelStorageAccess,
WorldGenSettings settings
) {
DataExchangeAPI.prepareServerside();
InternalBiomeAPI.prepareNewLevel();
DataFixerAPI.createWorldData(levelStorageAccess, settings);
_runBeforeLevelLoad();
}
public static void newWorldSetup(
String levelID,
WorldGenSettings worldGenSettings,
LevelStorageSource levelSource
) {
DataExchangeAPI.prepareServerside();
InternalBiomeAPI.prepareNewLevel();
DataFixerAPI.createWorldData(levelSource, levelID, worldGenSettings);
_runBeforeLevelLoad();
}
public static void newWorldSetup(LevelStorageSource.LevelStorageAccess levelStorageAccess) {
InternalBiomeAPI.prepareNewLevel();
DataFixerAPI.fixData(levelStorageAccess, false, (didFix) -> {/* not called when showUI==false */});
_runBeforeLevelLoad();
}
public static WorldGenSettings worldLoadStarted(WorldGenSettings settings, Optional<RegistryOps<Tag>> registryOps) {
if (registryOps.orElse(null) instanceof RegistryOpsAccessor acc) {
InternalBiomeAPI.initRegistry(acc.bcl_getRegistryAccess());
}
settings = LevelGenUtil.fixSettingsInCurrentWorld(registryOps, settings);
return settings;
}
public static void worldCreationStarted(RegistryOps<Tag> regOps) {
if (regOps instanceof RegistryOpsAccessor acc) {
worldCreationStarted(acc.bcl_getRegistryAccess());
}
}
public static void worldCreationStarted(
Optional<LevelStorageSource.LevelStorageAccess> levelStorageAccess,
WorldGenSettingsComponent worldGenSettingsComponent
) {
worldCreationStarted(worldGenSettingsComponent.registryHolder());
if (levelStorageAccess.isPresent()) {
newWorldSetup(levelStorageAccess.get(), worldGenSettingsComponent.settings().worldGenSettings());
}
}
/**
* Register a callback that is called before a level is loaded or created,
* but after the {@link WorldDataAPI} was initialized and patches from

View file

@ -1,13 +1,11 @@
package org.betterx.bclib.api.v2;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.api.v2.datafixer.DataFixerAPI;
import org.betterx.bclib.util.ModUtil;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
@ -20,14 +18,13 @@ import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
/**
* Mod-specifix data-storage for a world.
* <p>
* This class provides the ability for mod to store persistent data inside a world. The Storage for the world is
* currently initialized as part of the {@link DataFixerAPI} in {@link DataFixerAPI#fixData(LevelStorageAccess, boolean, Consumer)}
* or {@link DataFixerAPI#initializeWorldData(File, boolean)}
* currently initialized as part of the {@link org.betterx.bclib.presets.worldgen.WorldBootstrap} in
* {@link org.betterx.bclib.presets.worldgen.WorldBootstrap#initializeWorldDataAPI(File, boolean)}
*/
public class WorldDataAPI {
private static final Map<String, CompoundTag> TAGS = Maps.newHashMap();

View file

@ -2,7 +2,6 @@ package org.betterx.bclib.api.v2.datafixer;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.api.v2.WorldDataAPI;
import org.betterx.bclib.api.v2.levelgen.LevelGenUtil;
import org.betterx.bclib.client.gui.screens.AtomicProgressListener;
import org.betterx.bclib.client.gui.screens.ConfirmFixScreen;
import org.betterx.bclib.client.gui.screens.LevelFixErrorScreen;
@ -19,7 +18,6 @@ import net.minecraft.nbt.*;
import net.minecraft.network.chat.Component;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.storage.RegionFile;
import net.minecraft.world.level.levelgen.WorldGenSettings;
import net.minecraft.world.level.storage.LevelResource;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess;
@ -135,77 +133,17 @@ public class DataFixerAPI {
Consumer<Boolean> onResume
) {
File levelPath = levelStorageAccess.getLevelPath(LevelResource.ROOT).toFile();
File levelDat = levelStorageAccess.getLevelPath(LevelResource.LEVEL_DATA_FILE).toFile();
boolean newWorld = false;
if (!levelDat.exists()) {
BCLib.LOGGER.info("Creating a new World, no fixes needed");
newWorld = true;
}
initializeWorldData(levelPath, newWorld);
if (newWorld) return false;
return fixData(levelPath, levelStorageAccess.getLevelId(), showUI, onResume);
}
/**
* Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the
* current versions of the plugins.
* <p>
* This implementation will create a new {@link LevelStorageAccess} and call {@link #initializeWorldData(File, boolean)}
* using the provided root path.
*
* @param levelSource The SourceStorage for this Minecraft instance, You can get this using
* {@code Minecraft.getInstance().getLevelSource()}
* @param levelID The ID of the Level you want to patch
* @param newWorld {@code true} if this is a fresh world
* Creates the patch level file for new worlds
*/
public static void initializeWorldData(LevelStorageSource levelSource, String levelID, boolean newWorld) {
wrapCall(levelSource, levelID, (levelStorageAccess) -> {
initializeWorldData(levelStorageAccess, newWorld);
return true;
});
}
public static void createWorldData(LevelStorageSource levelSource, String levelID, WorldGenSettings settings) {
wrapCall(levelSource, levelID, (levelStorageAccess) -> {
createWorldData(levelStorageAccess, settings);
return true;
});
}
/**
* Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the
* current versions of the plugins.
*
* @param access levelAccess for the worldd
* @param newWorld {@code true} if this is a fresh world
*/
public static void initializeWorldData(LevelStorageAccess access, boolean newWorld) {
initializeWorldData(access.getLevelPath(LevelResource.ROOT).toFile(), newWorld);
}
public static void createWorldData(LevelStorageAccess access, WorldGenSettings settings) {
initializeWorldData(access, true);
LevelGenUtil.initializeWorldData(settings);
public static void initializePatchData() {
getMigrationProfile().markApplied();
WorldDataAPI.saveFile(BCLib.MOD_ID);
}
/**
* Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the
* current versions of the plugins.
*
* @param levelBaseDir Folder of the world
* @param newWorld {@code true} if this is a fresh world
*/
public static void initializeWorldData(File levelBaseDir, boolean newWorld) {
WorldDataAPI.load(new File(levelBaseDir, "data"));
if (newWorld) {
getMigrationProfile().markApplied();
WorldDataAPI.saveFile(BCLib.MOD_ID);
}
}
@Environment(EnvType.CLIENT)
private static AtomicProgressListener showProgressScreen() {

View file

@ -104,9 +104,6 @@ public class BCLChunkGenerator extends NoiseBasedChunkGenerator {
}
}
public static void injectNoiseSettings(WorldGenSettings settings) {
BCLChunkGenerator.injectNoiseSettings(settings, BCLChunkGenerator.ALL_DIMENSIONS);
}
public static void injectNoiseSettings(
ResourceKey<LevelStem> dimensionKey,
@ -127,7 +124,6 @@ public class BCLChunkGenerator extends NoiseBasedChunkGenerator {
}
public static final Predicate<ResourceKey<LevelStem>> NON_MANAGED_DIMENSIONS = dim -> dim != LevelStem.NETHER && dim != LevelStem.END;
public static final Predicate<ResourceKey<LevelStem>> ALL_DIMENSIONS = dim -> true;
public static void injectNoiseSettings(WorldGenSettings settings, Predicate<ResourceKey<LevelStem>> filter) {
List<ResourceKey<LevelStem>> otherDimensions = settings

View file

@ -16,6 +16,7 @@ import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.BiomeTags;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.Climate;
@ -147,13 +148,13 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource {
private static boolean isValidNetherBiome(Holder<Biome> biome, ResourceLocation location) {
return biome.unwrapKey().get().location().toString().equals("betternether:crimson_glowing_woods")
|| biome.unwrapKey().get().location().toString().equals("minecraft:crimson_forest")
|| biome.unwrapKey().get().location().toString().equals("betternether:crimson_pinewood");
// return biome.unwrapKey().get().location().toString().equals("betternether:crimson_glowing_woods")
// || biome.unwrapKey().get().location().toString().equals("minecraft:crimson_forest")
// || biome.unwrapKey().get().location().toString().equals("betternether:crimson_pinewood");
// return NetherBiomes.canGenerateInNether(biome.unwrapKey().get()) ||
// biome.is(BiomeTags.IS_NETHER) ||
// BiomeAPI.wasRegisteredAsNetherBiome(location);
return NetherBiomes.canGenerateInNether(biome.unwrapKey().get()) ||
biome.is(BiomeTags.IS_NETHER) ||
BiomeAPI.wasRegisteredAsNetherBiome(location);
}
private static boolean isValidNonVanillaNetherBiome(Holder<Biome> biome, ResourceLocation location) {

View file

@ -6,7 +6,7 @@ import org.betterx.bclib.api.v2.generator.BCLBiomeSource;
import org.betterx.bclib.api.v2.generator.BCLChunkGenerator;
import org.betterx.bclib.api.v2.generator.BCLibEndBiomeSource;
import org.betterx.bclib.api.v2.generator.BCLibNetherBiomeSource;
import org.betterx.bclib.mixin.common.RegistryOpsAccessor;
import org.betterx.bclib.presets.worldgen.BCLWorldPreset;
import org.betterx.bclib.presets.worldgen.BCLWorldPresetSettings;
import org.betterx.bclib.presets.worldgen.BCLWorldPresets;
import org.betterx.bclib.presets.worldgen.WorldPresetSettings;
@ -43,7 +43,7 @@ import org.jetbrains.annotations.NotNull;
public class LevelGenUtil {
private static final String TAG_VERSION = "version";
private static final String TAG_BN_GEN_VERSION = "generator_version";
private static final String TAG_GENERATOR = "generator";
public static final String TAG_GENERATOR = "generator";
@NotNull
public static LevelStem getBCLNetherLevelStem(Context context, Optional<Integer> version) {
@ -81,27 +81,6 @@ public class LevelGenUtil {
return getBCLEndLevelStem(context, endSource);
}
/**
* Datapacks can change the world's generator. This Method will ensure, that the Generators contain
* the correct BiomeSources for this world
*
* @param settings
* @return
* @see BCLChunkGenerator#injectNoiseSettings(WorldGenSettings) for the correcponding behaviour
* for new worlds
*/
public static WorldGenSettings fixSettingsInCurrentWorld(
Optional<RegistryOps<Tag>> registryOps,
WorldGenSettings settings
) {
if (registryOps.orElse(null) instanceof RegistryOpsAccessor acc) {
return getWorldSettings().repairSettingsOnLoad(acc.bcl_getRegistryAccess(), settings);
} else {
BCLib.LOGGER.error("Unable to obtain registryAccess when enforcing generators.");
}
return settings;
}
public static WorldGenSettings createWorldFromPreset(
ResourceKey<WorldPreset> preset,
RegistryAccess registryAccess,
@ -325,27 +304,6 @@ public class LevelGenUtil {
generatorSettings.putInt(key.location().toString(), getDimensionVersion(settings, key));
}
public static void initializeWorldData(WorldGenSettings settings) {
updateWorldData(getDimensionVersion(settings, LevelStem.NETHER), getDimensionVersion(settings, LevelStem.END));
}
public static void updateWorldData(int netherVersion, int endVersion) {
BCLWorldPresetSettings worldSettings = new BCLWorldPresetSettings(netherVersion, endVersion);
final RegistryAccess registryAccess = RegistryAccess.builtinCopy();
final RegistryOps<Tag> registryOps = RegistryOps.create(NbtOps.INSTANCE, registryAccess);
final var codec = WorldPresetSettings.CODEC.orElse(worldSettings);
final var encodeResult = codec.encodeStart(registryOps, worldSettings);
if (encodeResult.result().isPresent()) {
final CompoundTag settingsNbt = WorldDataAPI.getRootTag(BCLib.TOGETHER_WORLDS);
settingsNbt.put(TAG_GENERATOR, encodeResult.result().get());
} else {
BCLib.LOGGER.error("Unable to encode world generator settings generator for level.dat.");
}
WorldDataAPI.saveFile(BCLib.TOGETHER_WORLDS);
}
static CompoundTag getSettingsNbt() {
return WorldDataAPI.getCompoundTag(BCLib.TOGETHER_WORLDS, TAG_GENERATOR);
}
@ -390,7 +348,7 @@ public class LevelGenUtil {
}
BCLib.LOGGER.info("Set world to BiomeSource Version " + biomeSourceVersion);
updateWorldData(biomeSourceVersion, biomeSourceVersion);
BCLWorldPreset.writeWorldPresetSettings(new BCLWorldPresetSettings(biomeSourceVersion, biomeSourceVersion));
}
}

View file

@ -5,11 +5,14 @@ import org.betterx.bclib.interfaces.BiomeSourceAccessor;
import org.betterx.bclib.interfaces.NoiseGeneratorSettingsProvider;
import org.betterx.bclib.interfaces.SurfaceRuleProvider;
import org.betterx.bclib.mixin.common.BiomeGenerationSettingsAccessor;
import org.betterx.bclib.mixin.common.RegistryOpsAccessor;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
@ -65,6 +68,21 @@ public class InternalBiomeAPI {
})));
}
public static RegistryAccess worldRegistryAccess() {
return registryAccess;
}
/**
* Initialize registry for current server.
*
* @param regOps - registryOps for the current Session.
*/
public static void initRegistry(RegistryOps<Tag> regOps) {
if (regOps instanceof RegistryOpsAccessor acc) {
initRegistry(acc.bcl_getRegistryAccess());
}
}
/**
* Initialize registry for current server.
*
@ -159,12 +177,7 @@ public class InternalBiomeAPI {
}
}
}
/**
* Will apply biome modifications to world, internal usage only.
*
* @param level
*/
@Deprecated(forRemoval = true)
public static void applyModificationsDeprecated(ServerLevel level) {
//TODO: Now Disabled, because we fix the settings when everything gets loaded

View file

@ -0,0 +1,10 @@
package org.betterx.bclib.interfaces;
import net.minecraft.core.Holder;
import net.minecraft.world.level.levelgen.presets.WorldPreset;
import java.util.Optional;
public interface WorldGenSettingsComponentAccessor {
Optional<Holder<WorldPreset>> bcl_getPreset();
}

View file

@ -1,9 +1,9 @@
package org.betterx.bclib.mixin.client;
import org.betterx.bclib.api.v2.LifeCycleAPI;
import org.betterx.bclib.api.v2.levelgen.LevelGenUtil;
import org.betterx.bclib.api.v2.levelgen.biomes.InternalBiomeAPI;
import org.betterx.bclib.presets.worldgen.BCLWorldPresets;
import org.betterx.bclib.presets.worldgen.WorldBootstrap;
import com.mojang.datafixers.util.Pair;
import net.minecraft.client.gui.screens.Screen;
@ -62,6 +62,7 @@ public class CreateWorldScreenMixin {
//this is called when a new world is first created
@Inject(method = "createNewWorldDirectory", at = @At("RETURN"))
void bcl_createNewWorld(CallbackInfoReturnable<Optional<LevelStorageSource.LevelStorageAccess>> cir) {
LifeCycleAPI.worldCreationStarted(cir.getReturnValue(), this.worldGenSettingsComponent);
WorldBootstrap.InGUI.registryReady(this.worldGenSettingsComponent);
WorldBootstrap.InGUI.setupNewWorld(cir.getReturnValue(), this.worldGenSettingsComponent);
}
}

View file

@ -1,20 +1,28 @@
package org.betterx.bclib.mixin.client;
import org.betterx.bclib.interfaces.WorldGenSettingsComponentAccessor;
import net.minecraft.client.gui.screens.worldselection.WorldGenSettingsComponent;
import net.minecraft.core.Holder;
import net.minecraft.world.level.levelgen.presets.WorldPreset;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@Mixin(WorldGenSettingsComponent.class)
public class WorldGenSettingsComponentMixin {
public abstract class WorldGenSettingsComponentMixin implements WorldGenSettingsComponentAccessor {
@Override
@Accessor("preset")
public abstract Optional<Holder<WorldPreset>> bcl_getPreset();
@ModifyArg(method = "init", index = 0, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/components/CycleButton$Builder;withValues(Ljava/util/List;Ljava/util/List;)Lnet/minecraft/client/gui/components/CycleButton$Builder;"))
public List<Holder<WorldPreset>> bcl_SortLists(List<Holder<WorldPreset>> list) {
final Predicate<Holder<WorldPreset>> vanilla = (p -> p.unwrapKey()

View file

@ -1,6 +1,6 @@
package org.betterx.bclib.mixin.common;
import org.betterx.bclib.api.v2.LifeCycleAPI;
import org.betterx.bclib.presets.worldgen.WorldBootstrap;
import com.mojang.serialization.DynamicOps;
import net.minecraft.nbt.Tag;
@ -17,7 +17,7 @@ import org.spongepowered.asm.mixin.injection.ModifyVariable;
abstract public class MainMixin {
@ModifyVariable(method = "main", ordinal = 0, at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;getSummary()Lnet/minecraft/world/level/storage/LevelSummary;"))
private static LevelStorageSource.LevelStorageAccess bc_createAccess(LevelStorageSource.LevelStorageAccess levelStorageAccess) {
LifeCycleAPI.newWorldSetup(levelStorageAccess);
WorldBootstrap.DedicatedServer.setupWorld(levelStorageAccess);
return levelStorageAccess;
}
@ -25,7 +25,7 @@ abstract public class MainMixin {
@ModifyArg(method = "method_43613", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;getDataTag(Lcom/mojang/serialization/DynamicOps;Lnet/minecraft/world/level/DataPackConfig;Lcom/mojang/serialization/Lifecycle;)Lnet/minecraft/world/level/storage/WorldData;"))
private static DynamicOps<Tag> bcl_onCreate(DynamicOps<Tag> dynamicOps) {
if (dynamicOps instanceof RegistryOps<Tag> regOps) {
LifeCycleAPI.worldCreationStarted(regOps);
WorldBootstrap.DedicatedServer.registryReady(regOps);
}
return dynamicOps;
}

View file

@ -1,7 +1,6 @@
package org.betterx.bclib.mixin.common;
import org.betterx.bclib.api.v2.LifeCycleAPI;
import org.betterx.bclib.api.v2.generator.BCLChunkGenerator;
import org.betterx.bclib.presets.worldgen.WorldBootstrap;
import com.mojang.datafixers.DataFixer;
import com.mojang.serialization.Dynamic;
@ -36,8 +35,7 @@ public class PrimaryLevelDataMixin {
//This is the way a created (new) world is initializing the PrimaryLevelData
@ModifyArg(method = "<init>(Lnet/minecraft/world/level/LevelSettings;Lnet/minecraft/world/level/levelgen/WorldGenSettings;Lcom/mojang/serialization/Lifecycle;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/storage/PrimaryLevelData;<init>(Lcom/mojang/datafixers/DataFixer;ILnet/minecraft/nbt/CompoundTag;ZIIIFJJIIIZIZZZLnet/minecraft/world/level/border/WorldBorder$Settings;IILjava/util/UUID;Ljava/util/Set;Lnet/minecraft/world/level/timers/TimerQueue;Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/world/level/LevelSettings;Lnet/minecraft/world/level/levelgen/WorldGenSettings;Lcom/mojang/serialization/Lifecycle;)V"))
private static WorldGenSettings bcl_fixOtherSettings(WorldGenSettings worldGenSettings) {
BCLChunkGenerator.injectNoiseSettings(worldGenSettings);
return worldGenSettings;
return WorldBootstrap.enforceInNewWorld(worldGenSettings);
}
@Inject(method = "parse", at = @At("HEAD"))
@ -62,7 +60,8 @@ public class PrimaryLevelDataMixin {
@ModifyArg(method = "parse", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/storage/PrimaryLevelData;<init>(Lcom/mojang/datafixers/DataFixer;ILnet/minecraft/nbt/CompoundTag;ZIIIFJJIIIZIZZZLnet/minecraft/world/level/border/WorldBorder$Settings;IILjava/util/UUID;Ljava/util/Set;Lnet/minecraft/world/level/timers/TimerQueue;Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/nbt/CompoundTag;Lnet/minecraft/world/level/LevelSettings;Lnet/minecraft/world/level/levelgen/WorldGenSettings;Lcom/mojang/serialization/Lifecycle;)V"))
private static WorldGenSettings bcl_fixSettings(WorldGenSettings settings) {
Optional<RegistryOps<Tag>> registryOps = bcl_lastRegistryAccess.get();
settings = LifeCycleAPI.worldLoadStarted(settings, registryOps);
WorldBootstrap.InGUI.registryReady(registryOps);
settings = WorldBootstrap.enforceInLoadedWorld(registryOps, settings);
bcl_lastRegistryAccess.set(Optional.empty());
return settings;
}

View file

@ -3,7 +3,6 @@ package org.betterx.bclib.mixin.common;
import org.betterx.bclib.api.v2.LifeCycleAPI;
import org.betterx.bclib.api.v2.generator.BCLBiomeSource;
import org.betterx.bclib.api.v2.generator.BCLibNetherBiomeSource;
import org.betterx.bclib.api.v2.levelgen.biomes.InternalBiomeAPI;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceKey;
@ -75,8 +74,6 @@ public abstract class ServerLevelMixin extends Level {
bl2
);
InternalBiomeAPI.applyModificationsDeprecated(ServerLevel.class.cast(this));
if (level.dimension() == Level.NETHER) {
BCLibNetherBiomeSource.setWorldHeight(level.getChunkSource().getGenerator().getGenDepth());
}

View file

@ -1,10 +1,9 @@
package org.betterx.bclib.mixin.common;
import org.betterx.bclib.api.v2.LifeCycleAPI;
import org.betterx.bclib.api.v2.dataexchange.DataExchangeAPI;
import org.betterx.bclib.api.v2.datafixer.DataFixerAPI;
import org.betterx.bclib.api.v2.levelgen.biomes.InternalBiomeAPI;
import org.betterx.bclib.config.Configs;
import org.betterx.bclib.presets.worldgen.WorldBootstrap;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.worldselection.WorldOpenFlows;
@ -22,6 +21,8 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Optional;
@Mixin(WorldOpenFlows.class)
public abstract class WorldOpenFlowsMixin {
@ -34,8 +35,7 @@ public abstract class WorldOpenFlowsMixin {
@Inject(method = "loadLevel", cancellable = true, at = @At("HEAD"))
private void bcl_callFixerOnLoad(Screen screen, String levelID, CallbackInfo ci) {
DataExchangeAPI.prepareServerside();
InternalBiomeAPI.prepareNewLevel();
WorldBootstrap.InGUI.setupLoadedWorld(levelID, this.levelSource);
if (DataFixerAPI.fixData(this.levelSource, levelID, true, (appliedFixes) -> {
LifeCycleAPI._runBeforeLevelLoad();
@ -61,7 +61,7 @@ public abstract class WorldOpenFlowsMixin {
WorldGenSettings worldGenSettings,
CallbackInfo ci
) {
LifeCycleAPI.newWorldSetup(levelID, worldGenSettings, this.levelSource);
WorldBootstrap.InFreshLevel.setupNewWorld(levelID, worldGenSettings, this.levelSource, Optional.empty());
}
@Inject(method = "createLevelFromExistingSettings", at = @At("HEAD"))

View file

@ -0,0 +1,60 @@
package org.betterx.bclib.mixin.common;
import org.betterx.bclib.presets.worldgen.BCLWorldPreset;
import org.betterx.bclib.presets.worldgen.WorldPresetSettings;
import com.mojang.datafixers.kinds.App;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.presets.WorldPreset;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
@Mixin(WorldPreset.class)
public class WorldPresetMixin {
@ModifyArg(method = "<clinit>", at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/codecs/RecordCodecBuilder;create(Ljava/util/function/Function;)Lcom/mojang/serialization/Codec;"))
private static Function<RecordCodecBuilder.Instance<WorldPreset>, ? extends App<RecordCodecBuilder.Mu<WorldPreset>, WorldPreset>> foo(
Function<RecordCodecBuilder.Instance<WorldPreset>, ? extends App<RecordCodecBuilder.Mu<WorldPreset>, WorldPreset>> builder
) {
final Function<RecordCodecBuilder.Instance<WorldPreset>, App<RecordCodecBuilder.Mu<WorldPreset>, WorldPreset>> CODEC_FUNCTION = builderInstance -> {
RecordCodecBuilder<WorldPreset, Map<ResourceKey<LevelStem>, LevelStem>> dimensionsBuilder = Codec
.unboundedMap(
ResourceKey.codec(Registry.LEVEL_STEM_REGISTRY),
LevelStem.CODEC
)
.fieldOf("dimensions")
.forGetter((wp) -> (wp instanceof WorldPresetAccessor)
? ((WorldPresetAccessor) wp).bcl_getDimensions()
: null);
RecordCodecBuilder<WorldPreset, Optional<Integer>> sortBuilder = Codec.INT
.optionalFieldOf("sort_order")
.forGetter(wp -> (wp instanceof BCLWorldPreset)
? Optional.of(((BCLWorldPreset) wp).sortOrder)
: Optional.empty());
RecordCodecBuilder<WorldPreset, Optional<WorldPresetSettings>> settingsBuilder = WorldPresetSettings.CODEC
.optionalFieldOf("settings")
.forGetter(wp -> (wp instanceof BCLWorldPreset)
? Optional.of(((BCLWorldPreset) wp).settings)
: Optional.empty());
return builderInstance
.group(dimensionsBuilder, sortBuilder, settingsBuilder)
.apply(builderInstance, BCLWorldPreset::new);
};
return CODEC_FUNCTION;
}
}

View file

@ -1,44 +1,38 @@
package org.betterx.bclib.presets.worldgen;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.api.v2.WorldDataAPI;
import org.betterx.bclib.api.v2.levelgen.LevelGenUtil;
import org.betterx.bclib.mixin.common.WorldPresetAccessor;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.resources.RegistryFileCodec;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.presets.WorldPreset;
import java.util.Map;
import java.util.Optional;
public class BCLWorldPreset extends WorldPreset {
public final WorldPresetSettings settings;
public final int sortOrder;
public static final Codec<BCLWorldPreset> DIRECT_CODEC = RecordCodecBuilder.create(builderInstance -> {
RecordCodecBuilder<BCLWorldPreset, Map<ResourceKey<LevelStem>, LevelStem>> dimensionsBuidler = Codec
.unboundedMap(ResourceKey.codec(Registry.LEVEL_STEM_REGISTRY), LevelStem.CODEC)
.fieldOf("dimensions")
.forGetter(worldPreset -> worldPreset.getDimensions());
RecordCodecBuilder<BCLWorldPreset, Integer> sortBuilder = Codec.INT
.fieldOf("sort_order")
.forGetter(wp -> wp.sortOrder);
private static final String TAG_GENERATOR = LevelGenUtil.TAG_GENERATOR;
RecordCodecBuilder<BCLWorldPreset, WorldPresetSettings> settingsBuilder = WorldPresetSettings.CODEC
.fieldOf("settings")
.forGetter(wp -> wp.settings);
private static int NEXT_IN_SORT_ORDER = 1000;
return builderInstance
.group(dimensionsBuidler, sortBuilder, settingsBuilder)
.apply(builderInstance, BCLWorldPreset::new);
});
public static final Codec<Holder<WorldPreset>> CODEC = RegistryFileCodec.create(
Registry.WORLD_PRESET_REGISTRY,
(Codec<WorldPreset>) ((Object) DIRECT_CODEC)
);
public BCLWorldPreset(
Map<ResourceKey<LevelStem>, LevelStem> map,
Optional<Integer> sortOrder,
Optional<WorldPresetSettings> settings
) {
this(map, sortOrder.orElse(NEXT_IN_SORT_ORDER++), settings.orElse(VanillaWorldPresetSettings.DEFAULT));
}
public BCLWorldPreset(Map<ResourceKey<LevelStem>, LevelStem> map, int sortOrder, WorldPresetSettings settings) {
super(map);
@ -50,4 +44,28 @@ public class BCLWorldPreset extends WorldPreset {
return ((WorldPresetAccessor) this).bcl_getDimensions();
}
public static WorldPresetSettings writeWorldPresetSettings(Optional<Holder<WorldPreset>> worldPreset) {
if (worldPreset.isPresent() && worldPreset.get().value() instanceof BCLWorldPreset wp) {
writeWorldPresetSettings(wp.settings);
return wp.settings;
} else {
writeWorldPresetSettings(VanillaWorldPresetSettings.DEFAULT);
return VanillaWorldPresetSettings.DEFAULT;
}
}
public static void writeWorldPresetSettings(WorldPresetSettings presetSettings) {
final RegistryOps<Tag> registryOps = RegistryOps.create(NbtOps.INSTANCE, BuiltinRegistries.ACCESS);
final var codec = WorldPresetSettings.CODEC.orElse(presetSettings);
final var encodeResult = codec.encodeStart(registryOps, presetSettings);
if (encodeResult.result().isPresent()) {
final CompoundTag settingsNbt = WorldDataAPI.getRootTag(BCLib.TOGETHER_WORLDS);
settingsNbt.put(TAG_GENERATOR, encodeResult.result().get());
} else {
BCLib.LOGGER.error("Unable to encode world generator settings generator for level.dat.");
}
WorldDataAPI.saveFile(BCLib.TOGETHER_WORLDS);
}
}

View file

@ -0,0 +1,30 @@
package org.betterx.bclib.presets.worldgen;
import com.mojang.serialization.Codec;
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.levelgen.WorldGenSettings;
import java.util.Set;
public class VanillaWorldPresetSettings extends WorldPresetSettings {
public static final VanillaWorldPresetSettings DEFAULT = new VanillaWorldPresetSettings();
public static final Codec<VanillaWorldPresetSettings> CODEC = Codec.unit(VanillaWorldPresetSettings::new);
@Override
public Codec<? extends WorldPresetSettings> codec() {
return CODEC;
}
@Override
public WorldGenSettings repairSettingsOnLoad(RegistryAccess registryAccess, WorldGenSettings settings) {
return settings;
}
@Override
public BiomeSource fixBiomeSource(BiomeSource biomeSource, Set<Holder<Biome>> datapackBiomes) {
return biomeSource;
}
}

View file

@ -0,0 +1,162 @@
package org.betterx.bclib.presets.worldgen;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.api.v2.LifeCycleAPI;
import org.betterx.bclib.api.v2.WorldDataAPI;
import org.betterx.bclib.api.v2.dataexchange.DataExchangeAPI;
import org.betterx.bclib.api.v2.datafixer.DataFixerAPI;
import org.betterx.bclib.api.v2.levelgen.LevelGenUtil;
import org.betterx.bclib.api.v2.levelgen.biomes.InternalBiomeAPI;
import org.betterx.bclib.interfaces.WorldGenSettingsComponentAccessor;
import org.betterx.bclib.mixin.common.RegistryOpsAccessor;
import net.minecraft.client.gui.screens.worldselection.WorldGenSettingsComponent;
import net.minecraft.core.Holder;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.RegistryOps;
import net.minecraft.world.level.levelgen.WorldGenSettings;
import net.minecraft.world.level.levelgen.presets.WorldPreset;
import net.minecraft.world.level.storage.LevelResource;
import net.minecraft.world.level.storage.LevelStorageSource;
import java.io.File;
import java.util.Optional;
public class WorldBootstrap {
private static class Helpers {
private static void initializeWorldDataAPI(
LevelStorageSource.LevelStorageAccess levelStorageAccess,
boolean newWorld
) {
File levelPath = levelStorageAccess.getLevelPath(LevelResource.ROOT).toFile();
initializeWorldDataAPI(levelPath, newWorld);
}
private static void setupWorld() {
InternalBiomeAPI.prepareNewLevel();
DataExchangeAPI.prepareServerside();
}
private static void initializeWorldDataAPI(File levelBaseDir, boolean newWorld) {
WorldDataAPI.load(new File(levelBaseDir, "data"));
if (newWorld) {
WorldDataAPI.saveFile(BCLib.MOD_ID);
}
}
}
public static class DedicatedServer {
public static void registryReady(RegistryOps<Tag> regOps) {
InternalBiomeAPI.initRegistry(regOps);
}
public static void setupWorld(LevelStorageSource.LevelStorageAccess levelStorageAccess) {
Helpers.setupWorld();
File levelDat = levelStorageAccess.getLevelPath(LevelResource.LEVEL_DATA_FILE).toFile();
if (!levelDat.exists()) {
BCLib.LOGGER.info("Creating a new World, no fixes needed");
Helpers.initializeWorldDataAPI(levelStorageAccess, true);
BCLWorldPreset.writeWorldPresetSettings(Optional.empty());
DataFixerAPI.initializePatchData();
} else {
Helpers.initializeWorldDataAPI(levelStorageAccess, false);
DataFixerAPI.fixData(levelStorageAccess, false, (didFix) -> {/* not called when showUI==false */});
}
LifeCycleAPI._runBeforeLevelLoad();
}
}
public static class InGUI {
public static void registryReady(WorldGenSettingsComponent worldGenSettingsComponent) {
InternalBiomeAPI.initRegistry(worldGenSettingsComponent.registryHolder());
}
public static void registryReady(Optional<RegistryOps<Tag>> registryOps) {
if (registryOps.orElse(null) instanceof RegistryOpsAccessor acc) {
InternalBiomeAPI.initRegistry(acc.bcl_getRegistryAccess());
}
}
public static void setupNewWorld(
Optional<LevelStorageSource.LevelStorageAccess> levelStorageAccess,
WorldGenSettingsComponent worldGenSettingsComponent
) {
if (levelStorageAccess.isPresent()) {
Helpers.setupWorld();
Helpers.initializeWorldDataAPI(levelStorageAccess.get(), true);
if (worldGenSettingsComponent instanceof WorldGenSettingsComponentAccessor acc) {
BCLWorldPreset.writeWorldPresetSettings(acc.bcl_getPreset());
}
DataFixerAPI.initializePatchData();
// DataFixerAPI.createWorldData(
// levelStorageAccess.get(),
// worldGenSettingsComponent.settings().worldGenSettings()
// );
LifeCycleAPI._runBeforeLevelLoad();
}
}
/**
* Does not call {@link LifeCycleAPI#_runBeforeLevelLoad()}
*/
public static void setupLoadedWorld(
String levelID,
LevelStorageSource levelSource
) {
Helpers.setupWorld();
try {
var levelStorageAccess = levelSource.createAccess(levelID);
Helpers.initializeWorldDataAPI(levelStorageAccess, true);
levelStorageAccess.close();
} catch (Exception e) {
BCLib.LOGGER.error("Failed to initialize data in world", e);
}
}
}
public static class InFreshLevel {
public static void setupNewWorld(
String levelID,
WorldGenSettings worldGenSettings,
LevelStorageSource levelSource,
Optional<Holder<WorldPreset>> worldPreset
) {
InGUI.setupLoadedWorld(levelID, levelSource);
BCLWorldPreset.writeWorldPresetSettings(worldPreset);
DataFixerAPI.initializePatchData();
LifeCycleAPI._runBeforeLevelLoad();
}
}
public static WorldGenSettings enforceInNewWorld(WorldGenSettings worldGenSettings) {
worldGenSettings = LevelGenUtil
.getWorldSettings()
.repairSettingsOnLoad(InternalBiomeAPI.worldRegistryAccess(), worldGenSettings);
return worldGenSettings;
}
public static WorldGenSettings enforceInLoadedWorld(
Optional<RegistryOps<Tag>> registryOps,
WorldGenSettings worldGenSettings
) {
if (registryOps.orElse(null) instanceof RegistryOpsAccessor acc) {
return LevelGenUtil
.getWorldSettings()
.repairSettingsOnLoad(acc.bcl_getRegistryAccess(), worldGenSettings);
//.repairSettingsOnLoad(InternalBiomeAPI.worldRegistryAccess(), worldGenSettings);
} else {
BCLib.LOGGER.error("Unable to obtain registryAccess when enforcing generators.");
}
return worldGenSettings;
}
}

View file

@ -47,6 +47,7 @@ public abstract class WorldPresetSettings {
public static void bootstrap() {
register(BCLib.makeID("bcl_world_preset_settings"), BCLWorldPresetSettings.CODEC);
register(BCLib.makeID("vanilla_world_preset_settings"), VanillaWorldPresetSettings.CODEC);
}
public abstract Codec<? extends WorldPresetSettings> codec();

View file

@ -46,6 +46,8 @@
"WorldGenPropertiesMixin",
"WorldGenRegionMixin",
"WorldOpenFlowsMixin",
"WorldPresetAccessor",
"WorldPresetMixin",
"WorldPresetsBootstrapMixin",
"elytra.LivingEntityMixin",
"shears.BeehiveBlockMixin",