diff --git a/src/main/java/me/jellysquid/mods/sodium/client/SodiumClientMod.java b/src/main/java/me/jellysquid/mods/sodium/client/SodiumClientMod.java index 0c035012a6..e1ebe98f06 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/SodiumClientMod.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/SodiumClientMod.java @@ -1,10 +1,13 @@ package me.jellysquid.mods.sodium.client; import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions; +import me.jellysquid.mods.sodium.client.gui.console.Console; +import me.jellysquid.mods.sodium.client.gui.console.message.MessageLevel; import me.jellysquid.mods.sodium.client.util.FlawlessFrames; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; +import net.minecraft.text.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,12 +53,14 @@ public static Logger logger() { private static SodiumGameOptions loadConfig() { try { - return SodiumGameOptions.load(); + return SodiumGameOptions.loadFromDisk(); } catch (Exception e) { LOGGER.error("Failed to load configuration file", e); LOGGER.error("Using default configuration file in read-only mode"); - var config = new SodiumGameOptions(); + Console.instance().logMessage(MessageLevel.SEVERE, Text.translatable("sodium.console.config_not_loaded"), 12.5); + + var config = SodiumGameOptions.defaults(); config.setReadOnly(); return config; @@ -66,7 +71,7 @@ public static void restoreDefaultOptions() { CONFIG = SodiumGameOptions.defaults(); try { - CONFIG.writeChanges(); + SodiumGameOptions.writeToDisk(CONFIG); } catch (IOException e) { throw new RuntimeException("Failed to write config file", e); } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptions.java b/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptions.java index 1304b6163f..12a19f2405 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptions.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumGameOptions.java @@ -26,13 +26,12 @@ public class SodiumGameOptions { private boolean readOnly; - private Path configPath; + private SodiumGameOptions() { + // NO-OP + } public static SodiumGameOptions defaults() { - var options = new SodiumGameOptions(); - options.configPath = getConfigPath(DEFAULT_FILE_NAME); - - return options; + return new SodiumGameOptions(); } public static class PerformanceSettings { @@ -92,12 +91,8 @@ public boolean isFancy(GraphicsMode graphicsMode) { .excludeFieldsWithModifiers(Modifier.PRIVATE) .create(); - public static SodiumGameOptions load() { - return load(DEFAULT_FILE_NAME); - } - - public static SodiumGameOptions load(String name) { - Path path = getConfigPath(name); + public static SodiumGameOptions loadFromDisk() { + Path path = getConfigPath(); SodiumGameOptions config; if (Files.exists(path)) { @@ -110,10 +105,8 @@ public static SodiumGameOptions load(String name) { config = new SodiumGameOptions(); } - config.configPath = path; - try { - config.writeChanges(); + writeToDisk(config); } catch (IOException e) { throw new RuntimeException("Couldn't update config file", e); } @@ -121,18 +114,19 @@ public static SodiumGameOptions load(String name) { return config; } - private static Path getConfigPath(String name) { + private static Path getConfigPath() { return FabricLoader.getInstance() .getConfigDir() - .resolve(name); + .resolve(DEFAULT_FILE_NAME); } - public void writeChanges() throws IOException { - if (this.isReadOnly()) { + public static void writeToDisk(SodiumGameOptions config) throws IOException { + if (config.isReadOnly()) { throw new IllegalStateException("Config file is read-only"); } - Path dir = this.configPath.getParent(); + Path path = getConfigPath(); + Path dir = path.getParent(); if (!Files.exists(dir)) { Files.createDirectories(dir); @@ -141,13 +135,13 @@ public void writeChanges() throws IOException { } // Use a temporary location next to the config's final destination - Path tempPath = this.configPath.resolveSibling(this.configPath.getFileName() + ".tmp"); + Path tempPath = path.resolveSibling(path.getFileName() + ".tmp"); // Write the file to our temporary location - Files.writeString(tempPath, GSON.toJson(this)); + Files.writeString(tempPath, GSON.toJson(config)); // Atomically replace the old config file (if it exists) with the temporary file - Files.move(tempPath, this.configPath, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); + Files.move(tempPath, path, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); } public boolean isReadOnly() { @@ -157,8 +151,4 @@ public boolean isReadOnly() { public void setReadOnly() { this.readOnly = true; } - - public String getFileName() { - return this.configPath.getFileName().toString(); - } } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumOptionsGUI.java b/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumOptionsGUI.java index f140cf1323..acce4e14a9 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumOptionsGUI.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gui/SodiumOptionsGUI.java @@ -7,15 +7,14 @@ import me.jellysquid.mods.sodium.client.gui.options.control.Control; import me.jellysquid.mods.sodium.client.gui.options.control.ControlElement; import me.jellysquid.mods.sodium.client.gui.options.storage.OptionStorage; +import me.jellysquid.mods.sodium.client.gui.screen.ConfigCorruptedScreen; import me.jellysquid.mods.sodium.client.gui.widgets.FlatButtonWidget; import me.jellysquid.mods.sodium.client.util.Dim2i; -import net.caffeinemc.mods.sodium.api.util.ColorMixer; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.option.VideoOptionsScreen; import net.minecraft.text.OrderedText; -import net.minecraft.text.Style; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import net.minecraft.util.Language; @@ -44,8 +43,8 @@ public class SodiumOptionsGUI extends Screen { private boolean hasPendingChanges; private ControlElement hoveredElement; - public SodiumOptionsGUI(Screen prevScreen) { - super(Text.translatable("Sodium Options")); + private SodiumOptionsGUI(Screen prevScreen) { + super(Text.literal("Sodium Renderer Settings")); this.prevScreen = prevScreen; @@ -55,6 +54,14 @@ public SodiumOptionsGUI(Screen prevScreen) { this.pages.add(SodiumGameOptionPages.advanced()); } + public static Screen createScreen(Screen currentScreen) { + if (SodiumClientMod.options().isReadOnly()) { + return new ConfigCorruptedScreen(currentScreen, SodiumOptionsGUI::new); + } else { + return new SodiumOptionsGUI(currentScreen); + } + } + public void setPage(OptionPage page) { this.currentPage = page; @@ -112,7 +119,7 @@ private void hideDonationButton() { options.notifications.hideDonationButton = true; try { - options.writeChanges(); + SodiumGameOptions.writeToDisk(options); } catch (IOException e) { throw new RuntimeException("Failed to save configuration", e); } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gui/options/storage/SodiumOptionsStorage.java b/src/main/java/me/jellysquid/mods/sodium/client/gui/options/storage/SodiumOptionsStorage.java index ed0ea97a0a..063dd19416 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gui/options/storage/SodiumOptionsStorage.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gui/options/storage/SodiumOptionsStorage.java @@ -20,7 +20,7 @@ public SodiumGameOptions getData() { @Override public void save() { try { - this.options.writeChanges(); + SodiumGameOptions.writeToDisk(this.options); } catch (IOException e) { throw new RuntimeException("Couldn't save configuration changes", e); } diff --git a/src/main/java/me/jellysquid/mods/sodium/client/gui/screen/ConfigCorruptedScreen.java b/src/main/java/me/jellysquid/mods/sodium/client/gui/screen/ConfigCorruptedScreen.java index 653b275e01..b5c6afa916 100644 --- a/src/main/java/me/jellysquid/mods/sodium/client/gui/screen/ConfigCorruptedScreen.java +++ b/src/main/java/me/jellysquid/mods/sodium/client/gui/screen/ConfigCorruptedScreen.java @@ -1,14 +1,18 @@ package me.jellysquid.mods.sodium.client.gui.screen; import me.jellysquid.mods.sodium.client.SodiumClientMod; +import me.jellysquid.mods.sodium.client.gui.console.Console; +import me.jellysquid.mods.sodium.client.gui.console.message.MessageLevel; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.List; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -18,9 +22,9 @@ public class ConfigCorruptedScreen extends Screen { can happen when the file has been corrupted on disk, or when trying to manually edit the file by hand. - We can attempt to fix this problem automatically by restoring the - config file back to known-good defaults, but you will lose any - changes that have since been made to your video settings. + If you continue, the configuration file will be reset back to known-good + defaults, and you will lose any changes that have since been made to your + Video Settings. More information about the error can be found in the log file. """; @@ -29,29 +33,37 @@ public class ConfigCorruptedScreen extends Screen { .map(Text::literal) .collect(Collectors.toList()); - private static final Text TEXT_BUTTON_RESTORE_DEFAULTS = Text.literal("Restore defaults"); - private static final Text TEXT_BUTTON_CLOSE_GAME = Text.literal("Close game"); + private static final int BUTTON_WIDTH = 140; + private static final int BUTTON_HEIGHT = 20; - private final Supplier child; + private static final int SCREEN_PADDING = 32; - public ConfigCorruptedScreen(Supplier child) { - super(Text.literal("Config corruption detected")); + private final @Nullable Screen prevScreen; + private final Function nextScreen; - this.child = child; + public ConfigCorruptedScreen(@Nullable Screen prevScreen, @Nullable Function nextScreen) { + super(Text.literal("Sodium failed to load the configuration file")); + + this.prevScreen = prevScreen; + this.nextScreen = nextScreen; } @Override protected void init() { super.init(); - this.addDrawableChild(ButtonWidget.builder(TEXT_BUTTON_RESTORE_DEFAULTS, (btn) -> { + int buttonY = this.height - SCREEN_PADDING - BUTTON_HEIGHT; + + this.addDrawableChild(ButtonWidget.builder(Text.literal("Continue"), (btn) -> { + Console.instance().logMessage(MessageLevel.INFO, Text.translatable("sodium.console.config_file_was_reset"), 3.0); + SodiumClientMod.restoreDefaultOptions(); - MinecraftClient.getInstance().setScreen(this.child.get()); - }).dimensions(32, this.height - 40, 174, 20).build()); + MinecraftClient.getInstance().setScreen(this.nextScreen.apply(this.prevScreen)); + }).dimensions(this.width - SCREEN_PADDING - BUTTON_WIDTH, buttonY, BUTTON_WIDTH, BUTTON_HEIGHT).build()); - this.addDrawableChild(ButtonWidget.builder(TEXT_BUTTON_CLOSE_GAME, (btn) -> { - MinecraftClient.getInstance().scheduleStop(); - }).dimensions(this.width - 174 - 32, this.height - 40, 174, 20).build()); + this.addDrawableChild(ButtonWidget.builder(Text.literal("Go back"), (btn) -> { + MinecraftClient.getInstance().setScreen(this.prevScreen); + }).dimensions(SCREEN_PADDING, buttonY, BUTTON_WIDTH, BUTTON_HEIGHT).build()); } @Override @@ -59,7 +71,7 @@ public void render(DrawContext drawContext, int mouseX, int mouseY, float delta) super.render(drawContext, mouseX, mouseY, delta); drawContext.drawTextWithShadow(this.textRenderer, Text.literal("Sodium Renderer"), 32, 32, 0xffffff); - drawContext.drawTextWithShadow(this.textRenderer, Text.literal("Could not load configuration file"), 32, 48, 0xff0000); + drawContext.drawTextWithShadow(this.textRenderer, Text.literal("Could not load the configuration file"), 32, 48, 0xff0000); for (int i = 0; i < TEXT_BODY.size(); i++) { if (TEXT_BODY.get(i).getString().isEmpty()) { diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/core/MinecraftClientMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/core/MinecraftClientMixin.java index 6c7f1b3e38..7861b371a9 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/core/MinecraftClientMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/core/MinecraftClientMixin.java @@ -27,14 +27,6 @@ public class MinecraftClientMixin { @Unique private final LongArrayFIFOQueue fences = new LongArrayFIFOQueue(); - @Inject(method = "", at = @At("RETURN")) - private void postInit(RunArgs args, CallbackInfo ci) { - if (SodiumClientMod.options().isReadOnly()) { - var parent = MinecraftClient.getInstance().currentScreen; - MinecraftClient.getInstance().setScreen(new ConfigCorruptedScreen(() -> parent)); - } - } - /** * We run this at the beginning of the frame (except for the first frame) to give the previous frame plenty of time * to render on the GPU. This allows us to stall on ClientWaitSync for less time. diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/features/gui/hooks/settings/OptionsScreenMixin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/features/gui/hooks/settings/OptionsScreenMixin.java index 48458e2886..330c06b9b1 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/features/gui/hooks/settings/OptionsScreenMixin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/features/gui/hooks/settings/OptionsScreenMixin.java @@ -19,6 +19,6 @@ protected OptionsScreenMixin(Text title) { @Dynamic @Inject(method = "method_19828", at = @At("HEAD"), cancellable = true) private void open(CallbackInfoReturnable ci) { - ci.setReturnValue(new SodiumOptionsGUI(this)); + ci.setReturnValue(SodiumOptionsGUI.createScreen(this)); } } diff --git a/src/main/resources/assets/sodium/lang/en_us.json b/src/main/resources/assets/sodium/lang/en_us.json index c022977adb..888b5634e7 100644 --- a/src/main/resources/assets/sodium/lang/en_us.json +++ b/src/main/resources/assets/sodium/lang/en_us.json @@ -59,5 +59,7 @@ "sodium.console.pojav_launcher": "PojavLauncher is not supported when using Sodium.\n * You are very likely to run into extreme performance issues, graphical bugs, and crashes.\n * You will be on your own if you decide to continue -- we will not help you with any bugs or crashes!", "sodium.console.core_shaders_error": "The following resource packs are incompatible with Sodium:", "sodium.console.core_shaders_warn": "The following resource packs may be incompatible with Sodium:", - "sodium.console.core_shaders_info": "Check the game log for detailed information." + "sodium.console.core_shaders_info": "Check the game log for detailed information.", + "sodium.console.config_not_loaded": "The configuration file for Sodium has been corrupted, or is currently unreadable. Some options have been temporarily reset to their defaults. Please open the Video Settings screen to resolve this problem.", + "sodium.console.config_file_was_reset": "The config file has been reset to known-good defaults." }