[Change] Separated all World-Load related code to a new namespace
This commit is contained in:
parent
67d09676c4
commit
25fa53541f
79 changed files with 1924 additions and 814 deletions
|
@ -0,0 +1,31 @@
|
|||
package org.betterx.worlds.together;
|
||||
|
||||
import org.betterx.bclib.util.Logger;
|
||||
import org.betterx.worlds.together.world.WorldConfig;
|
||||
import org.betterx.worlds.together.worldPreset.WorldPresets;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
public class WorldsTogether {
|
||||
public static boolean SURPRESS_EXPERIMENTAL_DIALOG = false;
|
||||
public static final String MOD_ID = "worlds_together";
|
||||
public static final Logger LOGGER = new Logger(MOD_ID);
|
||||
public static final boolean RUNS_TERRABLENDER = FabricLoader.getInstance()
|
||||
.getModContainer("terrablender")
|
||||
.isPresent();
|
||||
|
||||
public static boolean isDevEnvironment() {
|
||||
return FabricLoader.getInstance().isDevelopmentEnvironment();
|
||||
}
|
||||
|
||||
public static void onInitialize() {
|
||||
WorldConfig.registerModCache(WorldsTogether.MOD_ID);
|
||||
WorldPresets.ensureStaticallyLoaded();
|
||||
}
|
||||
|
||||
public static ResourceLocation makeID(String s) {
|
||||
return new ResourceLocation(MOD_ID, s);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.betterx.worlds.together.client;
|
||||
|
||||
import org.betterx.worlds.together.worldPreset.client.WorldPresetsClient;
|
||||
|
||||
public class WorldsTogetherClient {
|
||||
public static void onInitializeClient() {
|
||||
WorldPresetsClient.setupClientside();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package org.betterx.worlds.together.flatLevel;
|
||||
|
||||
import org.betterx.bclib.api.v2.tag.TagAPI;
|
||||
import org.betterx.bclib.api.v2.tag.TagType;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.FlatLevelGeneratorPresetTags;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorPreset;
|
||||
|
||||
public class FlatLevelPresets {
|
||||
public static TagType.Simple<FlatLevelGeneratorPreset> FLAT_LEVEL_PRESETS =
|
||||
TagAPI.registerType(
|
||||
Registry.FLAT_LEVEL_GENERATOR_PRESET_REGISTRY,
|
||||
"tags/worldgen/flat_level_generator_preset",
|
||||
(b) -> null
|
||||
);
|
||||
|
||||
|
||||
public static ResourceKey<FlatLevelGeneratorPreset> register(ResourceLocation loc) {
|
||||
ResourceKey<FlatLevelGeneratorPreset> key = ResourceKey.create(
|
||||
Registry.FLAT_LEVEL_GENERATOR_PRESET_REGISTRY,
|
||||
loc
|
||||
);
|
||||
FLAT_LEVEL_PRESETS.addUntyped(FlatLevelGeneratorPresetTags.VISIBLE, key.location());
|
||||
return key;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package org.betterx.worlds.together.mixin.client;
|
||||
|
||||
import org.betterx.worlds.together.world.WorldGenUtil;
|
||||
import org.betterx.worlds.together.world.event.WorldBootstrap;
|
||||
import org.betterx.worlds.together.worldPreset.WorldPresets;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen;
|
||||
import net.minecraft.client.gui.screens.worldselection.WorldGenSettingsComponent;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.WorldLoader;
|
||||
import net.minecraft.world.level.DataPackConfig;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.levelgen.presets.WorldPreset;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(CreateWorldScreen.class)
|
||||
public class CreateWorldScreenMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
public WorldGenSettingsComponent worldGenSettingsComponent;
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void bcl_init(
|
||||
Screen screen,
|
||||
DataPackConfig dataPackConfig,
|
||||
WorldGenSettingsComponent worldGenSettingsComponent,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
WorldBootstrap.InGUI.registryReadyOnNewWorld(worldGenSettingsComponent);
|
||||
}
|
||||
|
||||
//Change the WorldPreset that is selected by default on the Create World Screen
|
||||
@ModifyArg(method = "openFresh", index = 1, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/worldselection/WorldGenSettingsComponent;<init>(Lnet/minecraft/client/gui/screens/worldselection/WorldCreationContext;Ljava/util/Optional;Ljava/util/OptionalLong;)V"))
|
||||
private static Optional<ResourceKey<WorldPreset>> bcl_NewDefault(Optional<ResourceKey<WorldPreset>> preset) {
|
||||
return WorldPresets.DEFAULT;
|
||||
}
|
||||
|
||||
//Make sure the WorldGenSettings used to populate the create screen match the default WorldPreset
|
||||
@ModifyArg(method = "openFresh", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/WorldLoader;load(Lnet/minecraft/server/WorldLoader$InitConfig;Lnet/minecraft/server/WorldLoader$WorldDataSupplier;Lnet/minecraft/server/WorldLoader$ResultFactory;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
||||
private static WorldLoader.WorldDataSupplier<WorldGenSettings> bcl_NewDefaultSettings(WorldLoader.WorldDataSupplier<WorldGenSettings> worldDataSupplier) {
|
||||
return (resourceManager, dataPackConfig) -> {
|
||||
Pair<WorldGenSettings, RegistryAccess.Frozen> res = worldDataSupplier.get(resourceManager, dataPackConfig);
|
||||
return WorldGenUtil.defaultWorldDataSupplier(res.getSecond());
|
||||
};
|
||||
}
|
||||
|
||||
//this is called when a new world is first created
|
||||
@Inject(method = "createNewWorldDirectory", at = @At("RETURN"))
|
||||
void bcl_createNewWorld(CallbackInfoReturnable<Optional<LevelStorageSource.LevelStorageAccess>> cir) {
|
||||
WorldBootstrap.InGUI.registryReadyOnNewWorld(this.worldGenSettingsComponent);
|
||||
WorldBootstrap.InGUI.setupNewWorld(cir.getReturnValue(), this.worldGenSettingsComponent);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package org.betterx.worlds.together.mixin.client;
|
||||
|
||||
import org.betterx.worlds.together.worldPreset.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 abstract class WorldGenSettingsComponentMixin implements WorldGenSettingsComponentAccessor {
|
||||
@Override
|
||||
@Accessor("preset")
|
||||
public abstract Optional<Holder<WorldPreset>> bcl_getPreset();
|
||||
|
||||
@Override
|
||||
@Accessor("preset")
|
||||
public abstract void bcl_setPreset(Optional<Holder<WorldPreset>> preset);
|
||||
|
||||
@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()
|
||||
.orElseThrow()
|
||||
.location()
|
||||
.getNamespace()
|
||||
.equals("minecraft"));
|
||||
List<Holder<WorldPreset>> custom = list
|
||||
.stream()
|
||||
.filter(p -> !vanilla.test(p))
|
||||
.collect(Collectors.toCollection(LinkedList::new));
|
||||
custom.addAll(list.stream().filter(vanilla).toList());
|
||||
|
||||
return custom;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package org.betterx.worlds.together.mixin.client;
|
||||
|
||||
import org.betterx.worlds.together.WorldsTogether;
|
||||
import org.betterx.worlds.together.world.event.WorldBootstrap;
|
||||
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.gui.screens.worldselection.WorldOpenFlows;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.server.ReloadableServerResources;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.WorldData;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
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 {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private LevelStorageSource levelSource;
|
||||
|
||||
@Shadow
|
||||
protected abstract void doLoadLevel(Screen screen, String levelID, boolean safeMode, boolean canAskForBackup);
|
||||
|
||||
@Inject(method = "loadLevel", cancellable = true, at = @At("HEAD"))
|
||||
private void bcl_callFixerOnLoad(Screen screen, String levelID, CallbackInfo ci) {
|
||||
WorldBootstrap.InGUI.setupLoadedWorld(levelID, this.levelSource);
|
||||
|
||||
//if (DataFixerAPI.fixData(this.levelSource, levelID, true, (appliedFixes) -> {
|
||||
if (WorldBootstrap.InGUI.applyWorldPatches(levelSource, levelID, (appliedFixes) -> {
|
||||
WorldBootstrap.InGUI.finishedWorldLoad(levelID, this.levelSource);
|
||||
this.doLoadLevel(screen, levelID, false, false);
|
||||
})) {
|
||||
//cancel call when fix-screen is presented
|
||||
ci.cancel();
|
||||
} else {
|
||||
WorldBootstrap.InGUI.finishedWorldLoad(levelID, this.levelSource);
|
||||
if (WorldsTogether.SURPRESS_EXPERIMENTAL_DIALOG) {
|
||||
this.doLoadLevel(screen, levelID, false, false);
|
||||
//cancel call as we manually start the level load here
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "createFreshLevel", at = @At("HEAD"))
|
||||
public void bcl_createFreshLevel(
|
||||
String levelID,
|
||||
LevelSettings levelSettings,
|
||||
RegistryAccess registryAccess,
|
||||
WorldGenSettings worldGenSettings,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
WorldBootstrap.InFreshLevel.setupNewWorld(levelID, worldGenSettings, this.levelSource, Optional.empty());
|
||||
}
|
||||
|
||||
@Inject(method = "createLevelFromExistingSettings", at = @At("HEAD"))
|
||||
public void bcl_createLevelFromExistingSettings(
|
||||
LevelStorageSource.LevelStorageAccess levelStorageAccess,
|
||||
ReloadableServerResources reloadableServerResources,
|
||||
RegistryAccess.Frozen frozen,
|
||||
WorldData worldData,
|
||||
CallbackInfo ci
|
||||
) {
|
||||
//called from the CreateWorldScreenMixin now
|
||||
//LifeCycleAPI.newWorldSetup(levelStorageAccess, worldData.worldGenSettings());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package org.betterx.worlds.together.mixin.common;
|
||||
|
||||
import org.betterx.worlds.together.world.event.WorldBootstrap;
|
||||
|
||||
import com.mojang.serialization.DynamicOps;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.resources.RegistryOps;
|
||||
import net.minecraft.server.Main;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
|
||||
@Mixin(Main.class)
|
||||
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) {
|
||||
WorldBootstrap.DedicatedServer.setupWorld(levelStorageAccess);
|
||||
return levelStorageAccess;
|
||||
}
|
||||
|
||||
|
||||
@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) {
|
||||
WorldBootstrap.DedicatedServer.registryReady(regOps);
|
||||
}
|
||||
return dynamicOps;
|
||||
}
|
||||
|
||||
@ModifyArg(method = "method_43613", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/storage/PrimaryLevelData;<init>(Lnet/minecraft/world/level/LevelSettings;Lnet/minecraft/world/level/levelgen/WorldGenSettings;Lcom/mojang/serialization/Lifecycle;)V"))
|
||||
private static WorldGenSettings bcl_onCreateLevelData(WorldGenSettings worldGenSettings) {
|
||||
WorldBootstrap.DedicatedServer.applyDatapackChangesOnNewWorld(worldGenSettings);
|
||||
return worldGenSettings;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package org.betterx.worlds.together.mixin.common;
|
||||
|
||||
import org.betterx.worlds.together.world.event.WorldBootstrap;
|
||||
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.resources.RegistryOps;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.storage.LevelVersion;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Optional;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Mixin(PrimaryLevelData.class)
|
||||
public class PrimaryLevelDataMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private WorldGenSettings worldGenSettings;
|
||||
private static final ThreadLocal<Optional<RegistryOps<Tag>>> bcl_lastRegistryAccess = ThreadLocal.withInitial(
|
||||
() -> Optional.empty());
|
||||
|
||||
//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) {
|
||||
return WorldBootstrap.enforceInNewWorld(worldGenSettings);
|
||||
}
|
||||
|
||||
@Inject(method = "parse", at = @At("HEAD"))
|
||||
private static void bcl_parse(
|
||||
Dynamic<Tag> dynamic,
|
||||
DataFixer dataFixer,
|
||||
int i,
|
||||
@Nullable CompoundTag compoundTag,
|
||||
LevelSettings levelSettings,
|
||||
LevelVersion levelVersion,
|
||||
WorldGenSettings worldGenSettings,
|
||||
Lifecycle lifecycle,
|
||||
CallbackInfoReturnable<PrimaryLevelData> cir
|
||||
) {
|
||||
if (dynamic.getOps() instanceof RegistryOps<Tag> regOps) {
|
||||
bcl_lastRegistryAccess.set(Optional.of(regOps));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//This is the way a loaded (existing) world is initializing the PrimaryLevelData
|
||||
@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();
|
||||
WorldBootstrap.InGUI.registryReadyOnLoadedWorld(registryOps);
|
||||
settings = WorldBootstrap.enforceInLoadedWorld(registryOps, settings);
|
||||
bcl_lastRegistryAccess.set(Optional.empty());
|
||||
return settings;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.betterx.worlds.together.mixin.common;
|
||||
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.RegistryOps;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(RegistryOps.class)
|
||||
public interface RegistryOpsAccessor {
|
||||
@Accessor("registryAccess")
|
||||
RegistryAccess bcl_getRegistryAccess();
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package org.betterx.worlds.together.mixin.common;
|
||||
|
||||
import org.betterx.worlds.together.worldPreset.WorldPresets;
|
||||
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.dedicated.DedicatedServerProperties;
|
||||
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;
|
||||
|
||||
@Mixin(DedicatedServerProperties.WorldGenProperties.class)
|
||||
public class WorldGenPropertiesMixin {
|
||||
@ModifyArg(method = "create", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/presets/WorldPreset;createWorldGenSettings(JZZ)Lnet/minecraft/world/level/levelgen/WorldGenSettings;"))
|
||||
public long bcl_create(long seed) {
|
||||
return seed;
|
||||
}
|
||||
|
||||
//Make sure Servers use our Default World Preset
|
||||
@ModifyArg(method = "create", at = @At(value = "INVOKE", ordinal = 0, target = "Lnet/minecraft/core/Registry;getHolder(Lnet/minecraft/resources/ResourceKey;)Ljava/util/Optional;"))
|
||||
private ResourceKey<WorldPreset> bcl_foo(ResourceKey<WorldPreset> resourceKey) {
|
||||
return WorldPresets.DEFAULT.orElseThrow();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package org.betterx.worlds.together.mixin.common;
|
||||
|
||||
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.gen.Accessor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(WorldPreset.class)
|
||||
public interface WorldPresetAccessor {
|
||||
@Accessor("dimensions")
|
||||
Map<ResourceKey<LevelStem>, LevelStem> bcl_getDimensions();
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package org.betterx.worlds.together.mixin.common;
|
||||
|
||||
import org.betterx.worlds.together.worldPreset.TogetherWorldPreset;
|
||||
import org.betterx.worlds.together.worldPreset.settings.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 TogetherWorldPreset)
|
||||
? Optional.of(((TogetherWorldPreset) wp).sortOrder)
|
||||
: Optional.empty());
|
||||
|
||||
RecordCodecBuilder<WorldPreset, Optional<WorldPresetSettings>> settingsBuilder = WorldPresetSettings.CODEC
|
||||
.optionalFieldOf("settings")
|
||||
.forGetter(wp -> (wp instanceof TogetherWorldPreset)
|
||||
? Optional.of(((TogetherWorldPreset) wp).settings)
|
||||
: Optional.empty());
|
||||
|
||||
return builderInstance
|
||||
.group(dimensionsBuilder, sortBuilder, settingsBuilder)
|
||||
.apply(builderInstance, TogetherWorldPreset::new);
|
||||
};
|
||||
|
||||
return CODEC_FUNCTION;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package org.betterx.worlds.together.mixin.common;
|
||||
|
||||
import org.betterx.worlds.together.world.WorldGenUtil;
|
||||
import org.betterx.worlds.together.worldPreset.WorldPresets;
|
||||
import org.betterx.worlds.together.worldPreset.settings.WorldPresetSettings;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.presets.WorldPreset;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||
import net.minecraft.world.level.levelgen.synth.NormalNoise;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
@Mixin(net.minecraft.world.level.levelgen.presets.WorldPresets.Bootstrap.class)
|
||||
public abstract class WorldPresetsBootstrapMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private Registry<WorldPreset> presets;
|
||||
@Shadow
|
||||
@Final
|
||||
private Registry<Biome> biomes;
|
||||
@Shadow
|
||||
@Final
|
||||
private Registry<StructureSet> structureSets;
|
||||
@Shadow
|
||||
@Final
|
||||
private Registry<NormalNoise.NoiseParameters> noises;
|
||||
@Shadow
|
||||
@Final
|
||||
private Holder<DimensionType> netherDimensionType;
|
||||
@Shadow
|
||||
@Final
|
||||
private Holder<NoiseGeneratorSettings> netherNoiseSettings;
|
||||
@Shadow
|
||||
@Final
|
||||
private Holder<DimensionType> endDimensionType;
|
||||
@Shadow
|
||||
@Final
|
||||
private Holder<NoiseGeneratorSettings> endNoiseSettings;
|
||||
|
||||
//see WorldPresets.register
|
||||
|
||||
@ModifyArg(method = "run", at = @At(value = "INVOKE", ordinal = 0, target = "Lnet/minecraft/world/level/levelgen/presets/WorldPresets$Bootstrap;registerCustomOverworldPreset(Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/world/level/dimension/LevelStem;)Lnet/minecraft/core/Holder;"))
|
||||
private LevelStem bcl_getOverworldStem(LevelStem overworldStem) {
|
||||
WorldPresetSettings.bootstrap();
|
||||
WorldGenUtil.Context netherContext = new WorldGenUtil.Context(
|
||||
this.biomes,
|
||||
this.netherDimensionType,
|
||||
this.structureSets,
|
||||
this.noises,
|
||||
this.netherNoiseSettings
|
||||
);
|
||||
WorldGenUtil.Context endContext = new WorldGenUtil.Context(
|
||||
this.biomes,
|
||||
this.endDimensionType,
|
||||
this.structureSets,
|
||||
this.noises,
|
||||
this.endNoiseSettings
|
||||
);
|
||||
|
||||
WorldPresets.bootstrapPresets(presets, overworldStem, netherContext, endContext);
|
||||
|
||||
return overworldStem;
|
||||
}
|
||||
|
||||
}
|
436
src/main/java/org/betterx/worlds/together/util/ModUtil.java
Normal file
436
src/main/java/org/betterx/worlds/together/util/ModUtil.java
Normal file
|
@ -0,0 +1,436 @@
|
|||
package org.betterx.worlds.together.util;
|
||||
|
||||
import org.betterx.bclib.BCLib;
|
||||
import org.betterx.worlds.together.WorldsTogether;
|
||||
|
||||
import net.fabricmc.loader.api.*;
|
||||
import net.fabricmc.loader.api.metadata.*;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ModUtil {
|
||||
private static Map<String, ModInfo> mods;
|
||||
|
||||
/**
|
||||
* Unloads the cache of available mods created from {@link #getMods()}
|
||||
*/
|
||||
public static void invalidateCachedMods() {
|
||||
mods = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* return a map of all mods that were found in the 'mods'-folder.
|
||||
* <p>
|
||||
* The method will cache the results. You can clear that cache (and free the memory) by
|
||||
* calling {@link #invalidateCachedMods()}
|
||||
* <p>
|
||||
* An error message is printed if a mod fails to load, but the parsing will continue.
|
||||
*
|
||||
* @return A map of all found mods. (key=ModID, value={@link ModInfo})
|
||||
*/
|
||||
public static Map<String, ModInfo> getMods() {
|
||||
if (mods != null) return mods;
|
||||
|
||||
mods = new HashMap<>();
|
||||
PathUtil.fileWalker(PathUtil.MOD_FOLDER.toFile(), false, (ModUtil::accept));
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
private static ModMetadata readJSON(InputStream is, String sourceFile) throws IOException {
|
||||
try (JsonReader reader = new JsonReader(new InputStreamReader(
|
||||
is,
|
||||
StandardCharsets.UTF_8
|
||||
))) {
|
||||
JsonObject data = JsonParser.parseReader(reader)
|
||||
.getAsJsonObject();
|
||||
Version ver;
|
||||
try {
|
||||
ver = SemanticVersion.parse(data.get("version").getAsString());
|
||||
} catch (VersionParsingException e) {
|
||||
WorldsTogether.LOGGER.error("Unable to parse Version in " + sourceFile);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (data.get("id") == null) {
|
||||
WorldsTogether.LOGGER.error("Unable to read ID in " + sourceFile);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (data.get("name") == null) {
|
||||
WorldsTogether.LOGGER.error("Unable to read name in " + sourceFile);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ModMetadata() {
|
||||
@Override
|
||||
public Version getVersion() {
|
||||
return ver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "fabric";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return data.get("id")
|
||||
.getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getProvides() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModEnvironment getEnvironment() {
|
||||
JsonElement env = data.get("environment");
|
||||
if (env == null) {
|
||||
WorldsTogether.LOGGER.warning("No environment specified in " + sourceFile);
|
||||
//return ModEnvironment.UNIVERSAL;
|
||||
}
|
||||
final String environment = env == null ? "" : env.getAsString()
|
||||
.toLowerCase(Locale.ROOT);
|
||||
|
||||
if (environment.isEmpty() || environment.equals("*") || environment.equals("\"*\"") || environment.equals(
|
||||
"common")) {
|
||||
JsonElement entrypoints = data.get("entrypoints");
|
||||
boolean hasClient = true;
|
||||
|
||||
//check if there is an actual client entrypoint
|
||||
if (entrypoints != null && entrypoints.isJsonObject()) {
|
||||
JsonElement client = entrypoints.getAsJsonObject()
|
||||
.get("client");
|
||||
if (client != null && client.isJsonArray()) {
|
||||
hasClient = client.getAsJsonArray()
|
||||
.size() > 0;
|
||||
} else if (client == null || !client.isJsonPrimitive()) {
|
||||
hasClient = false;
|
||||
} else if (!client.getAsJsonPrimitive()
|
||||
.isString()) {
|
||||
hasClient = false;
|
||||
}
|
||||
}
|
||||
|
||||
//if (hasClient == false) return ModEnvironment.SERVER;
|
||||
return ModEnvironment.UNIVERSAL;
|
||||
} else if (environment.equals("client")) {
|
||||
return ModEnvironment.CLIENT;
|
||||
} else if (environment.equals("server")) {
|
||||
return ModEnvironment.SERVER;
|
||||
} else {
|
||||
WorldsTogether.LOGGER.error("Unable to read environment in " + sourceFile);
|
||||
return ModEnvironment.UNIVERSAL;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ModDependency> getDepends() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ModDependency> getRecommends() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ModDependency> getSuggests() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ModDependency> getConflicts() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ModDependency> getBreaks() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public Collection<ModDependency> getDependencies() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return data.get("name")
|
||||
.getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Person> getAuthors() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Person> getContributors() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContactInformation getContact() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getLicense() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getIconPath(int size) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsCustomValue(String key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomValue getCustomValue(String key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, CustomValue> getCustomValues() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsCustomElement(String key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public JsonElement getCustomElement(String key) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ModInfo} or {@code null} if the mod was not found.
|
||||
* <p>
|
||||
* The call will also return null if the mode-Version in the jar-File is not the same
|
||||
* as the version of the loaded Mod.
|
||||
*
|
||||
* @param modID The mod ID to query
|
||||
* @return A {@link ModInfo}-Object for the querried Mod.
|
||||
*/
|
||||
public static ModInfo getModInfo(String modID) {
|
||||
return getModInfo(modID, true);
|
||||
}
|
||||
|
||||
public static ModInfo getModInfo(String modID, boolean matchVersion) {
|
||||
getMods();
|
||||
final ModInfo mi = mods.get(modID);
|
||||
if (mi == null || (matchVersion && !getModVersion(modID).equals(mi.getVersion()))) return null;
|
||||
return mi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Mod Version for the queried Mod
|
||||
*
|
||||
* @param modID The mod ID to query
|
||||
* @return The version of the locally installed Mod
|
||||
*/
|
||||
public static String getModVersion(String modID) {
|
||||
if (modID == WorldsTogether.MOD_ID) modID = BCLib.MOD_ID;
|
||||
|
||||
Optional<ModContainer> optional = FabricLoader.getInstance()
|
||||
.getModContainer(modID);
|
||||
if (optional.isPresent()) {
|
||||
ModContainer modContainer = optional.get();
|
||||
return ModInfo.versionToString(modContainer.getMetadata()
|
||||
.getVersion());
|
||||
|
||||
}
|
||||
|
||||
return getModVersionFromJar(modID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Mod Version for the queried Mod from the Jar-File in the games mod-directory
|
||||
*
|
||||
* @param modID The mod ID to query
|
||||
* @return The version of the locally installed Mod
|
||||
*/
|
||||
public static String getModVersionFromJar(String modID) {
|
||||
final ModInfo mi = getModInfo(modID, false);
|
||||
if (mi != null) return mi.getVersion();
|
||||
|
||||
return "0.0.0";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mod version from string. String should be in format: %d.%d.%d
|
||||
*
|
||||
* @param version - {@link String} mod version.
|
||||
* @return int mod version.
|
||||
*/
|
||||
public static int convertModVersion(String version) {
|
||||
if (version.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
int res = 0;
|
||||
final String semanticVersionPattern = "(\\d+)\\.(\\d+)(\\.(\\d+))?\\D*";
|
||||
final Matcher matcher = Pattern.compile(semanticVersionPattern)
|
||||
.matcher(version);
|
||||
if (matcher.find()) {
|
||||
if (matcher.groupCount() > 0)
|
||||
res = matcher.group(1) == null ? 0 : ((Integer.parseInt(matcher.group(1)) & 0xFF) << 22);
|
||||
if (matcher.groupCount() > 1)
|
||||
res |= matcher.group(2) == null ? 0 : ((Integer.parseInt(matcher.group(2)) & 0xFF) << 14);
|
||||
if (matcher.groupCount() > 3)
|
||||
res |= matcher.group(4) == null ? 0 : Integer.parseInt(matcher.group(4)) & 0x3FFF;
|
||||
}
|
||||
|
||||
return res;
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mod version from integer. String will be in format %d.%d.%d
|
||||
*
|
||||
* @param version - mod version in integer form.
|
||||
* @return {@link String} mod version.
|
||||
*/
|
||||
public static String convertModVersion(int version) {
|
||||
int a = (version >> 22) & 0xFF;
|
||||
int b = (version >> 14) & 0xFF;
|
||||
int c = version & 0x3FFF;
|
||||
return String.format(Locale.ROOT, "%d.%d.%d", a, b, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code true} if the version v1 is larger than v2
|
||||
*
|
||||
* @param v1 A Version string
|
||||
* @param v2 Another Version string
|
||||
* @return v1 > v2
|
||||
*/
|
||||
public static boolean isLargerVersion(String v1, String v2) {
|
||||
return convertModVersion(v1) > convertModVersion(v2);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code true} if the version v1 is larger or equal v2
|
||||
*
|
||||
* @param v1 A Version string
|
||||
* @param v2 Another Version string
|
||||
* @return v1 ≥ v2
|
||||
*/
|
||||
public static boolean isLargerOrEqualVersion(String v1, String v2) {
|
||||
return convertModVersion(v1) >= convertModVersion(v2);
|
||||
}
|
||||
|
||||
private static void accept(Path file) {
|
||||
try {
|
||||
URI uri = URI.create("jar:" + file.toUri());
|
||||
|
||||
FileSystem fs;
|
||||
// boolean doClose = false;
|
||||
try {
|
||||
fs = FileSystems.getFileSystem(uri);
|
||||
} catch (Exception e) {
|
||||
// doClose = true;
|
||||
fs = FileSystems.newFileSystem(file);
|
||||
}
|
||||
if (fs != null) {
|
||||
try {
|
||||
Path modMetaFile = fs.getPath("fabric.mod.json");
|
||||
if (modMetaFile != null) {
|
||||
try (InputStream is = Files.newInputStream(modMetaFile)) {
|
||||
//ModMetadata mc = ModMetadataParser.parseMetadata(is, uri.toString(), new LinkedList<String>());
|
||||
ModMetadata mc = readJSON(is, uri.toString());
|
||||
if (mc != null) {
|
||||
mods.put(mc.getId(), new ModInfo(mc, file));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
WorldsTogether.LOGGER.error("Error for " + uri + ": " + e);
|
||||
}
|
||||
//if (doClose) fs.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
WorldsTogether.LOGGER.error("Error for " + file.toUri() + ": " + e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ModInfo {
|
||||
public final ModMetadata metadata;
|
||||
public final Path jarPath;
|
||||
|
||||
ModInfo(ModMetadata metadata, Path jarPath) {
|
||||
this.metadata = metadata;
|
||||
this.jarPath = jarPath;
|
||||
}
|
||||
|
||||
public static String versionToString(Version v) {
|
||||
if (v instanceof SemanticVersion) {
|
||||
return versionToString((SemanticVersion) v);
|
||||
}
|
||||
return convertModVersion(convertModVersion(v.toString()));
|
||||
}
|
||||
|
||||
public static String versionToString(SemanticVersion v) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
boolean first = true;
|
||||
final int cCount = Math.min(v.getVersionComponentCount(), 3);
|
||||
for (int i = 0; i < cCount; i++) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
stringBuilder.append('.');
|
||||
}
|
||||
|
||||
stringBuilder.append(v.getVersionComponent(i));
|
||||
}
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ModInfo{" + "id=" + metadata.getId() + ", version=" + metadata.getVersion() + ", jarPath=" + jarPath + '}';
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
if (metadata == null) {
|
||||
return "0.0.0";
|
||||
}
|
||||
return versionToString(metadata.getVersion());
|
||||
}
|
||||
}
|
||||
}
|
100
src/main/java/org/betterx/worlds/together/util/PathUtil.java
Normal file
100
src/main/java/org/betterx/worlds/together/util/PathUtil.java
Normal file
|
@ -0,0 +1,100 @@
|
|||
package org.betterx.worlds.together.util;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class PathUtil {
|
||||
public final static Path GAME_FOLDER = FabricLoader.getInstance()
|
||||
.getGameDir()
|
||||
.normalize();
|
||||
|
||||
public final static Path MOD_FOLDER = FabricLoader.getInstance()
|
||||
.getGameDir()
|
||||
.resolve("mods")
|
||||
.normalize();
|
||||
|
||||
public final static Path MOD_BAK_FOLDER = MOD_FOLDER.resolve("_bclib_deactivated")
|
||||
.normalize();
|
||||
|
||||
/**
|
||||
* Tests if a path is a child-path.
|
||||
* <p>
|
||||
* A path is a child of another if it is located in the parent or any of the parents subdirectories
|
||||
*
|
||||
* @param parent The folder we search for the {@code child}
|
||||
* @param child The folder you want to test
|
||||
* @return {@code true} if {@code child} is in {@code parent} or any of its sub directories
|
||||
*/
|
||||
public static boolean isChildOf(Path parent, Path child) {
|
||||
if (child == null || parent == null) return false;
|
||||
parent = parent.toAbsolutePath().normalize();
|
||||
child = child.toAbsolutePath().normalize();
|
||||
|
||||
final int pCount = parent.getNameCount();
|
||||
final int cCount = child.getNameCount();
|
||||
|
||||
if (cCount > pCount) return isChildOf(parent, child.getParent());
|
||||
if (cCount < pCount) return false;
|
||||
|
||||
return child.equals(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple directory walker that ignores dot-files
|
||||
*
|
||||
* @param path The path where you want to start
|
||||
* @param pathConsumer The consumer called for each valid file. The consumer will get an absolute {@link Path}-Object
|
||||
* for each visited file
|
||||
*/
|
||||
public static void fileWalker(File path, Consumer<Path> pathConsumer) {
|
||||
fileWalker(path, true, pathConsumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple directory walker that ignores dot-files
|
||||
*
|
||||
* @param path The path where you want to start
|
||||
* @param recursive if {@code false}, only the {@code path} is traversed
|
||||
* @param pathConsumer The consumer called for each valid file. The consumer will get an absolute {@link Path}-Object
|
||||
* for each visited file
|
||||
*/
|
||||
public static void fileWalker(File path, boolean recursive, Consumer<Path> pathConsumer) {
|
||||
if (!path.exists()) return;
|
||||
for (final File f : path.listFiles()) {
|
||||
if (f.getName()
|
||||
.startsWith(".")) continue;
|
||||
if (f.isDirectory()) {
|
||||
if (recursive) fileWalker(f, pathConsumer);
|
||||
} else if (f.isFile()) {
|
||||
pathConsumer.accept(f.toPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a human readable File-Size
|
||||
*
|
||||
* @param size Filesize in bytes
|
||||
* @return A Human readable String
|
||||
*/
|
||||
public static String humanReadableFileSize(long size) {
|
||||
final int threshold = 2;
|
||||
final int factor = 1024;
|
||||
if (size < 0) return "? Byte";
|
||||
if (size < factor * threshold) {
|
||||
return size + " Byte";
|
||||
}
|
||||
char[] units = {'K', 'M', 'G', 'T', 'P'};
|
||||
int unitIndex = 0;
|
||||
double fSize = size;
|
||||
do {
|
||||
unitIndex++;
|
||||
fSize /= 1024;
|
||||
} while (fSize > factor * threshold && unitIndex < units.length);
|
||||
|
||||
return String.format("%.1f %ciB", fSize, units[unitIndex - 1]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package org.betterx.worlds.together.world;
|
||||
|
||||
public interface BiomeSourceWithSeed {
|
||||
void setSeed(long seed);
|
||||
}
|
165
src/main/java/org/betterx/worlds/together/world/WorldConfig.java
Normal file
165
src/main/java/org/betterx/worlds/together/world/WorldConfig.java
Normal file
|
@ -0,0 +1,165 @@
|
|||
package org.betterx.worlds.together.world;
|
||||
|
||||
import org.betterx.worlds.together.WorldsTogether;
|
||||
import org.betterx.worlds.together.util.ModUtil;
|
||||
import org.betterx.worlds.together.world.event.WorldBootstrap;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 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 WorldBootstrap} in
|
||||
* org.betterx.worlds.together.world.event.WorldBootstrap.Helpers#initializeWorldDataAPI(File, boolean)
|
||||
*/
|
||||
public class WorldConfig {
|
||||
private static final Map<String, CompoundTag> TAGS = Maps.newHashMap();
|
||||
private static final List<String> MODS = Lists.newArrayList();
|
||||
|
||||
private static final String TAG_CREATED = "create_version";
|
||||
private static final String TAG_MODIFIED = "modify_version";
|
||||
private static File dataDir;
|
||||
|
||||
public static void load(File dataDir) {
|
||||
WorldConfig.dataDir = dataDir;
|
||||
MODS.stream()
|
||||
.parallel()
|
||||
.forEach(modID -> {
|
||||
File file = new File(dataDir, modID + ".nbt");
|
||||
CompoundTag root = new CompoundTag();
|
||||
if (file.exists()) {
|
||||
try {
|
||||
root = NbtIo.readCompressed(file);
|
||||
} catch (IOException e) {
|
||||
WorldsTogether.LOGGER.error("World data loading failed", e);
|
||||
}
|
||||
TAGS.put(modID, root);
|
||||
} else {
|
||||
Optional<ModContainer> optional = FabricLoader.getInstance()
|
||||
.getModContainer(modID);
|
||||
if (optional.isPresent()) {
|
||||
ModContainer modContainer = optional.get();
|
||||
if (WorldsTogether.isDevEnvironment()) {
|
||||
root.putString("version", "255.255.9999");
|
||||
} else {
|
||||
root.putString("version", modContainer.getMetadata()
|
||||
.getVersion()
|
||||
.toString());
|
||||
}
|
||||
TAGS.put(modID, root);
|
||||
saveFile(modID);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register mod cache, world cache is located in world data folder.
|
||||
*
|
||||
* @param modID - {@link String} modID.
|
||||
*/
|
||||
public static void registerModCache(String modID) {
|
||||
if (!MODS.contains(modID))
|
||||
MODS.add(modID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get root {@link CompoundTag} for mod cache in world data folder.
|
||||
*
|
||||
* @param modID - {@link String} modID.
|
||||
* @return {@link CompoundTag}
|
||||
*/
|
||||
public static CompoundTag getRootTag(String modID) {
|
||||
CompoundTag root = TAGS.get(modID);
|
||||
if (root == null) {
|
||||
root = new CompoundTag();
|
||||
root.putString(TAG_CREATED, ModUtil.getModVersion(modID));
|
||||
TAGS.put(modID, root);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
public static boolean hasMod(String modID) {
|
||||
return MODS.contains(modID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get {@link CompoundTag} with specified path from mod cache in world data folder.
|
||||
*
|
||||
* @param modID - {@link String} path to tag, dot-separated.
|
||||
* @return {@link CompoundTag}
|
||||
*/
|
||||
public static CompoundTag getCompoundTag(String modID, String path) {
|
||||
String[] parts = path.split("\\.");
|
||||
CompoundTag tag = getRootTag(modID);
|
||||
for (String part : parts) {
|
||||
if (tag.contains(part)) {
|
||||
tag = tag.getCompound(part);
|
||||
} else {
|
||||
CompoundTag t = new CompoundTag();
|
||||
tag.put(part, t);
|
||||
tag = t;
|
||||
}
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces mod cache file to be saved.
|
||||
*
|
||||
* @param modID {@link String} mod ID.
|
||||
*/
|
||||
public static void saveFile(String modID) {
|
||||
|
||||
try {
|
||||
if (!dataDir.exists()) {
|
||||
dataDir.mkdirs();
|
||||
}
|
||||
CompoundTag tag = getRootTag(modID);
|
||||
tag.putString(TAG_MODIFIED, ModUtil.getModVersion(modID));
|
||||
|
||||
|
||||
final File tempFile = new File(dataDir, modID + "_temp.nbt");
|
||||
NbtIo.writeCompressed(tag, tempFile);
|
||||
|
||||
final File oldFile = new File(dataDir, modID + "_old.nbt");
|
||||
final File dataFile = new File(dataDir, modID + ".nbt");
|
||||
Util.safeReplaceFile(dataFile, tempFile, oldFile);
|
||||
} catch (IOException e) {
|
||||
WorldsTogether.LOGGER.error("World data saving failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stored mod version (only for mods with registered cache).
|
||||
*
|
||||
* @return {@link String} mod version.
|
||||
*/
|
||||
public static String getModVersion(String modID) {
|
||||
return getRootTag(modID).getString("version");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stored mod version as integer (only for mods with registered cache).
|
||||
*
|
||||
* @return {@code int} mod version.
|
||||
*/
|
||||
public static int getIntModVersion(String modID) {
|
||||
return ModUtil.convertModVersion(getModVersion(modID));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
package org.betterx.worlds.together.world;
|
||||
|
||||
import org.betterx.worlds.together.WorldsTogether;
|
||||
import org.betterx.worlds.together.worldPreset.WorldPresets;
|
||||
import org.betterx.worlds.together.worldPreset.settings.WorldPresetSettings;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
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.util.RandomSource;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.levelgen.presets.WorldPreset;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||
import net.minecraft.world.level.levelgen.synth.NormalNoise;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class WorldGenUtil {
|
||||
public static final String TAG_GENERATOR = "generator";
|
||||
|
||||
public static WorldGenSettings createWorldFromPreset(
|
||||
ResourceKey<WorldPreset> preset,
|
||||
RegistryAccess registryAccess,
|
||||
long seed,
|
||||
boolean generateStructures,
|
||||
boolean generateBonusChest
|
||||
) {
|
||||
WorldGenSettings settings = registryAccess
|
||||
.registryOrThrow(Registry.WORLD_PRESET_REGISTRY)
|
||||
.getHolderOrThrow(preset)
|
||||
.value()
|
||||
.createWorldGenSettings(seed, generateStructures, generateBonusChest);
|
||||
|
||||
for (LevelStem stem : settings.dimensions()) {
|
||||
if (stem.generator().getBiomeSource() instanceof BiomeSourceWithSeed bcl) {
|
||||
bcl.setSeed(seed);
|
||||
}
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
public static WorldGenSettings createDefaultWorldFromPreset(
|
||||
RegistryAccess registryAccess,
|
||||
long seed,
|
||||
boolean generateStructures,
|
||||
boolean generateBonusChest
|
||||
) {
|
||||
return createWorldFromPreset(
|
||||
WorldPresets.DEFAULT.orElseThrow(),
|
||||
registryAccess,
|
||||
seed,
|
||||
generateStructures,
|
||||
generateBonusChest
|
||||
);
|
||||
}
|
||||
|
||||
public static Pair<WorldGenSettings, RegistryAccess.Frozen> defaultWorldDataSupplier(RegistryAccess.Frozen frozen) {
|
||||
WorldGenSettings worldGenSettings = createDefaultWorldFromPreset(frozen);
|
||||
return Pair.of(worldGenSettings, frozen);
|
||||
}
|
||||
|
||||
public static WorldGenSettings createDefaultWorldFromPreset(RegistryAccess registryAccess, long seed) {
|
||||
return createDefaultWorldFromPreset(registryAccess, seed, true, false);
|
||||
}
|
||||
|
||||
public static WorldGenSettings createDefaultWorldFromPreset(RegistryAccess registryAccess) {
|
||||
return createDefaultWorldFromPreset(registryAccess, RandomSource.create().nextLong());
|
||||
}
|
||||
|
||||
public static CompoundTag getSettingsNbt() {
|
||||
return WorldConfig.getCompoundTag(WorldsTogether.MOD_ID, TAG_GENERATOR);
|
||||
}
|
||||
|
||||
public static WorldPresetSettings getWorldSettings() {
|
||||
if (BuiltinRegistries.ACCESS == null) return null;
|
||||
final RegistryAccess registryAccess = BuiltinRegistries.ACCESS;
|
||||
final RegistryOps<Tag> registryOps = RegistryOps.create(NbtOps.INSTANCE, registryAccess);
|
||||
|
||||
Optional<WorldPresetSettings> oLevelStem = WorldPresetSettings.CODEC
|
||||
.parse(new Dynamic<>(registryOps, getSettingsNbt()))
|
||||
.resultOrPartial(WorldsTogether.LOGGER::error);
|
||||
|
||||
return oLevelStem.orElse(WorldPresetSettings.DEFAULT);
|
||||
}
|
||||
|
||||
public static class Context extends StemContext {
|
||||
public final Registry<Biome> biomes;
|
||||
|
||||
public Context(
|
||||
Registry<Biome> biomes, Holder<DimensionType> dimension,
|
||||
Registry<StructureSet> structureSets,
|
||||
Registry<NormalNoise.NoiseParameters> noiseParameters,
|
||||
Holder<NoiseGeneratorSettings> generatorSettings
|
||||
) {
|
||||
super(dimension, structureSets, noiseParameters, generatorSettings);
|
||||
this.biomes = biomes;
|
||||
}
|
||||
}
|
||||
|
||||
public static class StemContext {
|
||||
public final Holder<DimensionType> dimension;
|
||||
public final Registry<StructureSet> structureSets;
|
||||
public final Registry<NormalNoise.NoiseParameters> noiseParameters;
|
||||
public final Holder<NoiseGeneratorSettings> generatorSettings;
|
||||
|
||||
public StemContext(
|
||||
Holder<DimensionType> dimension,
|
||||
Registry<StructureSet> structureSets,
|
||||
Registry<NormalNoise.NoiseParameters> noiseParameters,
|
||||
Holder<NoiseGeneratorSettings> generatorSettings
|
||||
) {
|
||||
this.dimension = dimension;
|
||||
this.structureSets = structureSets;
|
||||
this.noiseParameters = noiseParameters;
|
||||
this.generatorSettings = generatorSettings;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package org.betterx.worlds.together.world.event;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.levelgen.presets.WorldPreset;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class AdaptWorldPresetSettingEvent extends EventImpl<OnAdaptWorldPresetSettings> {
|
||||
public Optional<Holder<WorldPreset>> emit(Optional<Holder<WorldPreset>> start, WorldGenSettings settings) {
|
||||
for (OnAdaptWorldPresetSettings a : handlers) {
|
||||
start = a.adapt(start, settings);
|
||||
}
|
||||
return start;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.betterx.worlds.together.world.event;
|
||||
|
||||
import org.betterx.worlds.together.worldPreset.settings.WorldPresetSettings;
|
||||
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface BeforeServerWorldLoad {
|
||||
void prepareWorld(
|
||||
LevelStorageSource.LevelStorageAccess storageAccess,
|
||||
WorldPresetSettings settings,
|
||||
boolean isNewWorld
|
||||
);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.betterx.worlds.together.world.event;
|
||||
|
||||
import org.betterx.worlds.together.worldPreset.settings.WorldPresetSettings;
|
||||
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
|
||||
public interface BeforeWorldLoad {
|
||||
void prepareWorld(
|
||||
LevelStorageSource.LevelStorageAccess storageAccess,
|
||||
WorldPresetSettings settings,
|
||||
boolean isNewWorld
|
||||
);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package org.betterx.worlds.together.world.event;
|
||||
|
||||
public interface Event<T> {
|
||||
boolean on(T handler);
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.betterx.worlds.together.world.event;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
class EventImpl<T> implements Event<T> {
|
||||
final List<T> handlers = new LinkedList<>();
|
||||
|
||||
public final boolean on(T handler) {
|
||||
if (!handlers.contains(handler)) {
|
||||
handlers.add(handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public final void emit(Consumer<T> c) {
|
||||
handlers.forEach(c);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package org.betterx.worlds.together.world.event;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.levelgen.presets.WorldPreset;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface OnAdaptWorldPresetSettings {
|
||||
Optional<Holder<WorldPreset>> adapt(
|
||||
Optional<Holder<WorldPreset>> currentPreset,
|
||||
WorldGenSettings worldGenSettings
|
||||
);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package org.betterx.worlds.together.world.event;
|
||||
|
||||
public interface OnWorldLoad {
|
||||
void onLoad();
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.betterx.worlds.together.world.event;
|
||||
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface OnWorldPatch {
|
||||
boolean next(
|
||||
LevelStorageSource.LevelStorageAccess storageAccess,
|
||||
Consumer<Boolean> allDone
|
||||
);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package org.betterx.worlds.together.world.event;
|
||||
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
|
||||
public interface OnWorldRegistryReady {
|
||||
void initRegistry(RegistryAccess access);
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package org.betterx.worlds.together.world.event;
|
||||
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
class PatchWorldEvent extends EventImpl<OnWorldPatch> {
|
||||
|
||||
public boolean applyPatches(
|
||||
LevelStorageSource.LevelStorageAccess storageAccess,
|
||||
Consumer<Boolean> allDone
|
||||
) {
|
||||
return applyPatches(false, false, storageAccess, handlers.iterator(), allDone);
|
||||
}
|
||||
|
||||
private boolean applyPatches(
|
||||
boolean didApplyFixes,
|
||||
boolean didShowUI,
|
||||
LevelStorageSource.LevelStorageAccess storageAccess,
|
||||
Iterator<OnWorldPatch> iterator,
|
||||
Consumer<Boolean> allDone
|
||||
) {
|
||||
if (!iterator.hasNext()) {
|
||||
if (didShowUI) allDone.accept(didApplyFixes);
|
||||
return didApplyFixes;
|
||||
}
|
||||
OnWorldPatch now = iterator.next();
|
||||
|
||||
boolean shouldHaltForUI = now.next(storageAccess, (appliedFixes) -> applyPatches(
|
||||
didApplyFixes || appliedFixes, true, storageAccess, iterator, allDone
|
||||
));
|
||||
|
||||
if (!shouldHaltForUI) {
|
||||
applyPatches(didApplyFixes, didShowUI, storageAccess, iterator, allDone);
|
||||
}
|
||||
return didApplyFixes || shouldHaltForUI;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
package org.betterx.worlds.together.world.event;
|
||||
|
||||
import org.betterx.bclib.BCLib;
|
||||
import org.betterx.worlds.together.WorldsTogether;
|
||||
import org.betterx.worlds.together.mixin.common.RegistryOpsAccessor;
|
||||
import org.betterx.worlds.together.world.WorldConfig;
|
||||
import org.betterx.worlds.together.world.WorldGenUtil;
|
||||
import org.betterx.worlds.together.worldPreset.TogetherWorldPreset;
|
||||
import org.betterx.worlds.together.worldPreset.WorldGenSettingsComponentAccessor;
|
||||
import org.betterx.worlds.together.worldPreset.WorldPresets;
|
||||
import org.betterx.worlds.together.worldPreset.settings.VanillaWorldPresetSettings;
|
||||
import org.betterx.worlds.together.worldPreset.settings.WorldPresetSettings;
|
||||
|
||||
import net.minecraft.client.gui.screens.worldselection.WorldGenSettingsComponent;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
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;
|
||||
import java.util.function.Consumer;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public class WorldBootstrap {
|
||||
private static RegistryAccess LAST_REGISTRY_ACCESS = null;
|
||||
|
||||
public 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 initializeWorldDataAPI(File levelBaseDir, boolean newWorld) {
|
||||
WorldConfig.load(new File(levelBaseDir, "data"));
|
||||
|
||||
if (newWorld) {
|
||||
WorldConfig.saveFile(BCLib.MOD_ID);
|
||||
}
|
||||
}
|
||||
|
||||
private static void onRegistryReady(RegistryAccess a) {
|
||||
if (a != LAST_REGISTRY_ACCESS) {
|
||||
LAST_REGISTRY_ACCESS = a;
|
||||
WorldEventsImpl.WORLD_REGISTRY_READY.emit(e -> e.initRegistry(a));
|
||||
}
|
||||
}
|
||||
|
||||
private static Holder<WorldPreset> defaultServerPreset() {
|
||||
return WorldPresets.get(
|
||||
LAST_REGISTRY_ACCESS,
|
||||
WorldPresets.DEFAULT.orElseThrow()
|
||||
);
|
||||
}
|
||||
|
||||
private static WorldPresetSettings defaultServerSettings() {
|
||||
final Holder<WorldPreset> defaultPreset = defaultServerPreset();
|
||||
return defaultServerSettings(defaultPreset);
|
||||
}
|
||||
|
||||
private static WorldPresetSettings defaultServerSettings(Holder<WorldPreset> defaultPreset) {
|
||||
final WorldPresetSettings settings;
|
||||
if (defaultPreset.value() instanceof TogetherWorldPreset t) {
|
||||
settings = t.settings;
|
||||
} else {
|
||||
settings = VanillaWorldPresetSettings.DEFAULT;
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DedicatedServer {
|
||||
public static void registryReady(RegistryOps<Tag> regOps) {
|
||||
if (regOps instanceof RegistryOpsAccessor acc) {
|
||||
Helpers.onRegistryReady(acc.bcl_getRegistryAccess());
|
||||
}
|
||||
}
|
||||
|
||||
public static void setupWorld(LevelStorageSource.LevelStorageAccess levelStorageAccess) {
|
||||
File levelDat = levelStorageAccess.getLevelPath(LevelResource.LEVEL_DATA_FILE).toFile();
|
||||
if (!levelDat.exists()) {
|
||||
BCLib.LOGGER.info("Creating a new World, no fixes needed");
|
||||
final WorldPresetSettings settings = Helpers.defaultServerSettings();
|
||||
|
||||
Helpers.initializeWorldDataAPI(levelStorageAccess, true);
|
||||
WorldEventsImpl.BEFORE_SERVER_WORLD_LOAD.emit(e -> e.prepareWorld(
|
||||
levelStorageAccess, settings, true
|
||||
));
|
||||
} else {
|
||||
Helpers.initializeWorldDataAPI(levelStorageAccess, false);
|
||||
WorldEventsImpl.BEFORE_SERVER_WORLD_LOAD.emit(e -> e.prepareWorld(
|
||||
levelStorageAccess,
|
||||
WorldGenUtil.getWorldSettings(),
|
||||
false
|
||||
));
|
||||
WorldEventsImpl.ON_WORLD_LOAD.emit(OnWorldLoad::onLoad);
|
||||
}
|
||||
}
|
||||
|
||||
//Needs to get called after setupWorld
|
||||
public static void applyDatapackChangesOnNewWorld(WorldGenSettings worldGenSettings) {
|
||||
Optional<Holder<WorldPreset>> currentPreset = Optional.of(Helpers.defaultServerPreset());
|
||||
var settings = Helpers.defaultServerSettings(currentPreset.orElseThrow());
|
||||
|
||||
currentPreset = WorldEventsImpl.ADAPT_WORLD_PRESET.emit(currentPreset, worldGenSettings);
|
||||
if (currentPreset.map(h -> h.value()).orElse(null) instanceof TogetherWorldPreset t) {
|
||||
settings = t.settings;
|
||||
}
|
||||
TogetherWorldPreset.writeWorldPresetSettings(settings);
|
||||
WorldEventsImpl.ON_WORLD_LOAD.emit(OnWorldLoad::onLoad);
|
||||
}
|
||||
}
|
||||
|
||||
public static class InGUI {
|
||||
public static void registryReadyOnNewWorld(WorldGenSettingsComponent worldGenSettingsComponent) {
|
||||
Helpers.onRegistryReady(worldGenSettingsComponent.registryHolder());
|
||||
}
|
||||
|
||||
public static void registryReadyOnLoadedWorld(Optional<RegistryOps<Tag>> registryOps) {
|
||||
if (registryOps.orElse(null) instanceof RegistryOpsAccessor acc) {
|
||||
Helpers.onRegistryReady(acc.bcl_getRegistryAccess());
|
||||
}
|
||||
}
|
||||
|
||||
public static void setupNewWorld(
|
||||
Optional<LevelStorageSource.LevelStorageAccess> levelStorageAccess,
|
||||
WorldGenSettingsComponent worldGenSettingsComponent
|
||||
) {
|
||||
if (levelStorageAccess.isPresent()) {
|
||||
if (worldGenSettingsComponent instanceof WorldGenSettingsComponentAccessor acc) {
|
||||
Optional<Holder<WorldPreset>> currentPreset = acc.bcl_getPreset();
|
||||
Optional<Holder<WorldPreset>> newPreset = setupNewWorldCommon(
|
||||
levelStorageAccess.get(),
|
||||
currentPreset,
|
||||
worldGenSettingsComponent.settings().worldGenSettings()
|
||||
);
|
||||
if (newPreset != currentPreset) {
|
||||
acc.bcl_setPreset(newPreset);
|
||||
}
|
||||
} else {
|
||||
WorldsTogether.LOGGER.error("Unable to access WorldGenSettingsComponent.");
|
||||
}
|
||||
} else {
|
||||
WorldsTogether.LOGGER.error("Unable to access Level Folder.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static Optional<Holder<WorldPreset>> setupNewWorldCommon(
|
||||
LevelStorageSource.LevelStorageAccess levelStorageAccess,
|
||||
Optional<Holder<WorldPreset>> currentPreset,
|
||||
WorldGenSettings worldgenSettings
|
||||
) {
|
||||
Helpers.initializeWorldDataAPI(levelStorageAccess, true);
|
||||
|
||||
|
||||
final WorldPresetSettings settings;
|
||||
if (currentPreset.map(Holder::value).orElse(null) instanceof TogetherWorldPreset t) {
|
||||
settings = t.settings;
|
||||
} else {
|
||||
settings = VanillaWorldPresetSettings.DEFAULT;
|
||||
}
|
||||
|
||||
// Helpers.setupWorld();
|
||||
// DataFixerAPI.initializePatchData();
|
||||
WorldEventsImpl.BEFORE_WORLD_LOAD.emit(e -> e.prepareWorld(
|
||||
levelStorageAccess,
|
||||
settings,
|
||||
true
|
||||
));
|
||||
|
||||
currentPreset = WorldEventsImpl.ADAPT_WORLD_PRESET.emit(currentPreset, worldgenSettings);
|
||||
|
||||
TogetherWorldPreset.writeWorldPresetSettings(currentPreset);
|
||||
|
||||
//LifeCycleAPI._runBeforeLevelLoad();
|
||||
WorldEventsImpl.ON_WORLD_LOAD.emit(OnWorldLoad::onLoad);
|
||||
|
||||
return currentPreset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not call {@link WorldEventsImpl#ON_WORLD_LOAD}
|
||||
*/
|
||||
public static void setupLoadedWorld(
|
||||
String levelID,
|
||||
LevelStorageSource levelSource
|
||||
) {
|
||||
try {
|
||||
var levelStorageAccess = levelSource.createAccess(levelID);
|
||||
Helpers.initializeWorldDataAPI(levelStorageAccess, false);
|
||||
|
||||
//Helpers.setupWorld();
|
||||
WorldEventsImpl.BEFORE_WORLD_LOAD.emit(e -> e.prepareWorld(
|
||||
levelStorageAccess,
|
||||
WorldGenUtil.getWorldSettings(),
|
||||
false
|
||||
));
|
||||
levelStorageAccess.close();
|
||||
} catch (Exception e) {
|
||||
BCLib.LOGGER.error("Failed to initialize data in world", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean applyWorldPatches(
|
||||
LevelStorageSource levelSource,
|
||||
String levelID,
|
||||
Consumer<Boolean> onResume
|
||||
) {
|
||||
boolean result = false;
|
||||
try {
|
||||
var levelStorageAccess = levelSource.createAccess(levelID);
|
||||
result = WorldEventsImpl.PATCH_WORLD.applyPatches(levelStorageAccess, onResume);
|
||||
levelStorageAccess.close();
|
||||
} catch (Exception e) {
|
||||
BCLib.LOGGER.error("Failed to initialize data in world", e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void finishedWorldLoad(
|
||||
String levelID,
|
||||
LevelStorageSource levelSource
|
||||
) {
|
||||
//LifeCycleAPI._runBeforeLevelLoad();
|
||||
WorldEventsImpl.ON_WORLD_LOAD.emit(OnWorldLoad::onLoad);
|
||||
}
|
||||
}
|
||||
|
||||
public static class InFreshLevel {
|
||||
public static void setupNewWorld(
|
||||
String levelID,
|
||||
WorldGenSettings worldGenSettings,
|
||||
LevelStorageSource levelSource,
|
||||
Optional<Holder<WorldPreset>> worldPreset
|
||||
) {
|
||||
try {
|
||||
var levelStorageAccess = levelSource.createAccess(levelID);
|
||||
InGUI.setupNewWorldCommon(levelStorageAccess, worldPreset, worldGenSettings);
|
||||
levelStorageAccess.close();
|
||||
} catch (Exception e) {
|
||||
BCLib.LOGGER.error("Failed to initialize data in world", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static WorldGenSettings enforceInNewWorld(WorldGenSettings worldGenSettings) {
|
||||
worldGenSettings = WorldGenUtil
|
||||
.getWorldSettings()
|
||||
.repairSettingsOnLoad(LAST_REGISTRY_ACCESS, worldGenSettings);
|
||||
return worldGenSettings;
|
||||
}
|
||||
|
||||
public static WorldGenSettings enforceInLoadedWorld(
|
||||
Optional<RegistryOps<Tag>> registryOps,
|
||||
WorldGenSettings worldGenSettings
|
||||
) {
|
||||
if (registryOps.orElse(null) instanceof RegistryOpsAccessor acc) {
|
||||
return WorldGenUtil
|
||||
.getWorldSettings()
|
||||
.repairSettingsOnLoad(acc.bcl_getRegistryAccess(), worldGenSettings);
|
||||
//.repairSettingsOnLoad(LAST_REGISTRY_ACCESS, worldGenSettings);
|
||||
} else {
|
||||
BCLib.LOGGER.error("Unable to obtain registryAccess when enforcing generators.");
|
||||
}
|
||||
return worldGenSettings;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.betterx.worlds.together.world.event;
|
||||
|
||||
public class WorldEvents {
|
||||
public static final Event<OnWorldRegistryReady> WORLD_REGISTRY_READY = WorldEventsImpl.WORLD_REGISTRY_READY;
|
||||
public static final Event<BeforeWorldLoad> BEFORE_WORLD_LOAD = WorldEventsImpl.BEFORE_WORLD_LOAD;
|
||||
public static final Event<BeforeServerWorldLoad> BEFORE_SERVER_WORLD_LOAD = WorldEventsImpl.BEFORE_SERVER_WORLD_LOAD;
|
||||
public static final Event<OnWorldLoad> ON_WORLD_LOAD = WorldEventsImpl.ON_WORLD_LOAD;
|
||||
|
||||
public static final Event<OnWorldPatch> PATCH_WORLD = WorldEventsImpl.PATCH_WORLD;
|
||||
public static final Event<OnAdaptWorldPresetSettings> ADAPT_WORLD_PRESET = WorldEventsImpl.ADAPT_WORLD_PRESET;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.betterx.worlds.together.world.event;
|
||||
|
||||
class WorldEventsImpl {
|
||||
public static final EventImpl<OnWorldRegistryReady> WORLD_REGISTRY_READY = new EventImpl<>();
|
||||
public static final EventImpl<BeforeWorldLoad> BEFORE_WORLD_LOAD = new EventImpl<>();
|
||||
public static final EventImpl<BeforeServerWorldLoad> BEFORE_SERVER_WORLD_LOAD = new EventImpl<>();
|
||||
|
||||
public static final EventImpl<OnWorldLoad> ON_WORLD_LOAD = new EventImpl<>();
|
||||
|
||||
public static final PatchWorldEvent PATCH_WORLD = new PatchWorldEvent();
|
||||
public static final AdaptWorldPresetSettingEvent ADAPT_WORLD_PRESET = new AdaptWorldPresetSettingEvent();
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package org.betterx.worlds.together.worldPreset;
|
||||
|
||||
import org.betterx.worlds.together.WorldsTogether;
|
||||
import org.betterx.worlds.together.mixin.common.WorldPresetAccessor;
|
||||
import org.betterx.worlds.together.world.WorldConfig;
|
||||
import org.betterx.worlds.together.world.WorldGenUtil;
|
||||
import org.betterx.worlds.together.worldPreset.settings.VanillaWorldPresetSettings;
|
||||
import org.betterx.worlds.together.worldPreset.settings.WorldPresetSettings;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
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 TogetherWorldPreset extends WorldPreset {
|
||||
public final WorldPresetSettings settings;
|
||||
public final int sortOrder;
|
||||
|
||||
private static final String TAG_GENERATOR = WorldGenUtil.TAG_GENERATOR;
|
||||
|
||||
private static int NEXT_IN_SORT_ORDER = 1000;
|
||||
|
||||
public TogetherWorldPreset(
|
||||
Map<ResourceKey<LevelStem>, LevelStem> map,
|
||||
Optional<Integer> sortOrder,
|
||||
Optional<WorldPresetSettings> settings
|
||||
) {
|
||||
this(map, sortOrder.orElse(NEXT_IN_SORT_ORDER++), settings.orElse(VanillaWorldPresetSettings.DEFAULT));
|
||||
}
|
||||
|
||||
public TogetherWorldPreset(
|
||||
Map<ResourceKey<LevelStem>, LevelStem> map,
|
||||
int sortOrder,
|
||||
WorldPresetSettings settings
|
||||
) {
|
||||
super(map);
|
||||
this.sortOrder = sortOrder;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public TogetherWorldPreset withSettings(WorldPresetSettings settings) {
|
||||
return new TogetherWorldPreset(getDimensions(), sortOrder, settings);
|
||||
}
|
||||
|
||||
private Map<ResourceKey<LevelStem>, LevelStem> getDimensions() {
|
||||
return ((WorldPresetAccessor) this).bcl_getDimensions();
|
||||
}
|
||||
|
||||
public static WorldPresetSettings writeWorldPresetSettings(Optional<Holder<WorldPreset>> worldPreset) {
|
||||
if (worldPreset.isPresent() && worldPreset.get().value() instanceof TogetherWorldPreset 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 = WorldConfig.getRootTag(WorldsTogether.MOD_ID);
|
||||
settingsNbt.put(TAG_GENERATOR, encodeResult.result().get());
|
||||
} else {
|
||||
WorldsTogether.LOGGER.error("Unable to encode world generator settings for level.dat.");
|
||||
}
|
||||
|
||||
WorldConfig.saveFile(WorldsTogether.MOD_ID);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.betterx.worlds.together.worldPreset;
|
||||
|
||||
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();
|
||||
void bcl_setPreset(Optional<Holder<WorldPreset>> preset);
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package org.betterx.worlds.together.worldPreset;
|
||||
|
||||
import org.betterx.bclib.api.v2.tag.TagAPI;
|
||||
import org.betterx.bclib.api.v2.tag.TagType;
|
||||
import org.betterx.bclib.registry.PresetsRegistry;
|
||||
import org.betterx.worlds.together.WorldsTogether;
|
||||
import org.betterx.worlds.together.world.WorldGenUtil;
|
||||
import org.betterx.worlds.together.worldPreset.client.WorldPresetsClient;
|
||||
import org.betterx.worlds.together.worldPreset.settings.VanillaWorldPresetSettings;
|
||||
import org.betterx.worlds.together.worldPreset.settings.WorldPresetSettings;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.data.BuiltinRegistries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.WorldPresetTags;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.presets.WorldPreset;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class WorldPresets {
|
||||
|
||||
public static final TagType.Simple<WorldPreset> WORLD_PRESETS =
|
||||
TagAPI.registerType(BuiltinRegistries.WORLD_PRESET, "tags/worldgen/world_preset");
|
||||
private static Map<ResourceKey<WorldPreset>, PresetBuilder> BUILDERS = Maps.newHashMap();
|
||||
private static final Map<ResourceKey<WorldPreset>, WorldPresetSettings> SETTINGS = Maps.newHashMap();
|
||||
public static Optional<ResourceKey<WorldPreset>> DEFAULT = Optional.of(net.minecraft.world.level.levelgen.presets.WorldPresets.NORMAL);
|
||||
|
||||
public static Holder<WorldPreset> get(RegistryAccess access, ResourceKey<WorldPreset> key) {
|
||||
return ((access != null) ? access : BuiltinRegistries.ACCESS)
|
||||
.registryOrThrow(Registry.WORLD_PRESET_REGISTRY)
|
||||
.getHolderOrThrow(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a custom WorldPreset (with custom rules and behaviour)
|
||||
* <p>
|
||||
* See also {@link WorldPresetsClient} if you need to add a Customize Button/Screen
|
||||
* for your preset
|
||||
*
|
||||
* @param loc The ID of your Preset
|
||||
* @param visibleInUI if true, the preset will show up in the UI on world creataion
|
||||
* @return The key you may use to reference your new Preset
|
||||
*/
|
||||
private static ResourceKey<WorldPreset> register(ResourceLocation loc, boolean visibleInUI) {
|
||||
ResourceKey<WorldPreset> key = ResourceKey.create(Registry.WORLD_PRESET_REGISTRY, loc);
|
||||
if (visibleInUI) {
|
||||
WORLD_PRESETS.addUntyped(WorldPresetTags.NORMAL, key.location());
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
public static void ensureStaticallyLoaded() {
|
||||
|
||||
}
|
||||
|
||||
public static ResourceKey<WorldPreset> register(
|
||||
ResourceLocation loc,
|
||||
PresetBuilder builder,
|
||||
boolean visibleInUI
|
||||
) {
|
||||
ResourceKey<WorldPreset> key = register(loc, visibleInUI);
|
||||
|
||||
if (BUILDERS == null) {
|
||||
WorldsTogether.LOGGER.error("Unable to register WorldPreset '" + loc + "'.");
|
||||
|
||||
} else {
|
||||
BUILDERS.put(key, builder);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
public static void bootstrapPresets(
|
||||
Registry<WorldPreset> presets,
|
||||
LevelStem overworldStem,
|
||||
WorldGenUtil.Context netherContext,
|
||||
WorldGenUtil.Context endContext
|
||||
) {
|
||||
PresetsRegistry.onLoad();
|
||||
|
||||
for (Map.Entry<ResourceKey<WorldPreset>, PresetBuilder> e : BUILDERS.entrySet()) {
|
||||
TogetherWorldPreset preset = e.getValue().create(overworldStem, netherContext, endContext);
|
||||
SETTINGS.put(e.getKey(), preset.settings);
|
||||
BuiltinRegistries.register(presets, e.getKey(), preset);
|
||||
}
|
||||
BUILDERS = null;
|
||||
}
|
||||
|
||||
public static WorldPresetSettings getSettingsForPreset(ResourceKey<WorldPreset> key) {
|
||||
return SETTINGS.getOrDefault(key, VanillaWorldPresetSettings.DEFAULT);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PresetBuilder {
|
||||
TogetherWorldPreset create(
|
||||
LevelStem overworldStem,
|
||||
WorldGenUtil.Context netherContext,
|
||||
WorldGenUtil.Context endContext
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package org.betterx.worlds.together.worldPreset.client;
|
||||
|
||||
import org.betterx.bclib.registry.PresetsRegistryClient;
|
||||
|
||||
import net.minecraft.client.gui.screens.worldselection.PresetEditor;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.world.level.levelgen.presets.WorldPreset;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class WorldPresetsClient {
|
||||
public static void registerCustomizeUI(ResourceKey<WorldPreset> key, PresetEditor setupScreen) {
|
||||
if (setupScreen != null) {
|
||||
PresetEditor.EDITORS.put(Optional.of(key), setupScreen);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setupClientside() {
|
||||
PresetsRegistryClient.onLoad();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package org.betterx.worlds.together.worldPreset.settings;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package org.betterx.worlds.together.worldPreset.settings;
|
||||
|
||||
import org.betterx.worlds.together.WorldsTogether;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
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;
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class WorldPresetSettings {
|
||||
public static WorldPresetSettings DEFAULT = VanillaWorldPresetSettings.DEFAULT;
|
||||
public static final ResourceKey<Registry<Codec<? extends WorldPresetSettings>>> WORLD_PRESET_SETTINGS_REGISTRY =
|
||||
createRegistryKey(WorldsTogether.makeID("worldgen/world_preset_settings"));
|
||||
|
||||
public static final Registry<Codec<? extends WorldPresetSettings>> WORLD_PRESET_SETTINGS =
|
||||
registerSimple(WORLD_PRESET_SETTINGS_REGISTRY);
|
||||
|
||||
public static final Codec<WorldPresetSettings> CODEC = WORLD_PRESET_SETTINGS
|
||||
.byNameCodec()
|
||||
.dispatchStable(WorldPresetSettings::codec, Function.identity());
|
||||
|
||||
|
||||
private static <T> ResourceKey<Registry<T>> createRegistryKey(ResourceLocation location) {
|
||||
|
||||
return ResourceKey.createRegistryKey(location);
|
||||
}
|
||||
|
||||
private static <T> Registry<T> registerSimple(ResourceKey<? extends Registry<T>> resourceKey) {
|
||||
return new MappedRegistry<>(resourceKey, Lifecycle.stable(), null);
|
||||
}
|
||||
|
||||
public static Codec<? extends WorldPresetSettings> register(
|
||||
ResourceLocation loc,
|
||||
Codec<? extends WorldPresetSettings> codec
|
||||
) {
|
||||
return Registry.register(WORLD_PRESET_SETTINGS, loc, codec);
|
||||
}
|
||||
|
||||
public static void bootstrap() {
|
||||
register(WorldsTogether.makeID("vanilla_world_preset_settings"), VanillaWorldPresetSettings.CODEC);
|
||||
}
|
||||
|
||||
public abstract Codec<? extends WorldPresetSettings> codec();
|
||||
public abstract WorldGenSettings repairSettingsOnLoad(RegistryAccess registryAccess, WorldGenSettings settings);
|
||||
public abstract BiomeSource fixBiomeSource(BiomeSource biomeSource, Set<Holder<Biome>> datapackBiomes);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue