diff --git a/platforms/common/src/main/java/dynamic_fps/impl/config/DynamicFPSConfig.java b/platforms/common/src/main/java/dynamic_fps/impl/config/DynamicFPSConfig.java index 21d1c64e..51b3e0e9 100644 --- a/platforms/common/src/main/java/dynamic_fps/impl/config/DynamicFPSConfig.java +++ b/platforms/common/src/main/java/dynamic_fps/impl/config/DynamicFPSConfig.java @@ -14,7 +14,7 @@ public final class DynamicFPSConfig { @SerializedName("states") private final Map configs; - DynamicFPSConfig(boolean enabled, int abandonTime, boolean uncapMenuFrameRate, Map configs) { + private DynamicFPSConfig(boolean enabled, int abandonTime, boolean uncapMenuFrameRate, Map configs) { this.enabled = enabled; this.idleTime = abandonTime; this.uncapMenuFrameRate = uncapMenuFrameRate; @@ -28,6 +28,18 @@ public final class DynamicFPSConfig { } } + public static DynamicFPSConfig createDefault() { + DynamicFPSConfig instance = new DynamicFPSConfig( + true, + 0, + false, + new EnumMap<>(PowerState.class) + ); + + instance.save(); + return instance; + } + public Config get(PowerState state) { if (state == PowerState.FOCUSED) { return Config.ACTIVE; diff --git a/platforms/common/src/main/java/dynamic_fps/impl/config/Serialization.java b/platforms/common/src/main/java/dynamic_fps/impl/config/Serialization.java index bd38ab82..98bed957 100644 --- a/platforms/common/src/main/java/dynamic_fps/impl/config/Serialization.java +++ b/platforms/common/src/main/java/dynamic_fps/impl/config/Serialization.java @@ -16,7 +16,7 @@ import dynamic_fps.impl.GraphicsState; import dynamic_fps.impl.PowerState; import dynamic_fps.impl.service.Platform; -import net.minecraft.sounds.SoundSource; +import dynamic_fps.impl.util.Logging; import org.jetbrains.annotations.Nullable; import java.io.IOException; @@ -26,7 +26,6 @@ import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.util.EnumMap; import java.util.Locale; import java.util.Map; @@ -53,13 +52,21 @@ public static void save(DynamicFPSConfig instance) { Path temp = Files.createTempFile(cache, "config", ".json"); Files.write(temp, data.getBytes(StandardCharsets.UTF_8)); - Files.move(temp, config, StandardCopyOption.ATOMIC_MOVE); + Serialization.move(temp, config); // Attempt atomic move, fall back otherwise. } catch (IOException e) { // Cloth Config's built-in saving does not support catching exceptions :( throw new RuntimeException("Failed to save or modify Dynamic FPS config!", e); } } + private static void move(Path from, Path to) throws IOException { + try { + Files.move(from, to, StandardCopyOption.ATOMIC_MOVE); + } catch (IOException | UnsupportedOperationException e) { + Files.move(from, to, StandardCopyOption.REPLACE_EXISTING); + } + } + @SuppressWarnings("deprecation") public static DynamicFPSConfig load() { byte[] data; @@ -68,18 +75,18 @@ public static DynamicFPSConfig load() { try { data = Files.readAllBytes(config); } catch (NoSuchFileException e) { - DynamicFPSConfig instance = new DynamicFPSConfig( - true, - 0, - false, - new EnumMap<>(PowerState.class) - ); - instance.save(); - return instance; + return DynamicFPSConfig.createDefault(); } catch (IOException e) { throw new RuntimeException("Failed to load Dynamic FPS config.", e); } + // Sometimes when the config failed to save properly it'll end up being only null bytes. + // Since most users don't seem to know how to deal with this we'll just replace the config. + if (data[0] == 0) { + Logging.getLogger().warn("Dynamic FPS config corrupted! Recreating from defaults ..."); + return DynamicFPSConfig.createDefault(); + } + JsonElement root = new JsonParser().parse(new String(data, StandardCharsets.UTF_8)); upgradeConfig((JsonObject) root);