From 5f081d44124cc6566b1af53882c77beefe9fbaf8 Mon Sep 17 00:00:00 2001 From: LostLuma Date: Sat, 16 Dec 2023 12:17:16 +0100 Subject: [PATCH] Add idle detection and mode --- build.gradle | 4 + .../java/dynamic_fps/impl/DynamicFPSMod.java | 55 ++++++++++- .../java/dynamic_fps/impl/PowerState.java | 13 ++- .../dynamic_fps/impl/compat/ClothConfig.java | 27 ++++- .../impl/config/DynamicFPSConfig.java | 26 ++++- .../impl/mixin/MinecraftMixin.java | 13 +++ .../dynamic_fps/impl/mixin/WindowMixin.java | 12 --- .../impl/util/event/InputObserver.java | 99 +++++++++++++++++++ .../impl/util/{ => event}/WindowObserver.java | 20 ++-- .../assets/dynamic_fps/lang/de_at.json | 2 + .../assets/dynamic_fps/lang/de_ch.json | 2 + .../assets/dynamic_fps/lang/de_de.json | 2 + .../assets/dynamic_fps/lang/en_us.json | 6 ++ .../assets/dynamic_fps/lang/et_ee.json | 2 + .../assets/dynamic_fps/lang/fr_ca.json | 1 + .../assets/dynamic_fps/lang/fr_fr.json | 1 + .../assets/dynamic_fps/lang/it_it.json | 3 +- .../assets/dynamic_fps/lang/ko_kr.json | 2 + .../assets/dynamic_fps/lang/pl_pl.json | 2 + .../assets/dynamic_fps/lang/pt_br.json | 2 + .../assets/dynamic_fps/lang/pt_pt.json | 2 + .../assets/dynamic_fps/lang/ru_ru.json | 1 + .../assets/dynamic_fps/lang/sv_se.json | 5 +- .../assets/dynamic_fps/lang/tr_tr.json | 2 + .../assets/dynamic_fps/lang/uk_ua.json | 2 + .../assets/dynamic_fps/lang/vi_vn.json | 2 + .../assets/dynamic_fps/lang/zh_cn.json | 1 + .../assets/dynamic_fps/lang/zh_tw.json | 1 + src/main/resources/dynamic_fps.accesswidener | 3 + src/main/resources/fabric.mod.json | 1 + 30 files changed, 280 insertions(+), 34 deletions(-) create mode 100644 src/main/java/dynamic_fps/impl/util/event/InputObserver.java rename src/main/java/dynamic_fps/impl/util/{ => event}/WindowObserver.java (70%) create mode 100644 src/main/resources/dynamic_fps.accesswidener diff --git a/build.gradle b/build.gradle index 0fdcfdf6..06cab9b5 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,10 @@ dependencies { modApi libs.cloth.config } +loom { + accessWidenerPath = file("src/main/resources/dynamic_fps.accesswidener") +} + processResources { inputs.property "version", generateVersion() diff --git a/src/main/java/dynamic_fps/impl/DynamicFPSMod.java b/src/main/java/dynamic_fps/impl/DynamicFPSMod.java index f039aa1f..899e7f80 100644 --- a/src/main/java/dynamic_fps/impl/DynamicFPSMod.java +++ b/src/main/java/dynamic_fps/impl/DynamicFPSMod.java @@ -8,8 +8,10 @@ import dynamic_fps.impl.util.Logging; import dynamic_fps.impl.util.ModCompatibility; import dynamic_fps.impl.util.OptionsHolder; -import dynamic_fps.impl.util.WindowObserver; +import dynamic_fps.impl.util.event.InputObserver; +import dynamic_fps.impl.util.event.WindowObserver; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.Util; @@ -32,10 +34,15 @@ public class DynamicFPSMod implements ClientModInitializer { private static boolean isForcingLowFPS = false; private static Minecraft minecraft; + private static WindowObserver window; + private static InputObserver devices; private static long lastRender; + private static boolean wasIdle = false; + private static boolean tickEventRegistered = false; + // we always render one last frame before actually reducing FPS, so the hud text // shows up instantly when forcing low fps. // additionally, this would enable mods which render differently while mc is @@ -65,6 +72,7 @@ public void onInitializeClient() { toggleForcedKeyBinding.register(); toggleDisabledKeyBinding.register(); + registerTickEvent(); HudRenderCallback.EVENT.register(new HudInfoRenderer()); } @@ -78,6 +86,11 @@ public static void onStatusChanged() { checkForStateChanges(); } + public static void onConfigChanged() { + modConfig.save(); + registerTickEvent(); + } + public static PowerState powerState() { return state; } @@ -88,6 +101,7 @@ public static boolean isForcingLowFPS() { public static void setWindow(long address) { window = new WindowObserver(address); + devices = new InputObserver(address); } public static boolean checkForRender() { @@ -128,10 +142,43 @@ private static boolean isLevelCoveredByScreen() { return minecraft.screen != null && minecraft.screen.dynamic_fps$rendersBackground(); } + private static boolean isIdle() { + var idleTime = modConfig.idleTime(); + + if (idleTime == 0) { + return false; + } + + return (Util.getEpochMillis() - devices.lastActionTime()) >= idleTime * 1000; + } + private static boolean isLevelCoveredByOverlay() { return OVERLAY_OPTIMIZATION_ACTIVE && minecraft.getOverlay() instanceof LoadingOverlay loadingOverlay && loadingOverlay.dynamic_fps$isReloadComplete(); } + private static void registerTickEvent() { + if (tickEventRegistered) { + return; + } + + if (modConfig.idleTime() == -1) { + return; + } + + tickEventRegistered = true; + + // I assume world ticks only happen 20 times a second + // Instead of whatever amount of FPS we get at the moment? + ClientTickEvents.START_WORLD_TICK.register((minecraft) -> { + var idle = isIdle(); + + if (idle != wasIdle) { + wasIdle = idle; + onStatusChanged(); + } + }); + } + @SuppressWarnings("squid:S1215") // Garbage collector call public static void handleStateChange(PowerState previous, PowerState current) { if (DEBUG) { @@ -187,7 +234,11 @@ private static void checkForStateChanges0() { } else if (isForcingLowFPS) { current = PowerState.UNFOCUSED; } else if (window.isFocused()) { - current = PowerState.FOCUSED; + if (!isIdle()) { + current = PowerState.FOCUSED; + } else { + current = PowerState.ABANDONED; + } } else if (window.isHovered()) { current = PowerState.HOVERED; } else if (!window.isIconified()) { diff --git a/src/main/java/dynamic_fps/impl/PowerState.java b/src/main/java/dynamic_fps/impl/PowerState.java index 4b475cd2..fd1ad563 100644 --- a/src/main/java/dynamic_fps/impl/PowerState.java +++ b/src/main/java/dynamic_fps/impl/PowerState.java @@ -6,22 +6,27 @@ * Power states are prioritized based on their order here, see DynamicFPSMod.checkForStateChanges for impl details. */ public enum PowerState { - /* + /** * Window is currently focused. */ FOCUSED(false), - /* + /** * Mouse positioned over unfocused window. */ HOVERED(true), - /* + /** * Another application is focused. */ UNFOCUSED(true), - /* + /** + * User hasn't sent input for some time. + */ + ABANDONED(true), + + /** * Window minimized or otherwise hidden. */ INVISIBLE(true); diff --git a/src/main/java/dynamic_fps/impl/compat/ClothConfig.java b/src/main/java/dynamic_fps/impl/compat/ClothConfig.java index a47a3c84..38ea4362 100644 --- a/src/main/java/dynamic_fps/impl/compat/ClothConfig.java +++ b/src/main/java/dynamic_fps/impl/compat/ClothConfig.java @@ -20,10 +20,27 @@ public static Screen genConfigScreen(Screen parent) { var builder = ConfigBuilder.create() .setParentScreen(parent) .setTitle(localized("config", "title")) - .setSavingRunnable(DynamicFPSMod.modConfig::save); + .setSavingRunnable(DynamicFPSMod::onConfigChanged); var entryBuilder = builder.entryBuilder(); + var general = builder.getOrCreateCategory( + localized("config", "category.general") + ); + + general.addEntry( + entryBuilder.startIntSlider( + localized("config", "idle_time"), + DynamicFPSMod.modConfig.idleTime() / 60, + 0, 30 + ) + .setDefaultValue(0) + .setSaveConsumer(value -> DynamicFPSMod.modConfig.setIdleTime(value * 60)) + .setTextGetter(ClothConfig::idleTimeMessage) + .setTooltip(localized("config", "idle_time_tooltip")) + .build() + ); + for (var state : PowerState.values()) { if (!state.configurable) { continue; @@ -107,6 +124,14 @@ public static Screen genConfigScreen(Screen parent) { return builder.build(); } + private static Component idleTimeMessage(int value) { + if (value != 0) { + return Component.literal(Integer.toString(value)); + } else { + return localized("config", "idle_time_disabled"); + } + } + // Convert magic -1 number to 61 (and reverse) // So the "unlocked" FPS value is on the right private static int toConfigFpsTarget(int value) { diff --git a/src/main/java/dynamic_fps/impl/config/DynamicFPSConfig.java b/src/main/java/dynamic_fps/impl/config/DynamicFPSConfig.java index d55f27b4..421440a9 100644 --- a/src/main/java/dynamic_fps/impl/config/DynamicFPSConfig.java +++ b/src/main/java/dynamic_fps/impl/config/DynamicFPSConfig.java @@ -25,6 +25,7 @@ import net.minecraft.sounds.SoundSource; public final class DynamicFPSConfig { + private int idleTime; // Seconds private Map configs; private static final Path CONFIGS = FabricLoader.getInstance().getConfigDir(); @@ -33,10 +34,12 @@ public final class DynamicFPSConfig { private static final Codec> STATES_CODEC = Codec.unboundedMap(new EnumCodec<>(PowerState.values()), Config.CODEC); private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.intRange(0, 30 * 60).fieldOf("idle_time").forGetter(DynamicFPSConfig::idleTime), STATES_CODEC.fieldOf("states").forGetter(DynamicFPSConfig::configs) ).apply(instance, DynamicFPSConfig::new)); - private DynamicFPSConfig(Map configs) { + private DynamicFPSConfig(int abandonTime, Map configs) { + this.idleTime = abandonTime; this.configs = new EnumMap<>(configs); for (var state : PowerState.values()) { @@ -54,6 +57,14 @@ public Config get(PowerState state) { } } + public int idleTime() { + return this.idleTime; + } + + public void setIdleTime(int value) { + this.idleTime = value; + } + private Map configs() { return this.configs; } @@ -64,7 +75,7 @@ public static DynamicFPSConfig load() { try { data = Files.readString(CONFIG_FILE); } catch (NoSuchFileException e) { - var config = new DynamicFPSConfig(new EnumMap<>(PowerState.class)); + var config = new DynamicFPSConfig(0, new EnumMap<>(PowerState.class)); config.save(); return config; } catch (IOException e) { @@ -103,6 +114,9 @@ public static Config getDefaultConfig(PowerState state) { case UNFOCUSED: { return new Config(1, withMasterVolume(0.25f), GraphicsState.DEFAULT, false, false); } + case ABANDONED: { + return new Config(10, withMasterVolume(1.0f), GraphicsState.DEFAULT, false, false); + } case INVISIBLE: { return new Config(0, withMasterVolume(0.0f), GraphicsState.DEFAULT, false, false); } @@ -119,9 +133,17 @@ private static Map withMasterVolume(float value) { } private static void upgradeConfig(JsonObject root) { + upgradeIdleTime(root); upgradeVolumeMultiplier(root); } + private static void upgradeIdleTime(JsonObject root) { + // Add idle_time field if it's missing + if (!root.has("idle_time")) { + root.addProperty("idle_time", 0); + } + } + private static void upgradeVolumeMultiplier(JsonObject root) { // Convert each old power state config // - { "volume_multiplier": 0.0, ... } diff --git a/src/main/java/dynamic_fps/impl/mixin/MinecraftMixin.java b/src/main/java/dynamic_fps/impl/mixin/MinecraftMixin.java index 7f40e2db..0b5ab38b 100644 --- a/src/main/java/dynamic_fps/impl/mixin/MinecraftMixin.java +++ b/src/main/java/dynamic_fps/impl/mixin/MinecraftMixin.java @@ -1,15 +1,28 @@ package dynamic_fps.impl.mixin; +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.callback.CallbackInfo; +import com.mojang.blaze3d.platform.Window; + import dynamic_fps.impl.DynamicFPSMod; import net.minecraft.client.Minecraft; @Mixin(Minecraft.class) public class MinecraftMixin { + @Shadow + @Final + private Window window; + + @Inject(method = "", at = @At("TAIL")) + private void onInit(CallbackInfo callbackInfo) { + DynamicFPSMod.setWindow(this.window.window); + } + @Inject(method = "setScreen", at = @At("TAIL")) private void onSetScreen(CallbackInfo callbackInfo) { DynamicFPSMod.onStatusChanged(); diff --git a/src/main/java/dynamic_fps/impl/mixin/WindowMixin.java b/src/main/java/dynamic_fps/impl/mixin/WindowMixin.java index 1550fcf4..3663a496 100644 --- a/src/main/java/dynamic_fps/impl/mixin/WindowMixin.java +++ b/src/main/java/dynamic_fps/impl/mixin/WindowMixin.java @@ -1,11 +1,8 @@ package dynamic_fps.impl.mixin; -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.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import com.mojang.blaze3d.platform.Window; @@ -14,15 +11,6 @@ @Mixin(Window.class) public class WindowMixin { - @Shadow - @Final - private long window; - - @Inject(method = "", at = @At("TAIL")) - private void postinit(CallbackInfo callbackInfo) { - DynamicFPSMod.setWindow(this.window); - } - /** * Sets a frame rate limit while we're cancelling some or all rendering. */ diff --git a/src/main/java/dynamic_fps/impl/util/event/InputObserver.java b/src/main/java/dynamic_fps/impl/util/event/InputObserver.java new file mode 100644 index 00000000..3038c0b5 --- /dev/null +++ b/src/main/java/dynamic_fps/impl/util/event/InputObserver.java @@ -0,0 +1,99 @@ +package dynamic_fps.impl.util.event; + +import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWCharModsCallback; +import org.lwjgl.glfw.GLFWCursorPosCallback; +import org.lwjgl.glfw.GLFWDropCallback; +import org.lwjgl.glfw.GLFWKeyCallback; +import org.lwjgl.glfw.GLFWMouseButtonCallback; +import org.lwjgl.glfw.GLFWScrollCallback; + +import net.minecraft.Util; + +public class InputObserver { + private final long window; + + private long lastAction = Util.getEpochMillis(); + + // Keyboard + private final GLFWKeyCallback previousKeyCallback; + private final GLFWCharModsCallback previousCharModsCallback; + + // Mouse / Trackpad etc. + private final GLFWDropCallback previousDropCallback; + private final GLFWScrollCallback previousScrollCallback; + private final GLFWCursorPosCallback previousCursorPosCallback; + private final GLFWMouseButtonCallback previousMouseClickCallback; + + public InputObserver(long address) { + this.window = address; + + this.previousKeyCallback = GLFW.glfwSetKeyCallback(this.window, this::onKey); + this.previousCharModsCallback = GLFW.glfwSetCharModsCallback(this.window, this::onCharMods); + + this.previousDropCallback = GLFW.glfwSetDropCallback(this.window, this::onDrop); + this.previousScrollCallback = GLFW.glfwSetScrollCallback(this.window, this::onScroll); + this.previousCursorPosCallback = GLFW.glfwSetCursorPosCallback(this.window, this::onMove); + this.previousMouseClickCallback = GLFW.glfwSetMouseButtonCallback(this.window, this::onPress); + } + + public long lastActionTime() { + return this.lastAction; + } + + private void updateTime() { + this.lastAction = Util.getEpochMillis(); + } + + // Keyboard events + + private void onKey(long address, int key, int scancode, int action, int mods) { + this.updateTime(); + + if (this.previousKeyCallback != null) { + this.previousKeyCallback.invoke(address, key, scancode, action, mods); + } + } + + private void onCharMods(long address, int codepoint, int mods) { + this.updateTime(); + + if (this.previousCharModsCallback != null) { + this.previousCharModsCallback.invoke(address, codepoint, mods); + } + } + + // Mouse events + + private void onDrop(long address, int count, long names) { + this.updateTime(); + + if (this.previousDropCallback != null) { + this.previousDropCallback.invoke(address, count, names); + } + } + + private void onScroll(long address, double xoffset, double yoffset) { + this.updateTime(); + + if (this.previousScrollCallback != null) { + this.previousScrollCallback.invoke(address, xoffset, yoffset); + } + } + + private void onMove(long address, double x, double y) { + this.updateTime(); + + if (this.previousCursorPosCallback != null) { + this.previousCursorPosCallback.invoke(address, x, y); + } + } + + private void onPress(long address, int button, int action, int mods) { + this.updateTime(); + + if (this.previousMouseClickCallback != null) { + this.previousMouseClickCallback.invoke(address, button, action, mods); + } + } +} diff --git a/src/main/java/dynamic_fps/impl/util/WindowObserver.java b/src/main/java/dynamic_fps/impl/util/event/WindowObserver.java similarity index 70% rename from src/main/java/dynamic_fps/impl/util/WindowObserver.java rename to src/main/java/dynamic_fps/impl/util/event/WindowObserver.java index a0885ee9..6257bcaa 100644 --- a/src/main/java/dynamic_fps/impl/util/WindowObserver.java +++ b/src/main/java/dynamic_fps/impl/util/event/WindowObserver.java @@ -1,4 +1,4 @@ -package dynamic_fps.impl.util; +package dynamic_fps.impl.util.event; import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWCursorEnterCallback; @@ -22,11 +22,11 @@ public class WindowObserver { public WindowObserver(long address) { this.window = address; - previousFocusCallback = GLFW.glfwSetWindowFocusCallback(this.window, this::onFocusChanged); - previousMouseCallback = GLFW.glfwSetCursorEnterCallback(this.window, this::onMouseChanged); + this.previousFocusCallback = GLFW.glfwSetWindowFocusCallback(this.window, this::onFocusChanged); + this.previousMouseCallback = GLFW.glfwSetCursorEnterCallback(this.window, this::onMouseChanged); // Vanilla doesn't use this (currently), other mods might register this callback though ... - previousIconifyCallback = GLFW.glfwSetWindowIconifyCallback(this.window, this::onIconifyChanged); + this.previousIconifyCallback = GLFW.glfwSetWindowIconifyCallback(this.window, this::onIconifyChanged); } private boolean isCurrentWindow(long address) { @@ -43,8 +43,8 @@ private void onFocusChanged(long address, boolean focused) { DynamicFPSMod.onStatusChanged(); } - if (previousFocusCallback != null) { - previousFocusCallback.invoke(address, focused); + if (this.previousFocusCallback != null) { + this.previousFocusCallback.invoke(address, focused); } } @@ -58,8 +58,8 @@ private void onMouseChanged(long address, boolean hovered) { DynamicFPSMod.onStatusChanged(); } - if (previousMouseCallback != null) { - previousMouseCallback.invoke(address, hovered); + if (this.previousMouseCallback != null) { + this.previousMouseCallback.invoke(address, hovered); } } @@ -73,8 +73,8 @@ private void onIconifyChanged(long address, boolean iconified) { DynamicFPSMod.onStatusChanged(); } - if (previousIconifyCallback != null) { - previousIconifyCallback.invoke(address, iconified); + if (this.previousIconifyCallback != null) { + this.previousIconifyCallback.invoke(address, iconified); } } } diff --git a/src/main/resources/assets/dynamic_fps/lang/de_at.json b/src/main/resources/assets/dynamic_fps/lang/de_at.json index 27f1c84e..69e747a3 100644 --- a/src/main/resources/assets/dynamic_fps/lang/de_at.json +++ b/src/main/resources/assets/dynamic_fps/lang/de_at.json @@ -1,8 +1,10 @@ { "config.dynamic_fps.title": "Dynamic FPS Konfigurieren", + "config.dynamic_fps.category.general": "Allgemein", "config.dynamic_fps.category.hovered": "Hover", "config.dynamic_fps.category.unfocused": "Unfokussiert", + "config.dynamic_fps.category.abandoned": "Untätig", "config.dynamic_fps.category.invisible": "Unsichtbar", "config.dynamic_fps.frame_rate_target": "Zielbildrate", diff --git a/src/main/resources/assets/dynamic_fps/lang/de_ch.json b/src/main/resources/assets/dynamic_fps/lang/de_ch.json index 27f1c84e..69e747a3 100644 --- a/src/main/resources/assets/dynamic_fps/lang/de_ch.json +++ b/src/main/resources/assets/dynamic_fps/lang/de_ch.json @@ -1,8 +1,10 @@ { "config.dynamic_fps.title": "Dynamic FPS Konfigurieren", + "config.dynamic_fps.category.general": "Allgemein", "config.dynamic_fps.category.hovered": "Hover", "config.dynamic_fps.category.unfocused": "Unfokussiert", + "config.dynamic_fps.category.abandoned": "Untätig", "config.dynamic_fps.category.invisible": "Unsichtbar", "config.dynamic_fps.frame_rate_target": "Zielbildrate", diff --git a/src/main/resources/assets/dynamic_fps/lang/de_de.json b/src/main/resources/assets/dynamic_fps/lang/de_de.json index 27f1c84e..69e747a3 100644 --- a/src/main/resources/assets/dynamic_fps/lang/de_de.json +++ b/src/main/resources/assets/dynamic_fps/lang/de_de.json @@ -1,8 +1,10 @@ { "config.dynamic_fps.title": "Dynamic FPS Konfigurieren", + "config.dynamic_fps.category.general": "Allgemein", "config.dynamic_fps.category.hovered": "Hover", "config.dynamic_fps.category.unfocused": "Unfokussiert", + "config.dynamic_fps.category.abandoned": "Untätig", "config.dynamic_fps.category.invisible": "Unsichtbar", "config.dynamic_fps.frame_rate_target": "Zielbildrate", diff --git a/src/main/resources/assets/dynamic_fps/lang/en_us.json b/src/main/resources/assets/dynamic_fps/lang/en_us.json index 19694d7a..64125b52 100644 --- a/src/main/resources/assets/dynamic_fps/lang/en_us.json +++ b/src/main/resources/assets/dynamic_fps/lang/en_us.json @@ -1,10 +1,16 @@ { "config.dynamic_fps.title": "Configure Dynamic FPS", + "config.dynamic_fps.category.general": "General", "config.dynamic_fps.category.hovered": "Hovered", "config.dynamic_fps.category.unfocused": "Unfocused", + "config.dynamic_fps.category.abandoned": "Idle", "config.dynamic_fps.category.invisible": "Invisible", + "config.dynamic_fps.idle_time": "Idle time", + "config.dynamic_fps.idle_time_disabled": "Disabled", + "config.dynamic_fps.idle_time_tooltip": "Minutes without input until Dynamic FPS activates the Idle state while the game is focused.", + "config.dynamic_fps.frame_rate_target": "Frame Rate Target", "config.dynamic_fps.volume_multiplier": "Volume Multiplier", diff --git a/src/main/resources/assets/dynamic_fps/lang/et_ee.json b/src/main/resources/assets/dynamic_fps/lang/et_ee.json index 8cbaa4b5..3b8eb698 100644 --- a/src/main/resources/assets/dynamic_fps/lang/et_ee.json +++ b/src/main/resources/assets/dynamic_fps/lang/et_ee.json @@ -1,6 +1,8 @@ { "config.dynamic_fps.title": "Dynamic FPS seadistus", + "config.dynamic_fps.category.general": "Üldine", + "key.dynamic_fps.toggle_forced": "Sunni vähendatud ks (lüliti)", "key.dynamic_fps.toggle_disabled": "Keela Dynamic FPS (lüliti)", "gui.dynamic_fps.hud.reducing": "Dynamic FPS: sunnin vähendatud kaadrisagedust", diff --git a/src/main/resources/assets/dynamic_fps/lang/fr_ca.json b/src/main/resources/assets/dynamic_fps/lang/fr_ca.json index e38ac54a..8b41381f 100644 --- a/src/main/resources/assets/dynamic_fps/lang/fr_ca.json +++ b/src/main/resources/assets/dynamic_fps/lang/fr_ca.json @@ -1,6 +1,7 @@ { "config.dynamic_fps.title": "Configuration de Dynamic FPS", + "config.dynamic_fps.category.general": "Général", "config.dynamic_fps.category.hovered": "Survolé", "config.dynamic_fps.category.unfocused": "Non-focalisé", "config.dynamic_fps.category.invisible": "Invisible", diff --git a/src/main/resources/assets/dynamic_fps/lang/fr_fr.json b/src/main/resources/assets/dynamic_fps/lang/fr_fr.json index e38ac54a..8b41381f 100644 --- a/src/main/resources/assets/dynamic_fps/lang/fr_fr.json +++ b/src/main/resources/assets/dynamic_fps/lang/fr_fr.json @@ -1,6 +1,7 @@ { "config.dynamic_fps.title": "Configuration de Dynamic FPS", + "config.dynamic_fps.category.general": "Général", "config.dynamic_fps.category.hovered": "Survolé", "config.dynamic_fps.category.unfocused": "Non-focalisé", "config.dynamic_fps.category.invisible": "Invisible", diff --git a/src/main/resources/assets/dynamic_fps/lang/it_it.json b/src/main/resources/assets/dynamic_fps/lang/it_it.json index 49910133..9258e51a 100644 --- a/src/main/resources/assets/dynamic_fps/lang/it_it.json +++ b/src/main/resources/assets/dynamic_fps/lang/it_it.json @@ -1,6 +1,7 @@ { "config.dynamic_fps.title": "Configura Dynamic FPS", + "config.dynamic_fps.category.general": "Generale", "config.dynamic_fps.category.hovered": "Sopra il cursore", "config.dynamic_fps.category.unfocused": "Non in primo piano", "config.dynamic_fps.category.invisible": "Invisibile", @@ -18,4 +19,4 @@ "gui.dynamic_fps.hud.disabled": "Dynamic FPS disabilitato", "modmenu.descriptionTranslation.dynamic_fps": "Regola dinamicamente il FPS in modo che Minecraft non consumi risorse in background." -} \ No newline at end of file +} diff --git a/src/main/resources/assets/dynamic_fps/lang/ko_kr.json b/src/main/resources/assets/dynamic_fps/lang/ko_kr.json index a10cc222..1ecf817e 100644 --- a/src/main/resources/assets/dynamic_fps/lang/ko_kr.json +++ b/src/main/resources/assets/dynamic_fps/lang/ko_kr.json @@ -1,6 +1,8 @@ { "config.dynamic_fps.title": "Dynamic FPS 구성", + "config.dynamic_fps.category.general": "일반", + "key.dynamic_fps.toggle_forced": "강제로 FPS 감소 (토글)", "key.dynamic_fps.toggle_disabled": "Dynamic FPS 비활성화 (Toggle)", "gui.dynamic_fps.hud.reducing": "Dynamic FPS: 강제로 FPS 감소중", diff --git a/src/main/resources/assets/dynamic_fps/lang/pl_pl.json b/src/main/resources/assets/dynamic_fps/lang/pl_pl.json index 272a6d77..87c9cf88 100644 --- a/src/main/resources/assets/dynamic_fps/lang/pl_pl.json +++ b/src/main/resources/assets/dynamic_fps/lang/pl_pl.json @@ -1,6 +1,8 @@ { "config.dynamic_fps.title": "Skonfiguruj Dynamic FPS", + "config.dynamic_fps.category.general": "Ogólne", + "key.dynamic_fps.toggle_forced": "Wymuś obniżenie FPS (przełącznik)", "key.dynamic_fps.toggle_disabled": "Wyłącz Dynamic FPS (przełącznik)", "gui.dynamic_fps.hud.reducing": "Dynamic FPS: wymuszono obniżenie FPS", diff --git a/src/main/resources/assets/dynamic_fps/lang/pt_br.json b/src/main/resources/assets/dynamic_fps/lang/pt_br.json index 21475c90..ff7ce802 100644 --- a/src/main/resources/assets/dynamic_fps/lang/pt_br.json +++ b/src/main/resources/assets/dynamic_fps/lang/pt_br.json @@ -1,6 +1,8 @@ { "config.dynamic_fps.title": "Definições do Dynamic FPS", + "config.dynamic_fps.category.general": "Geral", + "key.dynamic_fps.toggle_forced": "Forçar redução da taxa de quadros (alternável)", "key.dynamic_fps.toggle_disabled": "Desativar o Disable Dynamic (alternável)", "gui.dynamic_fps.hud.reducing": "Dynamic FPS: forçando a redução da taxa de quadros", diff --git a/src/main/resources/assets/dynamic_fps/lang/pt_pt.json b/src/main/resources/assets/dynamic_fps/lang/pt_pt.json index 21475c90..ff7ce802 100644 --- a/src/main/resources/assets/dynamic_fps/lang/pt_pt.json +++ b/src/main/resources/assets/dynamic_fps/lang/pt_pt.json @@ -1,6 +1,8 @@ { "config.dynamic_fps.title": "Definições do Dynamic FPS", + "config.dynamic_fps.category.general": "Geral", + "key.dynamic_fps.toggle_forced": "Forçar redução da taxa de quadros (alternável)", "key.dynamic_fps.toggle_disabled": "Desativar o Disable Dynamic (alternável)", "gui.dynamic_fps.hud.reducing": "Dynamic FPS: forçando a redução da taxa de quadros", diff --git a/src/main/resources/assets/dynamic_fps/lang/ru_ru.json b/src/main/resources/assets/dynamic_fps/lang/ru_ru.json index ae9046cb..84dc6c83 100644 --- a/src/main/resources/assets/dynamic_fps/lang/ru_ru.json +++ b/src/main/resources/assets/dynamic_fps/lang/ru_ru.json @@ -1,6 +1,7 @@ { "config.dynamic_fps.title": "Настройки Dynamic FPS", + "config.dynamic_fps.category.general": "Основные", "config.dynamic_fps.category.hovered": "Наведён", "config.dynamic_fps.category.unfocused": "Расфокусирован", "config.dynamic_fps.category.invisible": "Свёрнут", diff --git a/src/main/resources/assets/dynamic_fps/lang/sv_se.json b/src/main/resources/assets/dynamic_fps/lang/sv_se.json index 9ccc4c14..dbaf2303 100644 --- a/src/main/resources/assets/dynamic_fps/lang/sv_se.json +++ b/src/main/resources/assets/dynamic_fps/lang/sv_se.json @@ -1,9 +1,10 @@ { "config.dynamic_fps.title": "Konfigurera Dynamic FPS", + "config.dynamic_fps.category.general": "Allmänt", "config.dynamic_fps.category.hovered": "Hovrande", "config.dynamic_fps.category.unfocused": "Ofokuserad", - "config.dynamic_fps.category.invisible": "Osynlig", + "config.dynamic_fps.category.invisible": "Osynlig", "config.dynamic_fps.frame_rate_target": "Bildfrekvensmål", "config.dynamic_fps.frame_rate_target_description": "Ställ in bildfrekvensmålet till -1 för att stänga av bildfrekvens reducering.", @@ -11,7 +12,7 @@ "config.dynamic_fps.graphics_state": "Bildskärmsinställningar", "config.dynamic_fps.show_toasts": "Visa Toasts", "config.dynamic_fps.run_garbage_collector": "Starta GC", - + "key.dynamic_fps.toggle_forced": "Tvinga Reducerad FPS (Växla)", "key.dynamic_fps.toggle_disabled": "Inaktivera Dynamic FPS (Växla)", "gui.dynamic_fps.hud.reducing": "Dynamic FPS: Tvingar Reducerad BPS", diff --git a/src/main/resources/assets/dynamic_fps/lang/tr_tr.json b/src/main/resources/assets/dynamic_fps/lang/tr_tr.json index e80f5f30..c4bbf9ea 100644 --- a/src/main/resources/assets/dynamic_fps/lang/tr_tr.json +++ b/src/main/resources/assets/dynamic_fps/lang/tr_tr.json @@ -1,6 +1,8 @@ { "config.dynamic_fps.title": "Dinamik FPS Ayarları", + "config.dynamic_fps.category.general": "Genel", + "key.dynamic_fps.toggle_forced": "Zorla Azaltılmış FPS (Aç / kapat)", "key.dynamic_fps.toggle_disabled": "Dinamik FPS'yi Devre Dışı Bırak (Aç / kapat)", "gui.dynamic_fps.hud.reducing": "Dinamik FPS: Azaltılmış FPS Zorlanıyor", diff --git a/src/main/resources/assets/dynamic_fps/lang/uk_ua.json b/src/main/resources/assets/dynamic_fps/lang/uk_ua.json index 0d04bd75..0f86ec37 100644 --- a/src/main/resources/assets/dynamic_fps/lang/uk_ua.json +++ b/src/main/resources/assets/dynamic_fps/lang/uk_ua.json @@ -1,6 +1,8 @@ { "config.dynamic_fps.title": "Параметри Dynamic FPS", + "config.dynamic_fps.category.general": "Загальні", + "key.dynamic_fps.toggle_forced": "Примусово знижувати частоту кадрів (перемикання)", "key.dynamic_fps.toggle_disabled": "Вимкнути Dynamic FPS (перемикання)", "gui.dynamic_fps.hud.reducing": "Dynamic FPS: Увімкнено примусове зниження частоти кадрів", diff --git a/src/main/resources/assets/dynamic_fps/lang/vi_vn.json b/src/main/resources/assets/dynamic_fps/lang/vi_vn.json index 991dd378..52bd7bd1 100644 --- a/src/main/resources/assets/dynamic_fps/lang/vi_vn.json +++ b/src/main/resources/assets/dynamic_fps/lang/vi_vn.json @@ -1,6 +1,8 @@ { "config.dynamic_fps.title": "Định cấu hình Dynamic FPS", + "config.dynamic_fps.category.general": "Chung", + "key.dynamic_fps.toggle_forced": "Buộc giảm FPS (Đổi)", "key.dynamic_fps.toggle_disabled": "Vô hiệu hoá Dynamic FPS (Đổi)", "gui.dynamic_fps.hud.reducing": "Dynamic FPS: Buộc giảm FPS", diff --git a/src/main/resources/assets/dynamic_fps/lang/zh_cn.json b/src/main/resources/assets/dynamic_fps/lang/zh_cn.json index f54e3669..ff72aca7 100644 --- a/src/main/resources/assets/dynamic_fps/lang/zh_cn.json +++ b/src/main/resources/assets/dynamic_fps/lang/zh_cn.json @@ -1,6 +1,7 @@ { "config.dynamic_fps.title": "Dynamic FPS 动态帧率配置", + "config.dynamic_fps.category.general": "预设", "config.dynamic_fps.category.hovered": "悬停", "config.dynamic_fps.category.unfocused": "失焦", "config.dynamic_fps.category.invisible": "不可见", diff --git a/src/main/resources/assets/dynamic_fps/lang/zh_tw.json b/src/main/resources/assets/dynamic_fps/lang/zh_tw.json index 96938631..bf83dd9d 100644 --- a/src/main/resources/assets/dynamic_fps/lang/zh_tw.json +++ b/src/main/resources/assets/dynamic_fps/lang/zh_tw.json @@ -1,6 +1,7 @@ { "config.dynamic_fps.title": "設定 Dynamic FPS", + "config.dynamic_fps.category.general": "一般", "config.dynamic_fps.category.hovered": "聚焦時", "config.dynamic_fps.category.unfocused": "未聚焦時", "config.dynamic_fps.category.invisible": "不可見時", diff --git a/src/main/resources/dynamic_fps.accesswidener b/src/main/resources/dynamic_fps.accesswidener new file mode 100644 index 00000000..aa31532c --- /dev/null +++ b/src/main/resources/dynamic_fps.accesswidener @@ -0,0 +1,3 @@ +accessWidener v2 named + +accessible field com/mojang/blaze3d/platform/Window window J diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index c7103d0c..58c9eca4 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -31,6 +31,7 @@ "mixins": [ "dynamic_fps.mixins.json" ], + "accessWidener": "dynamic_fps.accesswidener", "depends": { "minecraft": ">=1.20.0",