From eb8c87468f8854d86d3706f40a2cd4932a3dd5e3 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Mon, 26 Jul 2021 21:31:17 +0200 Subject: [PATCH 1/2] Initialize new worlds as fully patched --- src/main/java/ru/bclib/api/WorldDataAPI.java | 3 + .../ru/bclib/api/datafixer/DataFixerAPI.java | 103 +++++++++++++----- .../ru/bclib/mixin/client/MinecraftMixin.java | 8 ++ .../java/ru/bclib/mixin/common/MainMixin.java | 2 +- 4 files changed, 89 insertions(+), 27 deletions(-) diff --git a/src/main/java/ru/bclib/api/WorldDataAPI.java b/src/main/java/ru/bclib/api/WorldDataAPI.java index 1ce22f35..616791fd 100644 --- a/src/main/java/ru/bclib/api/WorldDataAPI.java +++ b/src/main/java/ru/bclib/api/WorldDataAPI.java @@ -104,6 +104,9 @@ public class WorldDataAPI { */ public static void saveFile(String modID) { try { + if (!dataDir.exists()){ + dataDir.mkdirs(); + } NbtIo.writeCompressed(getRootTag(modID), new File(dataDir, modID + ".nbt")); } catch (IOException e) { diff --git a/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java b/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java index 258747a7..269096b3 100644 --- a/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java +++ b/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java @@ -17,6 +17,8 @@ import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.storage.LevelResource; import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess; +import org.jetbrains.annotations.NotNull; import ru.bclib.BCLib; import ru.bclib.api.WorldDataAPI; import ru.bclib.config.Configs; @@ -31,6 +33,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -49,6 +52,29 @@ public class DataFixerAPI { public void call(); } + private static boolean wrapCall(LevelStorageSource levelSource, String levelID, Function runWithLevel) { + + LevelStorageSource.LevelStorageAccess levelStorageAccess; + try { + levelStorageAccess = levelSource.createAccess(levelID); + } catch (IOException e) { + BCLib.LOGGER.warning((String)"Failed to read level {} data", levelID, e); + SystemToast.onWorldAccessFailure(Minecraft.getInstance(), levelID); + Minecraft.getInstance().setScreen((Screen)null); + return true; + } + + boolean returnValue = runWithLevel.apply(levelStorageAccess); + + try { + levelStorageAccess.close(); + } catch (IOException e) { + BCLib.LOGGER.warning((String)"Failed to unlock access to level {}", levelID, e); + } + + return returnValue; + } + /** * Will apply necessary Patches to the world. * @@ -63,26 +89,7 @@ public class DataFixerAPI { * */ public static boolean fixData(LevelStorageSource levelSource, String levelID, boolean showUI, Consumer onResume) { - - LevelStorageSource.LevelStorageAccess levelStorageAccess; - try { - levelStorageAccess = levelSource.createAccess(levelID); - } catch (IOException e) { - BCLib.LOGGER.warning((String)"Failed to read level {} data", levelID, e); - SystemToast.onWorldAccessFailure(Minecraft.getInstance(), levelID); - Minecraft.getInstance().setScreen((Screen)null); - return true; - } - - boolean cancelRun = fixData(levelStorageAccess, showUI, onResume); - - try { - levelStorageAccess.close(); - } catch (IOException e) { - BCLib.LOGGER.warning((String)"Failed to unlock access to level {}", levelID, e); - } - - return cancelRun; + return wrapCall(levelSource, levelID, (levelStorageAccess) -> fixData(levelStorageAccess, showUI, onResume)); } /** @@ -98,12 +105,50 @@ public class DataFixerAPI { */ public static boolean fixData(LevelStorageSource.LevelStorageAccess levelStorageAccess, boolean showUI, Consumer onResume){ File levelPath = levelStorageAccess.getLevelPath(LevelResource.ROOT).toFile(); - WorldDataAPI.load(new File(levelPath, "data")); + + boolean newWorld = false; + if (!levelPath.exists()) { + BCLib.LOGGER.info("Creating a new World, no fixes needed"); + newWorld = true; + } + + initializeWorldData(levelPath, newWorld); + if (newWorld) return false; + return fixData(levelPath, levelStorageAccess.getLevelId(), showUI, onResume); } + /** + * Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the + * @param levelSource The SourceStorage for this Minecraft instance, You can get this using + * {@code Minecraft.getInstance().getLevelSource()} + * @param levelID The ID of the Level you want to patch + * @param newWorld {@code true} if this is a fresh world + */ + public static void initializeWorldData(LevelStorageSource levelSource, String levelID, boolean newWorld) { + wrapCall(levelSource, levelID, (levelStorageAccess) -> { + initializeWorldData(levelStorageAccess.getLevelPath(LevelResource.ROOT).toFile(), newWorld); + return true; + }); + } + + /** + * Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the + * current versions of the plugins. + * @param levelPath Folder of the world + * @param newWorld {@code true} if this is a fresh world + * + */ + public static void initializeWorldData(File levelPath, boolean newWorld){ + WorldDataAPI.load(new File(levelPath, "data")); + + if (newWorld){ + getMigrationProfile().markApplied(); + WorldDataAPI.saveFile(BCLib.MOD_ID); + } + } private static boolean fixData(File dir, String levelID, boolean showUI, Consumer onResume) { - MigrationProfile profile = loadProfile(dir, levelID); + MigrationProfile profile = loadProfileIfNeeded(); Consumer runFixes = (applyFixes) -> { if (applyFixes) { @@ -153,16 +198,15 @@ public class DataFixerAPI { return false; } - static MigrationProfile loadProfile(File dir, String levelID){ + private static MigrationProfile loadProfileIfNeeded(){ if (!Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_PATCH_CATEGORY, "applyPatches", true)) { LOGGER.info("World Patches are disabled"); return null; } - final CompoundTag patchConfig = WorldDataAPI.getCompoundTag(BCLib.MOD_ID, Configs.MAIN_PATCH_CATEGORY); - MigrationProfile profile = Patch.createMigrationData(patchConfig); + MigrationProfile profile = getMigrationProfile(); - if (!profile.hasAnyFixes()) { + if (!profile.hasAnyFixes()) { LOGGER.info("Everything up to date"); return null; } @@ -170,6 +214,13 @@ public class DataFixerAPI { return profile; } + @NotNull + private static MigrationProfile getMigrationProfile() { + final CompoundTag patchConfig = WorldDataAPI.getCompoundTag(BCLib.MOD_ID, Configs.MAIN_PATCH_CATEGORY); + MigrationProfile profile = Patch.createMigrationData(patchConfig); + return profile; + } + @Environment(EnvType.CLIENT) static void showBackupWarning(String levelID, Consumer whenFinished){ TranslatableComponent promptText = new TranslatableComponent("bclib.datafixer.backupWarning.prompt"); diff --git a/src/main/java/ru/bclib/mixin/client/MinecraftMixin.java b/src/main/java/ru/bclib/mixin/client/MinecraftMixin.java index 9970d91f..7cfd889a 100644 --- a/src/main/java/ru/bclib/mixin/client/MinecraftMixin.java +++ b/src/main/java/ru/bclib/mixin/client/MinecraftMixin.java @@ -5,6 +5,9 @@ import net.minecraft.client.color.block.BlockColors; import net.minecraft.client.color.item.ItemColors; import net.minecraft.client.main.GameConfig; import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess.RegistryHolder; +import net.minecraft.world.level.LevelSettings; +import net.minecraft.world.level.levelgen.WorldGenSettings; import net.minecraft.world.level.storage.LevelStorageSource; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -67,6 +70,11 @@ public abstract class MinecraftMixin { return levelID; } + @Inject(method="createLevel", at=@At("HEAD")) + private void bclib_initPatchData(String levelID, LevelSettings levelSettings, RegistryHolder registryHolder, WorldGenSettings worldGenSettings, CallbackInfo ci) { + DataFixerAPI.initializeWorldData(this.levelSource, levelID, true); + } + // @Inject(method="doLoadLevel", cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD, at=@At(value="INVOKE", target="Lnet/minecraft/client/Minecraft;makeServerStem(Lnet/minecraft/core/RegistryAccess$RegistryHolder;Ljava/util/function/Function;Lcom/mojang/datafixers/util/Function4;ZLnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;)Lnet/minecraft/client/Minecraft$ServerStem;")) // private void bclib_onCallFixer( diff --git a/src/main/java/ru/bclib/mixin/common/MainMixin.java b/src/main/java/ru/bclib/mixin/common/MainMixin.java index bb39c129..142aada1 100644 --- a/src/main/java/ru/bclib/mixin/common/MainMixin.java +++ b/src/main/java/ru/bclib/mixin/common/MainMixin.java @@ -11,7 +11,7 @@ import ru.bclib.api.datafixer.DataFixerAPI; abstract public class MainMixin { @ModifyArg(method="main", at=@At(value="INVOKE", target="Lnet/minecraft/server/MinecraftServer;convertFromRegionFormatIfNeeded(Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess;)V")) private static LevelStorageSource.LevelStorageAccess bclib_callServerFix(LevelStorageSource.LevelStorageAccess session){ - DataFixerAPI.fixData(session, false, (didFix)->{}); + DataFixerAPI.fixData(session, false, (didFix)->{/* not called when showUI==false */}); return session; } } From eda626fe108acb2307be0f2a70e3cf82b5be08bd Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Mon, 26 Jul 2021 21:36:34 +0200 Subject: [PATCH 2/2] Doc update --- src/main/java/ru/bclib/api/WorldDataAPI.java | 9 +++++++++ src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/src/main/java/ru/bclib/api/WorldDataAPI.java b/src/main/java/ru/bclib/api/WorldDataAPI.java index 616791fd..78bbd980 100644 --- a/src/main/java/ru/bclib/api/WorldDataAPI.java +++ b/src/main/java/ru/bclib/api/WorldDataAPI.java @@ -6,6 +6,7 @@ import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; +import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess; import ru.bclib.BCLib; import ru.bclib.api.datafixer.DataFixerAPI; @@ -14,7 +15,15 @@ import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Consumer; +/** + * Mod-specifix data-storage for a world. + * + * This class provides the ability for mod to store persistent data inside a world. The Storage for the world is + * currently initialized as part of the {@link DataFixerAPI} in {@link DataFixerAPI#fixData(LevelStorageAccess, boolean, Consumer)} + * or {@link DataFixerAPI#initializeWorldData(File, boolean)} + */ public class WorldDataAPI { private static final Map TAGS = Maps.newHashMap(); private static final List MODS = Lists.newArrayList(); diff --git a/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java b/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java index 269096b3..6f995919 100644 --- a/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java +++ b/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java @@ -119,6 +119,10 @@ public class DataFixerAPI { } /** * Initializes the DataStorage for this world. If the world is new, the patch registry is initialized to the + * current versions of the plugins. + *

+ * This implementation will create a new {@link LevelStorageAccess} and call {@link #initializeWorldData(File, boolean)} + * using the provided root path. * @param levelSource The SourceStorage for this Minecraft instance, You can get this using * {@code Minecraft.getInstance().getLevelSource()} * @param levelID The ID of the Level you want to patch