From a247b17e7f3e7aa81cc34e93d97dec87ff580adb Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Mon, 26 Jul 2021 18:36:20 +0200 Subject: [PATCH 1/3] Added Screen to confirm Patches --- .../ru/bclib/api/datafixer/DataFixerAPI.java | 177 +++++++++++++++++- .../complexmaterials/ComplexMaterial.java | 4 +- .../WoodenComplexMaterial.java | 25 ++- .../bclib/gui/screens/ConfirmFixScreen.java | 132 +++++++++++++ .../ru/bclib/mixin/client/MinecraftMixin.java | 52 ++++- .../mixin/common/MinecraftServerMixin.java | 10 +- .../resources/assets/bclib/lang/de_de.json | 3 + .../resources/assets/bclib/lang/en_us.json | 7 +- 8 files changed, 384 insertions(+), 26 deletions(-) create mode 100644 src/main/java/ru/bclib/gui/screens/ConfirmFixScreen.java create mode 100644 src/main/resources/assets/bclib/lang/de_de.json diff --git a/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java b/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java index 3f0abcc7..86df45bb 100644 --- a/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java +++ b/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java @@ -1,59 +1,215 @@ package ru.bclib.api.datafixer; +import net.minecraft.Util; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.toasts.SystemToast; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.worldselection.EditWorldScreen; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtIo; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.util.ProgressListener; 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 ru.bclib.BCLib; import ru.bclib.api.WorldDataAPI; import ru.bclib.config.Configs; +import ru.bclib.gui.screens.ConfirmFixScreen; import ru.bclib.util.Logger; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.function.Consumer; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * API to manage Patches that need to get applied to a world + */ public class DataFixerAPI { static final Logger LOGGER = new Logger("DataFixerAPI"); static class State { public boolean didFail = false; } - public static void fixData(File dir) { + @FunctionalInterface + public static interface Callback { + public void call(); + } + + /** + * Will apply necessary Patches to the world. + * + * @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 showUI {@code true}, if you want to present the user with a Screen that offers to backup the world + * before applying the patches + * @param onResume When this method retursn {@code true}, this function will be called when the world is ready + * @return {@code true} if the UI was displayed. The UI is only displayed if {@code showUI} was {@code true} and + * patches were enabled in the config and the Guardian did find any patches that need to be applied to the world. + * + */ + 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; + } + + /** + * Will apply necessary Patches to the world. + * + * @param levelStorageAccess The access class of the level you want to patch + * @param showUI {@code true}, if you want to present the user with a Screen that offers to backup the world + * before applying the patches + * @param onResume When this method retursn {@code true}, this function will be called when the world is ready + * @return {@code true} if the UI was displayed. The UI is only displayed if {@code showUI} was {@code true} and + * patches were enabled in the config and the Guardian did find any patches that need to be applied to the world. + * + */ + public static boolean fixData(LevelStorageSource.LevelStorageAccess levelStorageAccess, boolean showUI, Consumer onResume){ + File levelPath = levelStorageAccess.getLevelPath(LevelResource.ROOT).toFile(); + WorldDataAPI.load(new File(levelPath, "data")); + return fixData(levelPath, levelStorageAccess.getLevelId(), showUI, onResume); + } + + private static boolean fixData(File dir, String levelID, boolean showUI, Consumer onResume) { + MigrationProfile profile = loadProfile(dir, levelID); + + Consumer runFixes = (applyFixes) -> { + if (applyFixes) { + runDataFixes(dir, profile, new ProgressListener() { + private long timeStamp = Util.getMillis(); + + public void progressStartNoAbort(Component component) { + } + + public void progressStart(Component component) { + } + + public void progressStagePercentage(int i) { + if (Util.getMillis() - this.timeStamp >= 1000L) { + this.timeStamp = Util.getMillis(); + BCLib.LOGGER.info((String) "Patching... {}%", (Object) i); + } + } + + public void stop() { + } + + public void progressStage(Component component) { + } + }); + } else { + //System.out.println("NO FIXES"); + } + + //UI is asynchronous, so we need to send the callback now + if (profile != null && showUI) { + onResume.accept(applyFixes); + } + }; + + //we have some migrations + if (profile != null) { + //display the confirm UI. + if (showUI){ + showBackupWarning(levelID, runFixes); + return true; + } else { + BCLib.LOGGER.warning("Applying Fixes on Level"); + runFixes.accept(true); + } + } + return false; + } + + static MigrationProfile loadProfile(File dir, String levelID){ if (!Configs.MAIN_CONFIG.getBoolean(Configs.MAIN_PATCH_CATEGORY, "applyPatches", true)) { LOGGER.info("World Patches are disabled"); - return; + return null; } final CompoundTag patchConfig = WorldDataAPI.getCompoundTag(BCLib.MOD_ID, Configs.MAIN_PATCH_CATEGORY); - MigrationProfile data = Patch.createMigrationData(patchConfig); - if (!data.hasAnyFixes()) { + MigrationProfile profile = Patch.createMigrationData(patchConfig); + + if (!profile.hasAnyFixes()) { LOGGER.info("Everything up to date"); - return; - } + return null; + } + + return profile; + } + + static void showBackupWarning(String levelID, Consumer whenFinished){ + TranslatableComponent promptText = new TranslatableComponent("bclib.datafixer.backupWarning.prompt"); + TranslatableComponent buttonTitle = new TranslatableComponent("bclib.datafixer.backupWarning.button"); + + Minecraft.getInstance().setScreen(new ConfirmFixScreen((Screen) null, (createBackup, applyFixes) -> { + if (createBackup) { + EditWorldScreen.makeBackupAndShowToast(Minecraft.getInstance().getLevelSource(), levelID); + } + + Minecraft.getInstance().setScreen((Screen)null); + whenFinished.accept(applyFixes); + })); + + } + private static void runDataFixes(File dir, MigrationProfile profile, ProgressListener progress) { + System.out.println("RUNNING fixes"); + if (dir!= null) return; + State state = new State(); List regions = getAllRegions(dir, null); - regions.parallelStream().forEach((file) -> fixRegion(data, state, file)); + progress.progressStagePercentage(0); + int[] count = {0}; + regions.parallelStream().forEach((file) -> { + fixRegion(profile, state, file); + count[0]++; + progress.progressStagePercentage((100 * count[0])/regions.size()); + }); + progress.stop(); List players = getAllPlayers(dir); - players.parallelStream().forEach((file) -> fixPlayer(data, state, file)); + players.parallelStream().forEach((file) -> fixPlayer(profile, state, file)); - fixLevel(data, state, new File(dir, "level.dat")); + fixLevel(profile, state, new File(dir, "level.dat")); if (!state.didFail) { - data.markApplied(); + profile.markApplied(); WorldDataAPI.saveFile(BCLib.MOD_ID); } } + private static void fixLevel(MigrationProfile data, State state, File file) { try { LOGGER.info("Inspecting " + file); @@ -121,6 +277,7 @@ public class DataFixerAPI { LOGGER.info("Inspecting " + file); boolean[] changed = new boolean[1]; RegionFile region = new RegionFile(file, file.getParentFile(), true); + for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { ChunkPos pos = new ChunkPos(x, z); diff --git a/src/main/java/ru/bclib/complexmaterials/ComplexMaterial.java b/src/main/java/ru/bclib/complexmaterials/ComplexMaterial.java index 9cfb2d2b..b5fceada 100644 --- a/src/main/java/ru/bclib/complexmaterials/ComplexMaterial.java +++ b/src/main/java/ru/bclib/complexmaterials/ComplexMaterial.java @@ -36,8 +36,8 @@ public abstract class ComplexMaterial { private final Map blocks = Maps.newHashMap(); private final Map items = Maps.newHashMap(); - private final String baseName; - private final String modID; + protected final String baseName; + protected final String modID; public ComplexMaterial(String modID, String baseName) { this.baseName = baseName; diff --git a/src/main/java/ru/bclib/complexmaterials/WoodenComplexMaterial.java b/src/main/java/ru/bclib/complexmaterials/WoodenComplexMaterial.java index d51c8a3b..a4d9abe4 100644 --- a/src/main/java/ru/bclib/complexmaterials/WoodenComplexMaterial.java +++ b/src/main/java/ru/bclib/complexmaterials/WoodenComplexMaterial.java @@ -41,6 +41,7 @@ import ru.bclib.complexmaterials.entry.RecipeEntry; import ru.bclib.config.PathConfig; import ru.bclib.recipes.GridRecipe; +import java.util.Arrays; import java.util.List; public class WoodenComplexMaterial extends ComplexMaterial { @@ -96,8 +97,14 @@ public class WoodenComplexMaterial extends ComplexMaterial { @Override protected void initDefault(FabricBlockSettings blockSettings, FabricItemSettings itemSettings) { + initDefault(blockSettings, itemSettings, new String[0]); + } + + final protected void initDefault(FabricBlockSettings blockSettings, FabricItemSettings itemSettings, String[] excludedSuffixes) { Tag.Named tagBlockLog = getBlockTag(TAG_LOGS); Tag.Named tagItemLog = getItemTag(TAG_LOGS); + List excl = Arrays.asList(excludedSuffixes); + addBlockEntry( new BlockEntry(BLOCK_STRIPPED_LOG, (complexMaterial, settings) -> { @@ -173,12 +180,18 @@ public class WoodenComplexMaterial extends ComplexMaterial { addBlockEntry(new BlockEntry(BLOCK_BARREL, (complexMaterial, settings) -> { return new BaseBarrelBlock(getBlock(BLOCK_PLANKS)); })); - addBlockEntry(new BlockEntry(BLOCK_BOOKSHELF, (complexMaterial, settings) -> { - return new BaseBookshelfBlock(getBlock(BLOCK_PLANKS)); - }).setBlockTags(TagAPI.BLOCK_BOOKSHELVES)); - addBlockEntry(new BlockEntry(BLOCK_COMPOSTER, (complexMaterial, settings) -> { - return new BaseComposterBlock(getBlock(BLOCK_PLANKS)); - })); + + if (!excl.contains(BLOCK_BOOKSHELF)) { + addBlockEntry(new BlockEntry(BLOCK_BOOKSHELF, (complexMaterial, settings) -> { + return new BaseBookshelfBlock(getBlock(BLOCK_PLANKS)); + }).setBlockTags(TagAPI.BLOCK_BOOKSHELVES)); + } + + if (!excl.contains(BLOCK_COMPOSTER)) { + addBlockEntry(new BlockEntry(BLOCK_COMPOSTER, (complexMaterial, settings) -> { + return new BaseComposterBlock(getBlock(BLOCK_PLANKS)); + })); + } } @Override diff --git a/src/main/java/ru/bclib/gui/screens/ConfirmFixScreen.java b/src/main/java/ru/bclib/gui/screens/ConfirmFixScreen.java new file mode 100644 index 00000000..54ebbbc5 --- /dev/null +++ b/src/main/java/ru/bclib/gui/screens/ConfirmFixScreen.java @@ -0,0 +1,132 @@ +package ru.bclib.gui.screens; + + + import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.MultiLineLabel; +import net.minecraft.client.gui.screens.BackupConfirmScreen; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.CommonComponents; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import org.jetbrains.annotations.Nullable; +import ru.bclib.BCLib; + +import java.util.Objects; +class ColoredButton extends Button { + private final float r; + private final float g; + private final float b; + public ColoredButton(int x, int y, int width, int height, Component component, OnPress onPress, float r, float g, float b) { + super(x, y, width, height, component, onPress); + this.r = r; + this.g = g; + this.b = b; + } + + @Override + public void renderButton(PoseStack poseStack, int i, int j, float f) { + Minecraft minecraft = Minecraft.getInstance(); + Font font = minecraft.font; + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderTexture(0, WIDGETS_LOCATION); + RenderSystem.setShaderColor(r, g, b, this.alpha); + int k = this.getYImage(this.isHovered()); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.enableDepthTest(); + this.blit(poseStack, this.x, this.y, 0, 46 + k * 20, this.width / 2, this.height); + this.blit(poseStack, this.x + this.width / 2, this.y, 200 - this.width / 2, 46 + k * 20, this.width / 2, this.height); + this.renderBg(poseStack, minecraft, i, j); + int l = this.active ? 16777215 : 10526880; + drawCenteredString(poseStack, font, this.getMessage(), this.x + this.width / 2, this.y + (this.height - 8) / 2, l | Mth.ceil(this.alpha * 255.0F) << 24); + } +} + +@Environment(EnvType.CLIENT) +public class ConfirmFixScreen extends Screen { + static final ResourceLocation BCLIB_LOGO_LOCATION = new ResourceLocation(BCLib.MOD_ID, + "icon.png"); + @Nullable + private final Screen lastScreen; + protected final BackupConfirmScreen.Listener listener; + private final Component description; + private MultiLineLabel message; + protected int id; + + public ConfirmFixScreen(@Nullable Screen screen, BackupConfirmScreen.Listener listener) { + super(new TranslatableComponent("bclib.datafixer.backupWarning.title")); + this.message = MultiLineLabel.EMPTY; + this.lastScreen = screen; + this.listener = listener; + this.description = new TranslatableComponent("bclib.datafixer.backupWarning.message"); + } + + protected void init() { + super.init(); + this.message = MultiLineLabel.create(this.font, this.description, this.width - 50); + int promptLines = this.message.getLineCount() + 1; + Objects.requireNonNull(this.font); + int height = promptLines * 9; + final int BUTTON_WIDTH = 150; + final int BUTTON_SPACE = 10; + final int BUTTON_HEIGHT = 20; + + Button customButton = new Button((this.width -BUTTON_WIDTH)/2, 100 + height, BUTTON_WIDTH, BUTTON_HEIGHT, new TranslatableComponent("bclib.datafixer.backupWarning.backup"), (button) -> { + this.listener.proceed(true, true); + }); + this.addRenderableWidget(customButton); + + + customButton = new Button((this.width - 2*BUTTON_WIDTH - BUTTON_SPACE)/ 2 , 124 + height, BUTTON_WIDTH, BUTTON_HEIGHT, CommonComponents.GUI_CANCEL, (button) -> { + this.minecraft.setScreen(this.lastScreen); + }); + this.addRenderableWidget(customButton); + + customButton = new Button((this.width - 2*BUTTON_WIDTH - BUTTON_SPACE)/ 2 + BUTTON_WIDTH+BUTTON_SPACE, 124 + height, BUTTON_WIDTH, BUTTON_HEIGHT, new TranslatableComponent("bclib.datafixer.backupWarning.continue"), (button) -> { + this.listener.proceed(false, true); + }); + customButton.setAlpha(0.5f); + this.addRenderableWidget(customButton); + + + customButton = new Button((this.width - BUTTON_WIDTH)/ 2, 148 + height, BUTTON_WIDTH, BUTTON_HEIGHT, new TranslatableComponent("bclib.datafixer.backupWarning.nofixes"), (button) -> { + this.listener.proceed(false, false); + }); + customButton.setAlpha(0.5f); + this.addRenderableWidget(customButton); + } + + public void render(PoseStack poseStack, int i, int j, float f) { + this.renderBackground(poseStack); + drawCenteredString(poseStack, this.font, this.title, this.width / 2, 50, 16777215); + this.message.renderCentered(poseStack, this.width / 2, 70); + super.render(poseStack, i, j, f); + } + + public boolean shouldCloseOnEsc() { + return false; + } + + public boolean keyPressed(int i, int j, int k) { + if (i == 256) { + this.minecraft.setScreen(this.lastScreen); + return true; + } else { + return super.keyPressed(i, j, k); + } + } + + @Environment(EnvType.CLIENT) + public interface Listener { + void proceed(boolean bl, boolean bl2); + } +} diff --git a/src/main/java/ru/bclib/mixin/client/MinecraftMixin.java b/src/main/java/ru/bclib/mixin/client/MinecraftMixin.java index 4633efbe..9970d91f 100644 --- a/src/main/java/ru/bclib/mixin/client/MinecraftMixin.java +++ b/src/main/java/ru/bclib/mixin/client/MinecraftMixin.java @@ -5,16 +5,19 @@ 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.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 ru.bclib.api.datafixer.DataFixerAPI; import ru.bclib.interfaces.CustomColorProvider; @Mixin(Minecraft.class) -public class MinecraftMixin { +public abstract class MinecraftMixin { @Final @Shadow private BlockColors blockColors; @@ -33,4 +36,51 @@ public class MinecraftMixin { } }); } + + + @Shadow @Final private LevelStorageSource levelSource; + @Shadow public abstract void loadLevel(String string); + private final String BCLIB_RECURSION = "$@BCLIB:"; + + @Inject(method="loadLevel", cancellable = true, at=@At("HEAD")) + private void bclib_callFixerOnLoad(String levelID, CallbackInfo ci){ + boolean recursiveCall = false; + if (levelID.startsWith(BCLIB_RECURSION)) { + levelID = levelID.substring(BCLIB_RECURSION.length()); + recursiveCall = true; + } + + final String recursiveLevelID = BCLIB_RECURSION + levelID; + if (!recursiveCall && DataFixerAPI.fixData(this.levelSource, levelID, true, (appliedFixes)->{ + this.loadLevel(recursiveLevelID); + })){ + ci.cancel(); + } + } + + @ModifyArg(method="loadLevel", at=@At(value="INVOKE", target="Lnet/minecraft/client/Minecraft;doLoadLevel(Ljava/lang/String;Lnet/minecraft/core/RegistryAccess$RegistryHolder;Ljava/util/function/Function;Lcom/mojang/datafixers/util/Function4;ZLnet/minecraft/client/Minecraft$ExperimentalDialogType;)V")) + private String bclib_correctLevelID(String levelID){ + if (levelID.startsWith(BCLIB_RECURSION)) { + levelID = levelID.substring(BCLIB_RECURSION.length()); + } + + return levelID; + } + + +// @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( +// String string, +// RegistryHolder registryHolder, +// Function function, +// Function4 function4, +// boolean bl, +// ExperimentalDialogType experimentalDialogType, +// CallbackInfo ci, +// LevelStorageSource.LevelStorageAccess levelStorageAccess) { +// +// DataFixerAPI.fixData(levelStorageAccess); +// ci.cancel(); +// +// } } diff --git a/src/main/java/ru/bclib/mixin/common/MinecraftServerMixin.java b/src/main/java/ru/bclib/mixin/common/MinecraftServerMixin.java index 52495509..0fea24a6 100644 --- a/src/main/java/ru/bclib/mixin/common/MinecraftServerMixin.java +++ b/src/main/java/ru/bclib/mixin/common/MinecraftServerMixin.java @@ -6,7 +6,6 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerResources; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; -import net.minecraft.world.level.storage.LevelResource; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.WorldData; import org.spongepowered.asm.mixin.Final; @@ -17,11 +16,8 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import ru.bclib.api.BiomeAPI; -import ru.bclib.api.WorldDataAPI; -import ru.bclib.api.datafixer.DataFixerAPI; import ru.bclib.recipes.BCLRecipeManager; -import java.io.File; import java.util.Collection; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -38,12 +34,14 @@ public class MinecraftServerMixin { @Final @Shadow protected WorldData worldData; + @Inject(method="convertFromRegionFormatIfNeeded", at = @At("HEAD")) private static void bclib_applyPatches(LevelStorageSource.LevelStorageAccess session, CallbackInfo ci){ - File levelPath = session.getLevelPath(LevelResource.ROOT).toFile(); + + /*File levelPath = session.getLevelPath(LevelResource.ROOT).toFile(); WorldDataAPI.load(new File(levelPath, "data")); - DataFixerAPI.fixData(levelPath); + DataFixerAPI.fixData(levelPath, session.getLevelId());*/ } diff --git a/src/main/resources/assets/bclib/lang/de_de.json b/src/main/resources/assets/bclib/lang/de_de.json new file mode 100644 index 00000000..ff0aef35 --- /dev/null +++ b/src/main/resources/assets/bclib/lang/de_de.json @@ -0,0 +1,3 @@ +{ + "message.bclib.anvil_damage": "§cSchaden" +} \ No newline at end of file diff --git a/src/main/resources/assets/bclib/lang/en_us.json b/src/main/resources/assets/bclib/lang/en_us.json index b89551db..6df95514 100644 --- a/src/main/resources/assets/bclib/lang/en_us.json +++ b/src/main/resources/assets/bclib/lang/en_us.json @@ -1,3 +1,8 @@ { - "message.bclib.anvil_damage": "§cDamage" + "message.bclib.anvil_damage": "§cDamage", + "bclib.datafixer.backupWarning.title": "Guardian detected an incompatible World", + "bclib.datafixer.backupWarning.message": "The Guardian detected, that the internals of the following Mods did change since this world was last played.\n\nWe can automatically change the world for you. If you continue without applying the changes the world may not load correct. Before you continue, you should create a Backup.", + "bclib.datafixer.backupWarning.backup": "Create Backup and Continue", + "bclib.datafixer.backupWarning.nofixes": "Continue Without Fixes", + "bclib.datafixer.backupWarning.continue": "Continue Without Backup" } \ No newline at end of file From d3db623de59883ff905d236600c1adda1466f8e6 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Mon, 26 Jul 2021 18:50:15 +0200 Subject: [PATCH 2/3] Call DataFixer in Server Context --- .../java/ru/bclib/mixin/common/MainMixin.java | 17 +++++++++++++++++ src/main/resources/bclib.mixins.common.json | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ru/bclib/mixin/common/MainMixin.java diff --git a/src/main/java/ru/bclib/mixin/common/MainMixin.java b/src/main/java/ru/bclib/mixin/common/MainMixin.java new file mode 100644 index 00000000..bb39c129 --- /dev/null +++ b/src/main/java/ru/bclib/mixin/common/MainMixin.java @@ -0,0 +1,17 @@ +package ru.bclib.mixin.common; + +import net.minecraft.server.Main; +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 ru.bclib.api.datafixer.DataFixerAPI; + +@Mixin(Main.class) +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)->{}); + return session; + } +} diff --git a/src/main/resources/bclib.mixins.common.json b/src/main/resources/bclib.mixins.common.json index 5ea05c8b..ecf7c440 100644 --- a/src/main/resources/bclib.mixins.common.json +++ b/src/main/resources/bclib.mixins.common.json @@ -15,7 +15,8 @@ "AnvilBlockMixin", "AnvilMenuMixin", "TagLoaderMixin", - "BiomeMixin" + "BiomeMixin", + "MainMixin" ], "injectors": { "defaultRequire": 1 From c4d7035ef2c6abb45efd11d6e6f23bfe02e87b41 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Mon, 26 Jul 2021 18:52:05 +0200 Subject: [PATCH 3/3] Move method to client Environment --- src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java b/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java index 86df45bb..258747a7 100644 --- a/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java +++ b/src/main/java/ru/bclib/api/datafixer/DataFixerAPI.java @@ -1,5 +1,7 @@ package ru.bclib.api.datafixer; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.toasts.SystemToast; @@ -168,6 +170,7 @@ public class DataFixerAPI { return profile; } + @Environment(EnvType.CLIENT) static void showBackupWarning(String levelID, Consumer whenFinished){ TranslatableComponent promptText = new TranslatableComponent("bclib.datafixer.backupWarning.prompt"); TranslatableComponent buttonTitle = new TranslatableComponent("bclib.datafixer.backupWarning.button");