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; } }