diff --git a/src/main/java/org/embeddedt/modernfix/duck/ILevelSave.java b/src/main/java/org/embeddedt/modernfix/duck/ILevelSave.java new file mode 100644 index 00000000..2e09b28b --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/duck/ILevelSave.java @@ -0,0 +1,5 @@ +package org.embeddedt.modernfix.duck; + +public interface ILevelSave { + public void runWorldPersistenceHooks(); +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/LevelSaveMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/LevelSaveMixin.java new file mode 100644 index 00000000..b25dddf1 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/LevelSaveMixin.java @@ -0,0 +1,32 @@ +package org.embeddedt.modernfix.mixin; + +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.world.storage.SaveFormat; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.duck.ILevelSave; +import org.embeddedt.modernfix.util.DummyServerConfiguration; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.nio.file.Path; + +@Mixin(SaveFormat.LevelSave.class) +public class LevelSaveMixin implements ILevelSave { + @Shadow @Final private SaveFormat this$0; + + @Shadow @Final private Path saveDir; + + public void runWorldPersistenceHooks() { + ((SaveFormatAccessor)this.this$0).invokeReadFromLevelData(this.saveDir.toFile(), (file, dataFixer) -> { + try { + CompoundNBT compoundTag = CompressedStreamTools.readCompressed(file); + net.minecraftforge.fml.WorldPersistenceHooks.handleWorldDataLoad((SaveFormat.LevelSave)(Object)this, new DummyServerConfiguration(), compoundTag); + } catch (Exception e) { + ModernFix.LOGGER.error("Exception reading {}", file, e); + } + return null; + }); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/MinecraftMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/MinecraftMixin.java index 484bc5a9..4250a21a 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/MinecraftMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/MinecraftMixin.java @@ -9,17 +9,24 @@ import net.minecraft.world.storage.IServerConfiguration; import net.minecraft.world.storage.SaveFormat; import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.ModernFixClient; +import org.embeddedt.modernfix.duck.ILevelSave; 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.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.concurrent.ExecutionException; import java.util.function.Function; @Mixin(Minecraft.class) -public class MinecraftMixin { +public abstract class MinecraftMixin { + @Shadow public abstract Minecraft.PackManager reloadDatapacks(DynamicRegistries.Impl dynamicRegistries, Function worldStorageToDatapackFunction, Function4 quadFunction, boolean vanillaOnly, SaveFormat.LevelSave worldStorage) throws InterruptedException, ExecutionException; + private long datapackReloadStartTime; + private int registryHash; @Inject(method = "reloadDatapacks", at = @At(value = "HEAD")) private void recordReloadStart(DynamicRegistries.Impl p_238189_1_, Function p_238189_2_, Function4 p_238189_3_, boolean p_238189_4_, SaveFormat.LevelSave p_238189_5_, CallbackInfoReturnable cir) { datapackReloadStartTime = System.nanoTime(); @@ -35,4 +42,21 @@ public class MinecraftMixin { private void recordWorldLoadStart(String worldName, DynamicRegistries.Impl dynamicRegistries, Function levelSaveToDatapackFunction, Function4 quadFunction, boolean vanillaOnly, Minecraft.WorldSelectionType selectionType, boolean creating, CallbackInfo ci) { ModernFixClient.worldLoadStartTime = System.nanoTime(); } + + @Redirect(method = "loadWorld(Ljava/lang/String;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/registry/DynamicRegistries;func_239770_b_()Lnet/minecraft/util/registry/DynamicRegistries$Impl;")) + private DynamicRegistries.Impl useNullRegistry() { + return null; + } + + @Redirect(method = "loadWorld(Ljava/lang/String;Lnet/minecraft/util/registry/DynamicRegistries$Impl;Ljava/util/function/Function;Lcom/mojang/datafixers/util/Function4;ZLnet/minecraft/client/Minecraft$WorldSelectionType;Z)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;reloadDatapacks(Lnet/minecraft/util/registry/DynamicRegistries$Impl;Ljava/util/function/Function;Lcom/mojang/datafixers/util/Function4;ZLnet/minecraft/world/storage/SaveFormat$LevelSave;)Lnet/minecraft/client/Minecraft$PackManager;", ordinal = 0)) + private Minecraft.PackManager skipFirstReload(Minecraft client, DynamicRegistries.Impl dynamicRegistries, Function worldStorageToDatapackFunction, Function4 quadFunction, boolean vanillaOnly, SaveFormat.LevelSave levelSave, String worldName, DynamicRegistries.Impl originalRegistries, Function levelSaveToDatapackFunction, Function4 quadFunction2, boolean vanillaOnly2, Minecraft.WorldSelectionType selectionType, boolean creating) throws InterruptedException, ExecutionException { + if(!creating) { + ModernFix.LOGGER.warn("Skipping first reload, this is still experimental"); + ((ILevelSave)levelSave).runWorldPersistenceHooks(); + return null; + } else { + /* allow reload */ + return reloadDatapacks(dynamicRegistries, worldStorageToDatapackFunction, quadFunction, vanillaOnly, levelSave); + } + } } diff --git a/src/main/java/org/embeddedt/modernfix/mixin/SaveFormatAccessor.java b/src/main/java/org/embeddedt/modernfix/mixin/SaveFormatAccessor.java new file mode 100644 index 00000000..cb855d06 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/SaveFormatAccessor.java @@ -0,0 +1,15 @@ +package org.embeddedt.modernfix.mixin; + +import com.mojang.datafixers.DataFixer; +import net.minecraft.world.storage.SaveFormat; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.io.File; +import java.util.function.BiFunction; + +@Mixin(SaveFormat.class) +public interface SaveFormatAccessor { + @Invoker + T invokeReadFromLevelData(File saveDir, BiFunction levelDatReader); +} diff --git a/src/main/java/org/embeddedt/modernfix/util/DummyServerConfiguration.java b/src/main/java/org/embeddedt/modernfix/util/DummyServerConfiguration.java new file mode 100644 index 00000000..dd64704c --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/util/DummyServerConfiguration.java @@ -0,0 +1,150 @@ +package org.embeddedt.modernfix.util; + +import com.google.common.collect.ImmutableSet; +import com.mojang.serialization.Lifecycle; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.datafix.codec.DatapackCodec; +import net.minecraft.util.registry.DynamicRegistries; +import net.minecraft.world.Difficulty; +import net.minecraft.world.GameRules; +import net.minecraft.world.GameType; +import net.minecraft.world.WorldSettings; +import net.minecraft.world.gen.settings.DimensionGeneratorSettings; +import net.minecraft.world.storage.IServerConfiguration; +import net.minecraft.world.storage.IServerWorldInfo; + +import javax.annotation.Nullable; +import java.util.Set; + +public class DummyServerConfiguration implements IServerConfiguration { + @Override + public DatapackCodec getDatapackCodec() { + return DatapackCodec.VANILLA_CODEC; + } + + @Override + public void setDatapackCodec(DatapackCodec codec) { + + } + + @Override + public boolean isModded() { + return true; + } + + @Override + public Set getServerBranding() { + return ImmutableSet.of("forge"); + } + + @Override + public void addServerBranding(String name, boolean isModded) { + + } + + @Nullable + @Override + public CompoundNBT getCustomBossEventData() { + return null; + } + + @Override + public void setCustomBossEventData(@Nullable CompoundNBT nbt) { + + } + + @Override + public IServerWorldInfo getServerWorldInfo() { + return null; + } + + @Override + public WorldSettings getWorldSettings() { + return null; + } + + @Override + public CompoundNBT serialize(DynamicRegistries registries, @Nullable CompoundNBT hostPlayerNBT) { + return null; + } + + @Override + public boolean isHardcore() { + return false; + } + + @Override + public int getStorageVersionId() { + return 0; + } + + @Override + public String getWorldName() { + return null; + } + + @Override + public GameType getGameType() { + return null; + } + + @Override + public void setGameType(GameType type) { + + } + + @Override + public boolean areCommandsAllowed() { + return false; + } + + @Override + public Difficulty getDifficulty() { + return null; + } + + @Override + public void setDifficulty(Difficulty difficulty) { + + } + + @Override + public boolean isDifficultyLocked() { + return false; + } + + @Override + public void setDifficultyLocked(boolean locked) { + + } + + @Override + public GameRules getGameRulesInstance() { + return null; + } + + @Override + public CompoundNBT getHostPlayerNBT() { + return null; + } + + @Override + public CompoundNBT getDragonFightData() { + return null; + } + + @Override + public void setDragonFightData(CompoundNBT nbt) { + + } + + @Override + public DimensionGeneratorSettings getDimensionGeneratorSettings() { + return null; + } + + @Override + public Lifecycle getLifecycle() { + return Lifecycle.stable(); + } +} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 83bfe277..ac6e1362 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -6,7 +6,9 @@ "refmap": "modernfix.refmap.json", "mixins": [ "ModFileResourcePackMixin", - "VanillaPackMixin" + "VanillaPackMixin", + "LevelSaveMixin", + "SaveFormatAccessor" ], "client": [ "MinecraftMixin"