Skip to content

Commit

Permalink
Merge branch 'config'
Browse files Browse the repository at this point in the history
  • Loading branch information
juliand665 committed Jan 17, 2021
2 parents 46594cc + 887b7e7 commit ff3966b
Show file tree
Hide file tree
Showing 15 changed files with 305 additions and 81 deletions.
34 changes: 22 additions & 12 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
id 'fabric-loom' version '0.4-SNAPSHOT'
id 'fabric-loom' version '0.5-SNAPSHOT'
id 'maven-publish'
}

Expand All @@ -21,15 +21,25 @@ repositories {
dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modCompile "net.fabricmc:fabric-loader:${project.loader_version}"

modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modCompile "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"

// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitivity on them.
//modCompile "io.github.prospector:modmenu:${project.mod_menu_version}"
modRuntime ("com.github.SuperCoder7979:databreaker:${databreaker_version}") {
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"

// Mod Menu, to add the hook for a config screen
modImplementation "io.github.prospector:modmenu:${project.modmenu_version}"

// Easy config screen setup library
modApi("me.shedaniel.cloth:config-2:${project.cloth_version}") {
exclude(group: "net.fabricmc.fabric-api")
}
include "me.shedaniel.cloth:config-2:${project.cloth_version}"
// Using the TOML config format
modImplementation("com.moandjiezana.toml:toml4j:${project.toml4j_version}")
include "com.moandjiezana.toml:toml4j:${project.toml4j_version}"

// Data Breaker makes the dev env start up much faster by breaking support for old worlds, which we don't care about anyway.
modRuntime("com.github.SuperCoder7979:databreaker:${databreaker_version}") {
exclude module: "fabric-loader"
}

Expand All @@ -39,12 +49,12 @@ dependencies {

processResources {
inputs.property "version", project.version

from(sourceSets.main.resources.srcDirs) {
include "fabric.mod.json"
expand "version": project.version
}

from(sourceSets.main.resources.srcDirs) {
exclude "fabric.mod.json"
}
Expand Down Expand Up @@ -82,7 +92,7 @@ publishing {
}
}
}

// select the repositories you want to publish to
repositories {
// uncomment to publish to the local maven
Expand Down
29 changes: 16 additions & 13 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
org.gradle.jvmargs = -Xmx1G

# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.16.2
yarn_mappings=1.16.2+build.10
loader_version=0.9.1+build.205
fabric_version=0.18.0+build.397-1.16
# check these on https://modmuss50.me/fabric.html
minecraft_version = 1.16.2
yarn_mappings = 1.16.2+build.10
loader_version = 0.9.1+build.205
fabric_version = 0.18.0+build.397-1.16

# Mod Properties
mod_version = 1.2.1
maven_group = juliand665
archives_base_name = dynamic-fps
mod_version = 1.3.0
maven_group = juliand665
archives_base_name = dynamic-fps

# Dependencies
#developer_mode_version=1.0.15
#mod_menu_version = 1.8.5+build.23
databreaker_version = 0.2.6
findbugs_version = 3.0.2
#developer_mode_version=1.0.15

findbugs_version = 3.0.2
databreaker_version = 0.2.6
modmenu_version = 1.14.6+build.31
cloth_version = 4.8.3
toml4j_version = 0.7.2
48 changes: 48 additions & 0 deletions src/main/java/dynamicfps/DynamicFPSConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package dynamicfps;

import com.moandjiezana.toml.Toml;
import com.moandjiezana.toml.TomlWriter;
import net.fabricmc.loader.api.FabricLoader;

import java.io.File;
import java.io.IOException;

public final class DynamicFPSConfig {
private transient File file;
/// Whether to disable or enable the frame rate drop when unfocused
public boolean reduceFPSWhenUnfocused = true;
/// The frame rate to target when unfocused (only applies if `enableUnfocusedFPS` is true)
public int unfocusedFPS = 1;
/// Whether or not to uncap FPS when hovered, even if it would otherwise be reduced
public boolean restoreFPSWhenHovered = true;

private DynamicFPSConfig() {}

public static DynamicFPSConfig load() {
File file = new File(
FabricLoader.getInstance().getConfigDir().toString(),
DynamicFPSMod.MOD_ID + ".toml"
);

DynamicFPSConfig config;
if (file.exists()) {
Toml configTOML = new Toml().read(file);
config = configTOML.to(DynamicFPSConfig.class);
config.file = file;
} else {
config = new DynamicFPSConfig();
config.file = file;
config.save();
}
return config;
}

public void save() {
TomlWriter writer = new TomlWriter();
try {
writer.write(this, file);
} catch (IOException e) {
e.printStackTrace();
}
}
}
113 changes: 80 additions & 33 deletions src/main/java/dynamicfps/DynamicFPSMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,71 +12,118 @@
import net.minecraft.util.Util;
import org.lwjgl.glfw.GLFW;

import javax.annotation.Nullable;
import java.util.concurrent.locks.LockSupport;

import static dynamicfps.util.Localization.translationKey;

public class DynamicFPSMod implements ModInitializer {
public static final String MOD_ID = "dynamicfps";

private static long lastRender;
public static DynamicFPSConfig config = null;

private static boolean isForcingLowFPS = false;
private static boolean isDisabled = false;
public static boolean isDisabled() { return isDisabled; }

// 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 inactive.
private static boolean hasRenderedLastFrame = false;
private static boolean isForcingLowFPS = false;
public static boolean isForcingLowFPS() { return isForcingLowFPS; }

public static boolean isForcingLowFPS() {
return isForcingLowFPS;
}
private static final KeyBindingHandler toggleForcedKeyBinding = new KeyBindingHandler(
translationKey("key", "toggle_forced"),
"key.categories.misc",
() -> isForcingLowFPS = !isForcingLowFPS
);

private static final KeyBinding toggleKeyBinding = new KeyBinding(
"key." + MOD_ID + ".toggle",
InputUtil.Type.KEYSYM,
InputUtil.UNKNOWN_KEY.getCode(),
"key.categories.misc"
private static final KeyBindingHandler toggleDisabledKeyBinding = new KeyBindingHandler(
translationKey("key", "toggle_disabled"),
"key.categories.misc",
() -> isDisabled = !isDisabled
);

@Override
public void onInitialize() {
KeyBindingHelper.registerKeyBinding(toggleKeyBinding);
config = DynamicFPSConfig.load();

ClientTickEvents.END_CLIENT_TICK.register(new KeyBindingHandler(
toggleKeyBinding,
() -> isForcingLowFPS = !isForcingLowFPS
));
toggleForcedKeyBinding.register();
toggleDisabledKeyBinding.register();

HudRenderCallback.EVENT.register(new HudInfoRenderer());
}

private static long lastRender;
/**
Determines whether the game should render anything at this time. If not, blocks for a short time.
@return whether or not the game should be rendered after this.
*/
public static boolean checkForRender() {
MinecraftClient client = MinecraftClient.getInstance();
Window window = ((WindowHolder) client).getWindow();
if (isDisabled) return true;

long currentTime = Util.getMeasuringTimeMs();
long timeSinceLastRender = currentTime - lastRender;

boolean isVisible = GLFW.glfwGetWindowAttrib(window.getHandle(), GLFW.GLFW_VISIBLE) != 0;
boolean shouldReduceFPS = isForcingLowFPS || !client.isWindowFocused();
if (!shouldReduceFPS && hasRenderedLastFrame) {
if (!checkForRender(timeSinceLastRender)) return false;

lastRender = currentTime;
return true;
}

// 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 inactive.
private static boolean hasRenderedLastFrame = false;
private static boolean checkForRender(long timeSinceLastRender) {
Integer fpsOverride = fpsOverride();
if (fpsOverride == null) {
hasRenderedLastFrame = false;
return true;
}

boolean shouldRender = isVisible && (!shouldReduceFPS || timeSinceLastRender > 1000);
if (shouldRender) {
lastRender = currentTime;
} else {
if (!hasRenderedLastFrame) {
hasRenderedLastFrame = true;
return true; // render one last frame before reducing, to make sure differences in this state show up instantly.
}
LockSupport.parkNanos("waiting to render", 30_000_000); // 30 ms
if (!hasRenderedLastFrame) {
// render one last frame before reducing, to make sure differences in this state show up instantly.
hasRenderedLastFrame = true;
return true;
}
return shouldRender;

if (fpsOverride == 0) {
idle(1000);
return false;
}

long frameTime = 1000 / fpsOverride;
boolean shouldSkipRender = timeSinceLastRender < frameTime;
if (!shouldSkipRender) return true;

idle(frameTime);
return false;
}

/**
force minecraft to idle because otherwise we'll be busy checking for render again and again
*/
private static void idle(long waitMillis) {
// cap at 30 ms before we check again so user doesn't have to wait long after tabbing back in
waitMillis = Math.min(waitMillis, 30);
LockSupport.parkNanos("waiting to render", waitMillis * 1_000_000);
}

@Nullable
private static Integer fpsOverride() {
MinecraftClient client = MinecraftClient.getInstance();
Window window = ((WindowHolder) client).getWindow();

boolean isVisible = GLFW.glfwGetWindowAttrib(window.getHandle(), GLFW.GLFW_VISIBLE) != 0;
if (!isVisible) return 0;

if (isForcingLowFPS) return config.unfocusedFPS;

if (config.restoreFPSWhenHovered) {
boolean isHovered = GLFW.glfwGetWindowAttrib(window.getHandle(), GLFW.GLFW_HOVERED) != 0;
if (isHovered) return null;
}

if (config.reduceFPSWhenUnfocused && !client.isWindowFocused()) return config.unfocusedFPS;

return null;
}

public interface WindowHolder {
Expand Down
54 changes: 54 additions & 0 deletions src/main/java/dynamicfps/DynamicFPSModMenu.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package dynamicfps;

import io.github.prospector.modmenu.api.ConfigScreenFactory;
import io.github.prospector.modmenu.api.ModMenuApi;
import me.shedaniel.clothconfig2.api.ConfigBuilder;
import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
import net.minecraft.client.gui.screen.Screen;

import static dynamicfps.util.Localization.localized;

public class DynamicFPSModMenu implements ModMenuApi {
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return DynamicFPSModMenu::genConfig;
}

private static Screen genConfig(Screen parent) {
ConfigBuilder builder = ConfigBuilder.create()
.setParentScreen(parent)
.setTitle(localized("config", "title"))
.setSavingRunnable(DynamicFPSMod.config::save);
ConfigEntryBuilder entryBuilder = builder.entryBuilder();

// general
builder.getOrCreateCategory(localized("config", "category.general"))
.addEntry(entryBuilder
.startBooleanToggle(
localized("config", "reduce_when_unfocused"),
DynamicFPSMod.config.reduceFPSWhenUnfocused
)
.setSaveConsumer(value -> DynamicFPSMod.config.reduceFPSWhenUnfocused = value)
.build()
)
.addEntry(entryBuilder
.startIntSlider(
localized("config", "unfocused_fps"),
DynamicFPSMod.config.unfocusedFPS,
0, 60
)
.setSaveConsumer(value -> DynamicFPSMod.config.unfocusedFPS = value)
.build()
)
.addEntry(entryBuilder
.startBooleanToggle(
localized("config", "restore_when_hovered"),
DynamicFPSMod.config.restoreFPSWhenHovered
)
.setSaveConsumer(value -> DynamicFPSMod.config.restoreFPSWhenHovered = value)
.build()
);

return builder.build();
}
}
Loading

0 comments on commit ff3966b

Please sign in to comment.