├── .gitattributes
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── GitHub
├── logo.png
├── mainsettings.png
└── screenshotsettings.png
├── LICENSE
├── LICENSE_FORMER
├── README.md
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
└── main
├── java
└── io
│ └── github
│ └── ultimateboomer
│ └── resolutioncontrol
│ ├── ResolutionControlMod.java
│ ├── client
│ └── gui
│ │ └── screen
│ │ ├── InfoSettingsScreen.java
│ │ ├── MainSettingsScreen.java
│ │ ├── ScreenshotSettingsScreen.java
│ │ └── SettingsScreen.java
│ ├── compat
│ └── modmenu
│ │ └── ModMenuInfo.java
│ ├── mixin
│ ├── FramebufferMixin.java
│ ├── GameRendererMixin.java
│ ├── MinecraftClientMixin.java
│ ├── WindowMixin.java
│ └── WorldRendererMixin.java
│ └── util
│ ├── Config.java
│ ├── ConfigHandler.java
│ ├── DynamicResolutionHandler.java
│ ├── RCUtil.java
│ └── ScalingAlgorithm.java
└── resources
├── assets
└── resolutioncontrol
│ ├── icon.png
│ ├── lang
│ └── en_us.json
│ └── textures
│ └── gui
│ ├── settings.png
│ └── settings.psd
├── fabric.mod.json
├── resolutioncontrol.accesswidener
└── resolutioncontrol.mixins.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | # Automatically build the project and run any configured tests for every push
2 | # and submitted pull request. This can help catch issues that only occur on
3 | # certain platforms or Java versions, and provides a first line of defence
4 | # against bad commits.
5 |
6 | name: build
7 | on: [pull_request, push]
8 |
9 | jobs:
10 | build:
11 | strategy:
12 | matrix:
13 | # Use these Java versions
14 | java: [
15 | 16 # Minimum supported by Minecraft
16 | ]
17 | # and run on both Linux and Windows
18 | os: [ubuntu-20.04, windows-latest]
19 | runs-on: ${{ matrix.os }}
20 | steps:
21 | - name: checkout repository
22 | uses: actions/checkout@v2
23 | - name: validate gradle wrapper
24 | uses: gradle/wrapper-validation-action@v1
25 | - name: setup jdk ${{ matrix.java }}
26 | uses: actions/setup-java@v1
27 | with:
28 | java-version: ${{ matrix.java }}
29 | - name: make gradle wrapper executable
30 | if: ${{ runner.os != 'Windows' }}
31 | run: chmod +x ./gradlew
32 | - name: build
33 | run: ./gradlew build
34 | - name: capture build artifacts
35 | if: ${{ runner.os == 'Linux' && matrix.java == '16' }} # Only upload artifacts built from latest java on one OS
36 | uses: actions/upload-artifact@v2
37 | with:
38 | name: Artifacts
39 | path: build/libs/
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # gradle
2 |
3 | .gradle/
4 | build/
5 | out/
6 | classes/
7 |
8 | # idea
9 |
10 | .idea/
11 | *.iml
12 | *.ipr
13 | *.iws
14 |
15 | # vscode
16 |
17 | .settings/
18 | .vscode/
19 | bin/
20 | .classpath
21 | .project
22 |
23 | # fabric
24 |
25 | run/
26 |
27 | # custom stuff
28 |
29 | decompiled/
30 |
--------------------------------------------------------------------------------
/GitHub/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trisk/Resolution-Control/c6a679090351b1ba0a1383dccc44ef2c28344eaf/GitHub/logo.png
--------------------------------------------------------------------------------
/GitHub/mainsettings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trisk/Resolution-Control/c6a679090351b1ba0a1383dccc44ef2c28344eaf/GitHub/mainsettings.png
--------------------------------------------------------------------------------
/GitHub/screenshotsettings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trisk/Resolution-Control/c6a679090351b1ba0a1383dccc44ef2c28344eaf/GitHub/screenshotsettings.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Steven Cao
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LICENSE_FORMER:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Julian Dunskus
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # ResolutionControl+
6 |
7 | ResolutionControl+ is a fork of [Resolution Control](https://github.com/juliand665/Resolution-Control)
8 | with additional functionality.
9 |
10 | ## Features
11 |
12 | ### Resolution Scaling
13 |
14 | ResolutionControl+ allows you to change Minecraft's render resolution separately from the HUD elements.
15 | If you have a good GPU, you can increase the multiplier for anti-aliasing.
16 | Otherwise, you can lower the multiplier to improve performance or give Minecraft a retro style.
17 | You can also set a custom multiplier value by pressing the `S` button.
18 |
19 | Additionally, you can set the upscale/downscale algorithm used to scale the render to the viewport.
20 | Linear is useful as an anti-aliasing filter,
21 | and nearest neighbor generally looks better for lower than native resolutions.
22 |
23 | 
24 |
25 | ---
26 |
27 | ### Screenshots
28 |
29 | ResolutionControl+ can be used to take larger than native resolution screenshots.
30 | To do this, use the `Screenshot Framebuffer` keybind which is unbound by default.
31 |
32 | Compared to *Fabrishot*, this implementation does not lock up the game until the screenshot is saved.
33 | This makes taking large screenshots much faster.
34 | And while a screenshot is being processed, you can take another one without any problems.
35 |
36 | Here is the time it takes to take a 16k screenshot, on an i7 4770 and GTX 1060:
37 |
38 | ```
39 | Fabrishot: 12 seconds (pause)
40 | ResolutionControl+: <1 second (initial pause) + 13 seconds (background processing)
41 | ```
42 |
43 | [](http://www.youtube.com/watch?v=Dghj0Ldeu0Q)
44 |
45 | ---
46 |
47 | ## Settings
48 |
49 | **Resolution Scaling**
50 |
51 | - Render scale
52 | - 0.0 - 8.0x (can be exceeded by manually setting the value)
53 | - An estimate VRAM usage is displayed
54 | - Upscale/downscale algorithm
55 | - Linear, nearest
56 |
57 | **Screenshots**
58 |
59 | - Use set size
60 | - Enabled: render screenshots at the specified resolution
61 | - Note: can produce artifacts if the screenshot aspect ratio is significantly different
62 | from the aspect ratio of the viewport
63 | - Disabled: render at the scaled render resolution
64 | - Always allocated
65 | - Enabled: screenshot framebuffer is always allocated in memory
66 | - May reduce screenshot pause times, at the cost of more VRAM usage during normal gameplay
67 | - Disabled: screenshot framebuffer is allocated on-demand and freed immediately after
68 | - Screenshot size
69 | - Screenshot size if `use set size` is enabled
70 | - An estimate VRAM usage is displayed
71 |
72 | ---
73 |
74 | ## Compatibility
75 |
76 | Currently, ResolutionControl+ **does not work with Fabulous Graphics**.
77 |
78 | **Sodium** - Compatible
79 |
80 | **Canvas Renderer** - Mostly compatible, lower resolutions break HUD item rendering
81 |
82 | **Optifine** - Compatible, stacks with its own render scale implementation
83 |
84 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'fabric-loom' // version in gradle.properties
3 | id 'maven-publish'
4 | }
5 |
6 | sourceCompatibility = JavaVersion.VERSION_17
7 | targetCompatibility = JavaVersion.VERSION_17
8 |
9 | archivesBaseName = project.archives_base_name
10 | version = project.mod_version
11 | group = project.maven_group
12 |
13 | loom {
14 | accessWidenerPath = file("src/main/resources/${project.mod_name}.accesswidener")
15 | }
16 |
17 | repositories {
18 | //maven { url "https://maven.jamieswhiteshirt.com/libs-release/" }
19 | maven { url "https://maven.terraformersmc.com/releases/" }
20 | //maven { url 'https://jitpack.io' }
21 | }
22 |
23 | dependencies {
24 | minecraft "com.mojang:minecraft:${project.minecraft_version}"
25 | mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
26 | modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
27 |
28 | // Fabric API. This is technically optional, but you probably want it anyway.
29 | modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
30 |
31 | // PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
32 | // You may need to force-disable transitivity on them.
33 | //modCompile "com.jamieswhiteshirt:developer-mode:${project.developer_mode_version}"
34 | modImplementation "com.terraformersmc:modmenu:${project.mod_menu_version}"
35 |
36 | // Disable mc-smoothboot until new build is available
37 | //modLocalRuntime "com.github.ultimateboomer:mc-smoothboot:${project.smoothboot_version}"
38 | }
39 |
40 | processResources {
41 | inputs.property "version", project.version
42 |
43 | filesMatching("fabric.mod.json") {
44 | expand "version": project.version
45 | }
46 | }
47 |
48 | tasks.withType(JavaCompile).configureEach {
49 | // ensure that the encoding is set to UTF-8, no matter what the system default is
50 | // this fixes some edge cases with special characters not displaying correctly
51 | // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
52 | // If Javadoc is generated, this must be specified in that task too.
53 | it.options.encoding = "UTF-8"
54 |
55 | // Minecraft 1.18 (1.18-pre2) upwards uses Java 17.
56 | it.options.release = 17
57 | }
58 |
59 | java {
60 | // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
61 | // if it is present.
62 | // If you remove this line, sources will not be generated.
63 | withSourcesJar()
64 | }
65 |
66 | jar {
67 | from("LICENSE") {
68 | rename { "${it}_${project.archivesBaseName}" }
69 | }
70 | }
71 |
72 | // configure the maven publication
73 | publishing {
74 | publications {
75 | mavenJava(MavenPublication) {
76 | // add all the jars that should be included when publishing to maven
77 | artifact(remapJar) {
78 | builtBy remapJar
79 | }
80 | artifact(sourcesJar) {
81 | builtBy remapSourcesJar
82 | }
83 | }
84 | }
85 |
86 | // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
87 | repositories {
88 | // Add repositories to publish to here.
89 | // Notice: This block does NOT have the same function as the block in the top level.
90 | // The repositories here will be used for publishing your artifact, not for
91 | // retrieving dependencies.
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Done to increase the memory available to gradle.
2 | org.gradle.jvmargs=-Xmx1G
3 |
4 | # Fabric Properties
5 | # Check https://fabricmc.net/versions.html
6 | minecraft_version=1.18.1
7 | yarn_mappings=1.18.1+build.18
8 | loader_version=0.12.12
9 | loom_version=0.10-SNAPSHOT
10 |
11 | # Mod Properties
12 | mod_name=resolutioncontrol
13 | mod_version=3.0.0+1.18
14 | maven_group=io.github.ultimateboomer
15 | archives_base_name=resolution-control-plus
16 |
17 | # Dependencies
18 | # https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api/
19 | fabric_version=0.45.2+1.18
20 | #developer_mode_version=1.0.15
21 | mod_menu_version=3.0.1
22 | smoothboot_version=1.18-1.7.0
23 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trisk/Resolution-Control/c6a679090351b1ba0a1383dccc44ef2c28344eaf/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | maven {
4 | name = 'Fabric'
5 | url = 'https://maven.fabricmc.net/'
6 | }
7 | gradlePluginPortal()
8 | }
9 |
10 | plugins {
11 | id 'fabric-loom' version "${loom_version}"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/ResolutionControlMod.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol;
2 |
3 | import io.github.ultimateboomer.resolutioncontrol.client.gui.screen.MainSettingsScreen;
4 | import io.github.ultimateboomer.resolutioncontrol.client.gui.screen.SettingsScreen;
5 | import io.github.ultimateboomer.resolutioncontrol.util.*;
6 | import net.fabricmc.api.ModInitializer;
7 | import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
8 | import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
9 | import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
10 | import net.fabricmc.loader.api.FabricLoader;
11 | import net.minecraft.client.MinecraftClient;
12 | import net.minecraft.client.gl.Framebuffer;
13 | import net.minecraft.client.gl.WindowFramebuffer;
14 | import net.minecraft.client.option.KeyBinding;
15 | import net.minecraft.client.util.InputUtil;
16 | import net.minecraft.client.util.ScreenshotRecorder;
17 | import net.minecraft.client.util.Window;
18 | import net.minecraft.text.TranslatableText;
19 | import net.minecraft.util.Identifier;
20 | import org.apache.logging.log4j.LogManager;
21 | import org.apache.logging.log4j.Logger;
22 | import org.jetbrains.annotations.Nullable;
23 | import org.lwjgl.glfw.GLFW;
24 |
25 | import java.util.HashSet;
26 | import java.util.Objects;
27 | import java.util.Set;
28 |
29 | public class ResolutionControlMod implements ModInitializer {
30 | public static final String MOD_ID = "resolutioncontrol";
31 | public static final String MOD_NAME = "ResolutionControl+";
32 |
33 | public static final Logger LOGGER = LogManager.getLogger(MOD_NAME);
34 |
35 | public static Identifier identifier(String path) {
36 | return new Identifier(MOD_ID, path);
37 | }
38 |
39 | private static final MinecraftClient client = MinecraftClient.getInstance();
40 |
41 | private static ResolutionControlMod instance;
42 |
43 | public static ResolutionControlMod getInstance() {
44 | return instance;
45 | }
46 |
47 | private static final String SCREENSHOT_PREFIX = "fb";
48 |
49 | private boolean optifineInstalled;
50 |
51 | private KeyBinding settingsKey;
52 | private KeyBinding screenshotKey;
53 |
54 | private boolean shouldScale = false;
55 |
56 | @Nullable
57 | private Framebuffer framebuffer;
58 |
59 | @Nullable
60 | private Framebuffer screenshotFrameBuffer;
61 |
62 | @Nullable
63 | private Framebuffer clientFramebuffer;
64 |
65 | private Set minecraftFramebuffers;
66 |
67 | private Class extends SettingsScreen> lastSettingsScreen = MainSettingsScreen.class;
68 |
69 | private int currentWidth;
70 | private int currentHeight;
71 |
72 | private long estimatedMemory;
73 |
74 | private boolean screenshot = false;
75 |
76 | private int lastWidth;
77 | private int lastHeight;
78 |
79 | @Override
80 | public void onInitialize() {
81 | instance = this;
82 |
83 | settingsKey = KeyBindingHelper.registerKeyBinding(new KeyBinding(
84 | "key.resolutioncontrol.settings",
85 | InputUtil.Type.KEYSYM,
86 | GLFW.GLFW_KEY_O,
87 | "key.categories.resolutioncontrol"));
88 |
89 | screenshotKey = KeyBindingHelper.registerKeyBinding(new KeyBinding(
90 | "key.resolutioncontrol.screenshot",
91 | InputUtil.Type.KEYSYM,
92 | -1,
93 | "key.categories.resolutioncontrol"));
94 |
95 | ClientTickEvents.END_CLIENT_TICK.register(client -> {
96 | while (settingsKey.wasPressed()) {
97 | client.setScreen(SettingsScreen.getScreen(lastSettingsScreen));
98 | }
99 | });
100 |
101 | ClientTickEvents.END_CLIENT_TICK.register(client -> {
102 | while (screenshotKey.wasPressed()) {
103 | if (getOverrideScreenshotScale()) {
104 | this.screenshot = true;
105 | client.player.sendMessage(
106 | new TranslatableText("resolutioncontrol.screenshot.wait"), false);
107 | } else {
108 | saveScreenshot(framebuffer);
109 | }
110 | }
111 | });
112 |
113 | ClientTickEvents.END_CLIENT_TICK.register(client -> {
114 | if (ConfigHandler.instance.getConfig().enableDynamicResolution && client.world != null
115 | && getWindow().getX() != -32000) {
116 | DynamicResolutionHandler.INSTANCE.tick();
117 | }
118 | });
119 |
120 | ServerWorldEvents.LOAD.register((server, world) -> {
121 | if (ConfigHandler.instance.getConfig().enableDynamicResolution) {
122 | DynamicResolutionHandler.INSTANCE.reset();
123 | }
124 | });
125 |
126 | optifineInstalled = FabricLoader.getInstance().isModLoaded("optifabric");
127 | }
128 |
129 | private void saveScreenshot(Framebuffer fb) {
130 | ScreenshotRecorder.saveScreenshot(client.runDirectory,
131 | RCUtil.getScreenshotFilename(client.runDirectory).toString(),
132 | fb, text -> client.player.sendMessage(text, false));
133 | }
134 |
135 | public void setShouldScale(boolean shouldScale) {
136 | if (shouldScale == this.shouldScale) return;
137 |
138 | // if (getScaleFactor() == 1) return;
139 |
140 | Window window = getWindow();
141 | if (framebuffer == null) {
142 | this.shouldScale = true; // so we get the right dimensions
143 | framebuffer = new WindowFramebuffer(
144 | window.getFramebufferWidth(),
145 | window.getFramebufferHeight()
146 | );
147 | calculateSize();
148 | }
149 |
150 | this.shouldScale = shouldScale;
151 |
152 | client.getProfiler().swap(shouldScale ? "startScaling" : "finishScaling");
153 |
154 | // swap out framebuffers as needed
155 | if (shouldScale) {
156 | clientFramebuffer = client.getFramebuffer();
157 |
158 | if (screenshot) {
159 | resizeMinecraftFramebuffers();
160 |
161 | if (!isScreenshotFramebufferAlwaysAllocated() && screenshotFrameBuffer != null) {
162 | screenshotFrameBuffer.delete();
163 | }
164 |
165 | if (screenshotFrameBuffer == null) {
166 | initScreenshotFramebuffer();
167 | }
168 |
169 | setClientFramebuffer(screenshotFrameBuffer);
170 |
171 | screenshotFrameBuffer.beginWrite(true);
172 | } else {
173 | setClientFramebuffer(framebuffer);
174 |
175 | framebuffer.beginWrite(true);
176 | }
177 | // nothing on the client's framebuffer yet
178 | } else {
179 | setClientFramebuffer(clientFramebuffer);
180 | client.getFramebuffer().beginWrite(true);
181 |
182 | // Screenshot framebuffer
183 | if (screenshot) {
184 | saveScreenshot(screenshotFrameBuffer);
185 |
186 | if (!isScreenshotFramebufferAlwaysAllocated()) {
187 | screenshotFrameBuffer.delete();
188 | screenshotFrameBuffer = null;
189 | }
190 |
191 | screenshot = false;
192 | resizeMinecraftFramebuffers();
193 | } else {
194 | framebuffer.draw(
195 | window.getFramebufferWidth(),
196 | window.getFramebufferHeight()
197 | );
198 | }
199 | }
200 |
201 | client.getProfiler().swap("level");
202 | }
203 |
204 | public void initMinecraftFramebuffers() {
205 | if (minecraftFramebuffers != null) {
206 | minecraftFramebuffers.clear();
207 | } else {
208 | minecraftFramebuffers = new HashSet<>();
209 | }
210 |
211 | minecraftFramebuffers.add(client.worldRenderer.getEntityOutlinesFramebuffer());
212 | minecraftFramebuffers.add(client.worldRenderer.getTranslucentFramebuffer());
213 | minecraftFramebuffers.add(client.worldRenderer.getEntityFramebuffer());
214 | minecraftFramebuffers.add(client.worldRenderer.getParticlesFramebuffer());
215 | minecraftFramebuffers.add(client.worldRenderer.getWeatherFramebuffer());
216 | minecraftFramebuffers.add(client.worldRenderer.getCloudsFramebuffer());
217 | minecraftFramebuffers.remove(null);
218 | }
219 |
220 | public Framebuffer getFramebuffer() {
221 | return framebuffer;
222 | }
223 |
224 | public void initScreenshotFramebuffer() {
225 | if (Objects.nonNull(screenshotFrameBuffer)) screenshotFrameBuffer.delete();
226 |
227 | screenshotFrameBuffer = new WindowFramebuffer(
228 | getScreenshotWidth(), getScreenshotHeight()
229 | );
230 | }
231 |
232 | public float getScaleFactor() {
233 | return Config.getInstance().scaleFactor;
234 | }
235 |
236 | public void setScaleFactor(float scaleFactor) {
237 | Config.getInstance().scaleFactor = scaleFactor;
238 |
239 | updateFramebufferSize();
240 |
241 | ConfigHandler.instance.saveConfig();
242 | }
243 |
244 | public ScalingAlgorithm getUpscaleAlgorithm() {
245 | return Config.getInstance().upscaleAlgorithm;
246 | }
247 |
248 | public void setUpscaleAlgorithm(ScalingAlgorithm algorithm) {
249 | if (algorithm == Config.getInstance().upscaleAlgorithm) return;
250 |
251 | Config.getInstance().upscaleAlgorithm = algorithm;
252 |
253 | onResolutionChanged();
254 |
255 | ConfigHandler.instance.saveConfig();
256 | }
257 |
258 | public void nextUpscaleAlgorithm() {
259 | ScalingAlgorithm currentAlgorithm = getUpscaleAlgorithm();
260 | if (currentAlgorithm.equals(ScalingAlgorithm.NEAREST)) {
261 | setUpscaleAlgorithm(ScalingAlgorithm.LINEAR);
262 | } else {
263 | setUpscaleAlgorithm(ScalingAlgorithm.NEAREST);
264 | }
265 | }
266 |
267 | public ScalingAlgorithm getDownscaleAlgorithm() {
268 | return Config.getInstance().downscaleAlgorithm;
269 | }
270 |
271 | public void setDownscaleAlgorithm(ScalingAlgorithm algorithm) {
272 | if (algorithm == Config.getInstance().downscaleAlgorithm) return;
273 |
274 | Config.getInstance().downscaleAlgorithm = algorithm;
275 |
276 | onResolutionChanged();
277 |
278 | ConfigHandler.instance.saveConfig();
279 | }
280 |
281 | public void nextDownscaleAlgorithm() {
282 | ScalingAlgorithm currentAlgorithm = getDownscaleAlgorithm();
283 | if (currentAlgorithm.equals(ScalingAlgorithm.NEAREST)) {
284 | setDownscaleAlgorithm(ScalingAlgorithm.LINEAR);
285 | } else {
286 | setDownscaleAlgorithm(ScalingAlgorithm.NEAREST);
287 | }
288 | }
289 |
290 | public double getCurrentScaleFactor() {
291 | return shouldScale ?
292 | Config.getInstance().enableDynamicResolution ?
293 | DynamicResolutionHandler.INSTANCE.getCurrentScale() : Config.getInstance().scaleFactor : 1;
294 | }
295 |
296 | public boolean getOverrideScreenshotScale() {
297 | return Config.getInstance().overrideScreenshotScale;
298 | }
299 |
300 | public void setOverrideScreenshotScale(boolean value) {
301 | Config.getInstance().overrideScreenshotScale = value;
302 | if (value && isScreenshotFramebufferAlwaysAllocated()) {
303 | initScreenshotFramebuffer();
304 | } else {
305 | if (screenshotFrameBuffer != null) {
306 | screenshotFrameBuffer.delete();
307 | screenshotFrameBuffer = null;
308 | }
309 | }
310 | }
311 |
312 | public int getScreenshotWidth() {
313 | return Math.max(Config.getInstance().screenshotWidth, 1);
314 | }
315 |
316 | public void setScreenshotWidth(int width) {
317 | Config.getInstance().screenshotWidth = width;
318 | }
319 |
320 | public int getScreenshotHeight() {
321 | return Math.max(Config.getInstance().screenshotHeight, 1);
322 | }
323 |
324 | public void setScreenshotHeight(int height) {
325 | Config.getInstance().screenshotHeight = height;
326 | }
327 |
328 | public boolean isScreenshotFramebufferAlwaysAllocated() {
329 | return Config.getInstance().screenshotFramebufferAlwaysAllocated;
330 | }
331 |
332 | public void setScreenshotFramebufferAlwaysAllocated(boolean value) {
333 | Config.getInstance().screenshotFramebufferAlwaysAllocated = value;
334 |
335 | if (value) {
336 | if (getOverrideScreenshotScale() && Objects.isNull(this.screenshotFrameBuffer)) {
337 | initScreenshotFramebuffer();
338 | }
339 | } else {
340 | if (this.screenshotFrameBuffer != null) {
341 | this.screenshotFrameBuffer.delete();
342 | this.screenshotFrameBuffer = null;
343 | }
344 | }
345 | }
346 |
347 | public void setEnableDynamicResolution(boolean enableDynamicResolution) {
348 | Config.getInstance().enableDynamicResolution = enableDynamicResolution;
349 | }
350 |
351 | public void onResolutionChanged() {
352 | if (getWindow() == null)
353 | return;
354 |
355 | LOGGER.info("Size changed to {}x{} {}x{} {}x{}",
356 | getWindow().getFramebufferWidth(), getWindow().getFramebufferHeight(),
357 | getWindow().getWidth(), getWindow().getHeight(),
358 | getWindow().getScaledWidth(), getWindow().getScaledHeight());
359 |
360 | // if (getWindow().getScaledHeight() == lastWidth
361 | // || getWindow().getScaledHeight() == lastHeight)
362 | // {
363 | updateFramebufferSize();
364 |
365 | lastWidth = getWindow().getScaledHeight();
366 | lastHeight = getWindow().getScaledHeight();
367 | // }
368 |
369 |
370 | }
371 |
372 | public void updateFramebufferSize() {
373 | if (framebuffer == null)
374 | return;
375 |
376 | resize(framebuffer);
377 | resize(client.worldRenderer.getEntityOutlinesFramebuffer());
378 | // resizeMinecraftFramebuffers();
379 |
380 | calculateSize();
381 | }
382 |
383 | public void resizeMinecraftFramebuffers() {
384 | initMinecraftFramebuffers();
385 | minecraftFramebuffers.forEach(this::resize);
386 | }
387 |
388 | public void calculateSize() {
389 | currentWidth = framebuffer.textureWidth;
390 | currentHeight = framebuffer.textureHeight;
391 |
392 | // Framebuffer uses color (4 x 8 = 32 bit int) and depth (32 bit float)
393 | estimatedMemory = (long) currentWidth * currentHeight * 8;
394 | }
395 |
396 | public void resize(@Nullable Framebuffer framebuffer) {
397 | if (framebuffer == null) return;
398 |
399 | boolean prev = shouldScale;
400 | shouldScale = true;
401 | if (screenshot) {
402 | framebuffer.resize(
403 | getScreenshotWidth(),
404 | getScreenshotHeight(),
405 | MinecraftClient.IS_SYSTEM_MAC
406 | );
407 | } else {
408 | framebuffer.resize(
409 | getWindow().getFramebufferWidth(),
410 | getWindow().getFramebufferHeight(),
411 | MinecraftClient.IS_SYSTEM_MAC
412 | );
413 | }
414 | shouldScale = prev;
415 | }
416 |
417 | private Window getWindow() {
418 | return client.getWindow();
419 | }
420 |
421 | private void setClientFramebuffer(Framebuffer framebuffer) {
422 | client.framebuffer = framebuffer;
423 | }
424 |
425 | public KeyBinding getSettingsKey() {
426 | return settingsKey;
427 | }
428 |
429 | public int getCurrentWidth() {
430 | return currentWidth;
431 | }
432 |
433 | public int getCurrentHeight() {
434 | return currentHeight;
435 | }
436 |
437 | public long getEstimatedMemory() {
438 | return estimatedMemory;
439 | }
440 |
441 | public boolean isScreenshotting() {
442 | return screenshot;
443 | }
444 |
445 | public boolean isOptifineInstalled() {
446 | return optifineInstalled;
447 | }
448 |
449 | public void saveSettings() {
450 | ConfigHandler.instance.saveConfig();
451 | }
452 |
453 | public void setLastSettingsScreen(Class extends SettingsScreen> ordinal) {
454 | this.lastSettingsScreen = ordinal;
455 | }
456 |
457 | }
458 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/client/gui/screen/InfoSettingsScreen.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.client.gui.screen;
2 |
3 | import com.mojang.blaze3d.systems.RenderSystem;
4 | import net.minecraft.client.gui.screen.Screen;
5 | import net.minecraft.client.util.math.MatrixStack;
6 | import org.apache.commons.lang3.StringUtils;
7 | import org.jetbrains.annotations.Nullable;
8 | import org.lwjgl.opengl.GL11;
9 |
10 | public class InfoSettingsScreen extends SettingsScreen {
11 | private String gpuName;
12 | private int maxTextureSize;
13 |
14 | public InfoSettingsScreen(@Nullable Screen parent) {
15 | super(text("settings.info"), parent);
16 | }
17 |
18 | @Override
19 | protected void init() {
20 | super.init();
21 |
22 | String[] gpuInfoSplit = StringUtils.split(GL11.glGetString(GL11.GL_RENDERER));
23 | this.gpuName = "";
24 | // Clean GPU string
25 | for (int i = 1, gpuInfoSplitLength = gpuInfoSplit.length; i < gpuInfoSplitLength; i++) {
26 | String s = gpuInfoSplit[i];
27 | if (s.contains("/")) {
28 | break;
29 | } else {
30 | this.gpuName += s + " ";
31 | }
32 | }
33 |
34 | this.maxTextureSize = RenderSystem.maxSupportedTextureSize();
35 | }
36 |
37 | @Override
38 | public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
39 | super.render(matrices, mouseX, mouseY, delta);
40 |
41 | drawLeftAlignedString(matrices,
42 | " \u00A78" + text("settings.info.gpu").getString() + " \u00A7r" + gpuName,
43 | centerX - 75, centerY - 35,
44 | 0x000000);
45 |
46 | drawLeftAlignedString(matrices,
47 | " \u00A78" + text("settings.info.maxTextureSize").getString() + " \u00A7r" + maxTextureSize,
48 | centerX - 75, centerY - 20,
49 | 0x000000);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/client/gui/screen/MainSettingsScreen.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.client.gui.screen;
2 |
3 | import io.github.ultimateboomer.resolutioncontrol.ResolutionControlMod;
4 | import io.github.ultimateboomer.resolutioncontrol.util.RCUtil;
5 | import net.minecraft.client.gui.screen.Screen;
6 | import net.minecraft.client.gui.widget.ButtonWidget;
7 | import net.minecraft.client.gui.widget.TextFieldWidget;
8 | import net.minecraft.client.util.math.MatrixStack;
9 | import net.minecraft.text.LiteralText;
10 | import net.minecraft.text.Text;
11 | import net.minecraft.util.Identifier;
12 | import org.apache.commons.lang3.ArrayUtils;
13 | import org.apache.commons.lang3.math.NumberUtils;
14 | import org.jetbrains.annotations.Nullable;
15 | import org.lwjgl.glfw.GLFW;
16 |
17 |
18 | @SuppressWarnings("FieldCanBeLocal")
19 | public final class MainSettingsScreen extends SettingsScreen {
20 | private static final Identifier backgroundTexture = ResolutionControlMod.identifier("textures/gui/settings.png");
21 |
22 | private static final float[] scaleValues = {0.0f, 0.01f, 0.025f, 0.05f, 0.1f, 0.25f, 0.5f, 0.75f, 1.0f,
23 | 1.25f, 1.5f, 2.0f, 3.0f, 4.0f, 6.0f, 8.0f};
24 |
25 | private static final double redValue = 2.0;
26 |
27 | private static final Text increaseText = new LiteralText("+");
28 | private static final Text decreaseText = new LiteralText("-");
29 | private static final Text setText = new LiteralText("S");
30 | private static final Text resetText = new LiteralText("R");
31 | private static final Text cancelText = new LiteralText("C");
32 |
33 | private ButtonWidget increaseButton;
34 | private ButtonWidget decreaseButton;
35 | private ButtonWidget setButton;
36 | private ButtonWidget cancelOrResetButton;
37 |
38 | private TextFieldWidget entryTextField;
39 |
40 | private ButtonWidget upscaleAlgoButton;
41 | private ButtonWidget downscaleAlgoButton;
42 |
43 | private boolean manualEntry = false;
44 |
45 | public MainSettingsScreen(@Nullable Screen parent) {
46 | super(text("settings.main"), parent);
47 | }
48 |
49 | @Override
50 | protected void init() {
51 | super.init();
52 |
53 | int buttonSize = 20;
54 | int buttonOffset = buttonSize / 2;
55 | int buttonY = centerY + 15 - buttonSize / 2;
56 | int textFieldSize = 40;
57 |
58 | decreaseButton = new ButtonWidget(
59 | centerX - 55 - buttonOffset - buttonSize / 2, buttonY,
60 | buttonSize, buttonSize,
61 | decreaseText,
62 | button -> changeScaleFactor(false));
63 | this.addDrawableChild(decreaseButton);
64 |
65 | increaseButton = new ButtonWidget(
66 | centerX - 55 + buttonOffset - buttonSize / 2, buttonY,
67 | buttonSize, buttonSize,
68 | increaseText,
69 | button -> changeScaleFactor(true)
70 | );
71 | this.addDrawableChild(increaseButton);
72 |
73 | setButton = new ButtonWidget(
74 | centerX - 55 - buttonOffset - buttonSize / 2, buttonY + buttonSize,
75 | buttonSize, buttonSize,
76 | setText,
77 | button -> {
78 | setManualEntry(!manualEntry, false);
79 | }
80 | );
81 | this.addDrawableChild(setButton);
82 |
83 | cancelOrResetButton = new ButtonWidget(
84 | centerX - 55 - buttonOffset + buttonSize / 2, buttonY + buttonSize,
85 | buttonSize, buttonSize,
86 | resetText,
87 | button -> {
88 | if (manualEntry) {
89 | setManualEntry(false, true);
90 | } else {
91 | mod.setScaleFactor(1.0f);
92 | updateButtons();
93 | }
94 | }
95 | );
96 | this.addDrawableChild(cancelOrResetButton);
97 |
98 | entryTextField = new TextFieldWidget(client.textRenderer,
99 | centerX - 55 - textFieldSize / 2, centerY - 36,
100 | textFieldSize, buttonSize, LiteralText.EMPTY);
101 | entryTextField.setVisible(false);
102 | this.addDrawableChild(entryTextField);
103 |
104 | upscaleAlgoButton = new ButtonWidget(
105 | centerX + 15, centerY - 28,
106 | 60, buttonSize,
107 | mod.getUpscaleAlgorithm().getText(),
108 | button -> {
109 | mod.nextUpscaleAlgorithm();
110 | button.setMessage(mod.getUpscaleAlgorithm().getText());
111 | }
112 | );
113 | this.addDrawableChild(upscaleAlgoButton);
114 |
115 | downscaleAlgoButton = new ButtonWidget(
116 | centerX + 15, centerY + 8,
117 | 60, buttonSize,
118 | mod.getDownscaleAlgorithm().getText(),
119 | button -> {
120 | mod.nextDownscaleAlgorithm();
121 | button.setMessage(mod.getDownscaleAlgorithm().getText());
122 | }
123 | );
124 | this.addDrawableChild(downscaleAlgoButton);
125 |
126 | updateButtons();
127 | }
128 |
129 | @Override
130 | public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
131 | if (this.manualEntry) {
132 | if (keyCode == GLFW.GLFW_KEY_ENTER) {
133 | this.setManualEntry(false, false);
134 | return true;
135 | } else if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
136 | this.setManualEntry(false, true);
137 | return true;
138 | } else {
139 | return super.keyPressed(keyCode, scanCode, modifiers);
140 | }
141 | } else {
142 | return super.keyPressed(keyCode, scanCode, modifiers);
143 | }
144 | }
145 |
146 | @Override
147 | public void render(MatrixStack matrices, int mouseX, int mouseY, float time) {
148 | super.render(matrices, mouseX, mouseY, time);
149 |
150 | if (!this.manualEntry) {
151 | drawCenteredString(matrices, String.format("\u00a7%s%s\u00a7rx",
152 | mod.getScaleFactor() > redValue ? "4" : "0", mod.getScaleFactor()),
153 | centerX - 55, centerY - 36, 0x000000);
154 |
155 | drawCenteredString(matrices, String.format("\u00a78%sx%s\u00a7r",
156 | ResolutionControlMod.getInstance().getCurrentWidth(),
157 | ResolutionControlMod.getInstance().getCurrentHeight()),
158 | centerX - 55, centerY - 24, 0x000000);
159 |
160 | drawCenteredString(matrices, "\u00a78" + text("settings.main.estimate",
161 | RCUtil.formatMetric(ResolutionControlMod.getInstance().getEstimatedMemory()) + "B")
162 | .getString() + "\u00a7r",
163 | centerX - 55, centerY - 12, 0x000000);
164 | }
165 |
166 | drawLeftAlignedString(matrices,
167 | "\u00a78" + text("settings.main.upscale").getString(),
168 | centerX + 15, centerY - 40, 0x000000);
169 | drawLeftAlignedString(matrices,
170 | "\u00a78" + text("settings.main.downscale").getString(),
171 | centerX + 15, centerY - 5, 0x000000);
172 |
173 |
174 | }
175 |
176 | @Override
177 | public void tick() {
178 | if (manualEntry) {
179 | if (!this.getFocused().equals(entryTextField)) {
180 | this.focusOn(entryTextField);
181 | }
182 |
183 | if (!entryTextField.active) {
184 | entryTextField.active = true;
185 | }
186 | }
187 |
188 | entryTextField.tick();
189 | super.tick();
190 | }
191 |
192 | private void changeScaleFactor(boolean add) {
193 | float currentScale = mod.getScaleFactor();
194 | int nextIndex = ArrayUtils.indexOf(scaleValues, currentScale);
195 | if (nextIndex == -1) {
196 | for (int i = -1; i < scaleValues.length; ++i) {
197 | double scale1 = i == -1 ? 0.0 : scaleValues[i];
198 | double scale2 = i == scaleValues.length - 1 ? Double.POSITIVE_INFINITY : scaleValues[i + 1];
199 |
200 | if (currentScale > scale1 && currentScale < scale2) {
201 | nextIndex = i + (add ? 1 : 0);
202 | break;
203 | }
204 | }
205 | } else {
206 | nextIndex += add ? 1 : -1;
207 | }
208 |
209 | mod.setScaleFactor(scaleValues[nextIndex]);
210 |
211 | updateButtons();
212 | }
213 |
214 | private void updateButtons() {
215 | increaseButton.active = mod.getScaleFactor() < scaleValues[scaleValues.length - 1];
216 | decreaseButton.active = mod.getScaleFactor() > scaleValues[0];
217 | }
218 |
219 | public void setManualEntry(boolean manualEntry, boolean cancel) {
220 | this.manualEntry = manualEntry;
221 | if (manualEntry) {
222 | entryTextField.setText(String.valueOf(mod.getScaleFactor()));
223 | entryTextField.setVisible(true);
224 | entryTextField.setSelectionStart(0);
225 | entryTextField.setSelectionEnd(entryTextField.getText().length());
226 | entryTextField.active = true;
227 | cancelOrResetButton.setMessage(cancelText);
228 | increaseButton.active = false;
229 | decreaseButton.active = false;
230 | this.focusOn(entryTextField);
231 | } else {
232 | if (!cancel) {
233 | String text = entryTextField.getText();
234 | if (NumberUtils.isParsable(text)) {
235 | float value = Math.abs(Float.parseFloat(text));
236 | mod.setScaleFactor(value);
237 | }
238 | }
239 |
240 | entryTextField.setVisible(false);
241 | setButton.setMessage(setText);
242 | cancelOrResetButton.setMessage(resetText);
243 | increaseButton.active = true;
244 | decreaseButton.active = true;
245 |
246 | updateButtons();
247 | }
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/client/gui/screen/ScreenshotSettingsScreen.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.client.gui.screen;
2 |
3 | import io.github.ultimateboomer.resolutioncontrol.util.RCUtil;
4 | import net.minecraft.client.gui.screen.Screen;
5 | import net.minecraft.client.gui.widget.ButtonWidget;
6 | import net.minecraft.client.gui.widget.TextFieldWidget;
7 | import net.minecraft.client.util.math.MatrixStack;
8 | import net.minecraft.text.LiteralText;
9 | import net.minecraft.text.Text;
10 | import org.apache.commons.lang3.math.NumberUtils;
11 | import org.jetbrains.annotations.Nullable;
12 |
13 | public class ScreenshotSettingsScreen extends SettingsScreen {
14 | private static final double[] scaleValues = {0.1, 0.25, 0.5, 1.0,
15 | 2.0, 3.0, 4.0, 6.0, 8.0, 16.0};
16 |
17 | private static final Text increaseText = new LiteralText("x2");
18 | private static final Text decreaseText = new LiteralText("/2");
19 | private static final Text resetText = new LiteralText("R");
20 |
21 | private TextFieldWidget widthTextField;
22 | private TextFieldWidget heightTextField;
23 |
24 | private ButtonWidget increaseButton;
25 | private ButtonWidget decreaseButton;
26 | private ButtonWidget resetButton;
27 |
28 | private ButtonWidget toggleOverrideSizeButton;
29 | private ButtonWidget toggleAlwaysAllocatedButton;
30 |
31 | private final int buttonSize = 20;
32 | private final int textFieldSize = 40;
33 |
34 | private long estimatedSize;
35 |
36 | public ScreenshotSettingsScreen(@Nullable Screen parent) {
37 | super(text("settings.screenshot"), parent);
38 | }
39 |
40 | @Override
41 | protected void init() {
42 | super.init();
43 |
44 | toggleOverrideSizeButton = new ButtonWidget(
45 | centerX + 20, centerY - 40,
46 | 50, 20,
47 | getStateText(mod.getOverrideScreenshotScale()),
48 | button -> {
49 | mod.setOverrideScreenshotScale(!mod.getOverrideScreenshotScale());
50 | button.setMessage(getStateText(mod.getOverrideScreenshotScale()));
51 | }
52 | );
53 | this.addDrawableChild(toggleOverrideSizeButton);
54 |
55 | toggleAlwaysAllocatedButton = new ButtonWidget(
56 | centerX + 20, centerY - 20,
57 | 50, 20,
58 | getStateText(mod.isScreenshotFramebufferAlwaysAllocated()),
59 | button -> {
60 | mod.setScreenshotFramebufferAlwaysAllocated(!mod.isScreenshotFramebufferAlwaysAllocated());
61 | button.setMessage(getStateText(mod.isScreenshotFramebufferAlwaysAllocated()));
62 | }
63 | );
64 | this.addDrawableChild(toggleAlwaysAllocatedButton);
65 |
66 | widthTextField = new TextFieldWidget(client.textRenderer,
67 | centerX - 85, centerY + 7,
68 | textFieldSize, buttonSize,
69 | LiteralText.EMPTY);
70 | widthTextField.setText(String.valueOf(mod.getScreenshotWidth()));
71 | this.addDrawableChild(widthTextField);
72 |
73 | heightTextField = new TextFieldWidget(client.textRenderer,
74 | centerX - 35, centerY + 7,
75 | textFieldSize, buttonSize,
76 | LiteralText.EMPTY);
77 | heightTextField.setText(String.valueOf(mod.getScreenshotHeight()));
78 | this.addDrawableChild(heightTextField);
79 |
80 | increaseButton = new ButtonWidget(
81 | centerX - 10 - 60, centerY + 35,
82 | 20, 20,
83 | increaseText,
84 | button -> multiply(2.0));
85 | this.addDrawableChild(increaseButton);
86 |
87 | decreaseButton = new ButtonWidget(
88 | centerX + 10 - 60, centerY + 35,
89 | 20, 20,
90 | decreaseText,
91 | button -> multiply(0.5));
92 | this.addDrawableChild(decreaseButton);
93 |
94 | resetButton = new ButtonWidget(
95 | centerX + 30 - 60, centerY + 35,
96 | 20, 20,
97 | resetText,
98 | button -> resetSize());
99 | this.addDrawableChild(resetButton);
100 |
101 | calculateSize();
102 | }
103 |
104 | @Override
105 | public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
106 | super.render(matrices, mouseX, mouseY, delta);
107 |
108 | drawLeftAlignedString(matrices,
109 | "\u00a78" + text("settings.screenshot.overrideSize").getString(),
110 | centerX - 75, centerY - 35,
111 | 0x000000);
112 |
113 | drawLeftAlignedString(matrices,
114 | "\u00a78" + text("settings.screenshot.alwaysAllocated").getString(),
115 | centerX - 75, centerY - 15,
116 | 0x000000);
117 |
118 | drawLeftAlignedString(matrices,
119 | "\u00a78x",
120 | centerX - 42.5f, centerY + 12,
121 | 0x000000);
122 |
123 | drawLeftAlignedString(matrices,
124 | "\u00a78" + text("settings.main.estimate").getString()
125 | + " " + RCUtil.formatMetric(estimatedSize) + "B",
126 | centerX + 25, centerY + 12,
127 | 0x000000);
128 | }
129 |
130 | @Override
131 | public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
132 | calculateSize();
133 | return super.keyPressed(keyCode, scanCode, modifiers);
134 | }
135 |
136 | @Override
137 | public void tick() {
138 | widthTextField.tick();
139 | heightTextField.tick();
140 | super.tick();
141 | }
142 |
143 | @Override
144 | protected void applySettingsAndCleanup() {
145 | if (NumberUtils.isParsable(widthTextField.getText())
146 | && NumberUtils.isParsable(heightTextField.getText())) {
147 | int newWidth = (int) Math.abs(Double.parseDouble(widthTextField.getText()));
148 | int newHeight = (int) Math.abs(Double.parseDouble(heightTextField.getText()));
149 |
150 | if (newWidth != mod.getScreenshotWidth() || newHeight != mod.getScreenshotHeight()) {
151 | mod.setScreenshotWidth(newWidth);
152 | mod.setScreenshotHeight(newHeight);
153 |
154 | if (mod.isScreenshotFramebufferAlwaysAllocated()) {
155 | mod.initScreenshotFramebuffer();
156 | }
157 | }
158 | }
159 | super.applySettingsAndCleanup();
160 | }
161 |
162 | private void multiply(double mul) {
163 | if (NumberUtils.isParsable(widthTextField.getText())
164 | && NumberUtils.isParsable(heightTextField.getText())) {
165 | widthTextField.setText(String.valueOf(
166 | (int) Math.abs(Double.parseDouble(widthTextField.getText()) * mul)));
167 | heightTextField.setText(String.valueOf(
168 | (int) Math.abs(Double.parseDouble(heightTextField.getText()) * mul)));
169 | calculateSize();
170 | }
171 | }
172 |
173 | private void resetSize() {
174 | mod.setScreenshotWidth(3840);
175 | mod.setScreenshotHeight(2160);
176 | widthTextField.setText(String.valueOf(mod.getScreenshotWidth()));
177 | heightTextField.setText(String.valueOf(mod.getScreenshotHeight()));
178 | }
179 |
180 | private void calculateSize() {
181 | if (NumberUtils.isParsable(widthTextField.getText())
182 | && NumberUtils.isParsable(heightTextField.getText())) {
183 | estimatedSize = (long) (Double.parseDouble(widthTextField.getText())
184 | * Double.parseDouble(heightTextField.getText()) * 8);
185 | }
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/client/gui/screen/SettingsScreen.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.client.gui.screen;
2 |
3 | import com.mojang.blaze3d.systems.RenderSystem;
4 | import io.github.ultimateboomer.resolutioncontrol.ResolutionControlMod;
5 | import net.minecraft.client.gui.screen.Screen;
6 | import net.minecraft.client.gui.widget.ButtonWidget;
7 | import net.minecraft.client.util.math.MatrixStack;
8 | import net.minecraft.text.Text;
9 | import net.minecraft.text.TranslatableText;
10 | import net.minecraft.util.Identifier;
11 | import org.apache.commons.lang3.mutable.MutableInt;
12 | import org.jetbrains.annotations.Nullable;
13 |
14 | import java.util.LinkedHashMap;
15 | import java.util.Map;
16 | import java.util.function.Function;
17 |
18 | @SuppressWarnings("StaticInitializerReferencesSubClass")
19 | public class SettingsScreen extends Screen {
20 | protected static final Identifier WINDOW_TEXTURE = ResolutionControlMod.identifier("textures/gui/settings.png");
21 |
22 | protected static Text text(String path, Object... args) {
23 | return new TranslatableText(ResolutionControlMod.MOD_ID + "." + path, args);
24 | }
25 |
26 | protected static final int containerWidth = 192;
27 | protected static final int containerHeight = 128;
28 |
29 | protected static final Map,
30 | Function> screensSupplierList;
31 |
32 | static {
33 | screensSupplierList = new LinkedHashMap<>();
34 | screensSupplierList.put(MainSettingsScreen.class, MainSettingsScreen::new);
35 | screensSupplierList.put(ScreenshotSettingsScreen.class, ScreenshotSettingsScreen::new);
36 | screensSupplierList.put(InfoSettingsScreen.class, InfoSettingsScreen::new);
37 | }
38 |
39 | protected final ResolutionControlMod mod = ResolutionControlMod.getInstance();
40 |
41 | @Nullable
42 | protected final Screen parent;
43 |
44 | protected int centerX;
45 | protected int centerY;
46 | protected int startX;
47 | protected int startY;
48 |
49 | protected Map, ButtonWidget> menuButtons;
50 |
51 | protected ButtonWidget doneButton;
52 |
53 | protected SettingsScreen(Text title, @Nullable Screen parent) {
54 | super(title);
55 | this.parent = parent;
56 | }
57 |
58 | @Override
59 | protected void init() {
60 | super.init();
61 |
62 | centerX = width / 2;
63 | centerY = height / 2;
64 | startX = centerX - containerWidth / 2;
65 | startY = centerY - containerHeight / 2;
66 |
67 | // Init menu buttons
68 | menuButtons = new LinkedHashMap<>();
69 | final int menuButtonWidth = 80;
70 | final int menuButtonHeight = 20;
71 | MutableInt o = new MutableInt();
72 |
73 | screensSupplierList.forEach((c, constructor) -> {
74 | SettingsScreen r = constructor.apply(this.parent);
75 | ButtonWidget b = new ButtonWidget(
76 | startX - menuButtonWidth - 20, startY + o.getValue(),
77 | menuButtonWidth, menuButtonHeight,
78 | r.getTitle(),
79 | button -> client.setScreen(constructor.apply(this.parent))
80 | );
81 |
82 | if (this.getClass().equals(c))
83 | b.active = false;
84 |
85 | menuButtons.put(c, b);
86 | o.add(25);
87 | });
88 |
89 | menuButtons.values().forEach(this::addDrawableChild);
90 |
91 | doneButton = new ButtonWidget(
92 | centerX + 15, startY + containerHeight - 30,
93 | 60, 20,
94 | new TranslatableText("gui.done"),
95 | button -> {
96 | applySettingsAndCleanup();
97 | client.setScreen(this.parent);
98 | }
99 | );
100 | this.addDrawableChild(doneButton);
101 | }
102 |
103 | @Override
104 | public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
105 | if (client.world == null) {
106 | renderBackgroundTexture(0);
107 | }
108 |
109 | RenderSystem.disableDepthTest();
110 | RenderSystem.setShaderTexture(0, WINDOW_TEXTURE);
111 | RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f);
112 |
113 | int textureWidth = 256;
114 | int textureHeight = 192;
115 | drawTexture(
116 | matrices,
117 | centerX - textureWidth / 2, centerY - textureHeight / 2,
118 | 0, 0,
119 | textureWidth, textureHeight
120 | );
121 |
122 | super.render(matrices, mouseX, mouseY, delta);
123 |
124 | drawLeftAlignedString(matrices, "\u00a7r" + getTitle().getString(),
125 | centerX + 15, startY + 10, 0x000000);
126 |
127 | drawRightAlignedString(matrices, text("settings.title").getString(),
128 | centerX + 5, startY + 10, 0x404040);
129 | }
130 |
131 | @Override
132 | public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
133 | if ((ResolutionControlMod.getInstance().getSettingsKey().matchesKey(keyCode, scanCode))) {
134 | this.applySettingsAndCleanup();
135 | this.client.setScreen(this.parent);
136 | this.client.mouse.lockCursor();
137 | return true;
138 | } else {
139 | return super.keyPressed(keyCode, scanCode, modifiers);
140 | }
141 | }
142 |
143 | @Override
144 | public void onClose() {
145 | this.applySettingsAndCleanup();
146 | super.onClose();
147 | }
148 |
149 | protected void applySettingsAndCleanup() {
150 | mod.saveSettings();
151 | mod.setLastSettingsScreen(this.getClass());
152 | };
153 |
154 | @SuppressWarnings("IntegerDivisionInFloatingPointContext")
155 | protected void drawCenteredString(MatrixStack matrices, String text, float x, float y, int color) {
156 | textRenderer.draw(matrices, text, x - textRenderer.getWidth(text) / 2, y, color);
157 | }
158 |
159 | protected void drawLeftAlignedString(MatrixStack matrices, String text, float x, float y, int color) {
160 | textRenderer.draw(matrices, text, x, y, color);
161 | }
162 |
163 | protected void drawRightAlignedString(MatrixStack matrices, String text, float x, float y, int color) {
164 | textRenderer.draw(matrices, text, x - textRenderer.getWidth(text), y, color);
165 | }
166 |
167 | public static SettingsScreen getScreen(Class extends SettingsScreen> screenClass) {
168 | return screensSupplierList.get(screenClass).apply(null);
169 | }
170 |
171 | protected static Text getStateText(boolean enabled) {
172 | return enabled ? new TranslatableText("addServer.resourcePack.enabled")
173 | : new TranslatableText("addServer.resourcePack.disabled");
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/compat/modmenu/ModMenuInfo.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.compat.modmenu;
2 |
3 | import com.terraformersmc.modmenu.api.ConfigScreenFactory;
4 | import com.terraformersmc.modmenu.api.ModMenuApi;
5 | import io.github.ultimateboomer.resolutioncontrol.client.gui.screen.MainSettingsScreen;
6 |
7 | public final class ModMenuInfo implements ModMenuApi {
8 | @Override
9 | public ConfigScreenFactory> getModConfigScreenFactory() {
10 | return MainSettingsScreen::new;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/mixin/FramebufferMixin.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.mixin;
2 |
3 | import com.mojang.blaze3d.platform.GlStateManager;
4 | import io.github.ultimateboomer.resolutioncontrol.ResolutionControlMod;
5 | import io.github.ultimateboomer.resolutioncontrol.util.Config;
6 | import net.minecraft.client.MinecraftClient;
7 | import net.minecraft.client.gl.Framebuffer;
8 | import net.minecraft.util.math.MathHelper;
9 | import org.lwjgl.opengl.GL11;
10 | import org.lwjgl.opengl.GL12;
11 | import org.lwjgl.opengl.GL45;
12 | import org.spongepowered.asm.mixin.Mixin;
13 | import org.spongepowered.asm.mixin.Shadow;
14 | import org.spongepowered.asm.mixin.Unique;
15 | import org.spongepowered.asm.mixin.injection.At;
16 | import org.spongepowered.asm.mixin.injection.Inject;
17 | import org.spongepowered.asm.mixin.injection.Redirect;
18 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
19 |
20 | import java.nio.IntBuffer;
21 |
22 | @Mixin(Framebuffer.class)
23 | public abstract class FramebufferMixin {
24 | @Unique private boolean isMipmapped;
25 | @Unique private float scaleMultiplier;
26 |
27 | @Shadow public abstract int getColorAttachment();
28 |
29 | @Inject(method = "initFbo", at = @At("HEAD"))
30 | private void onInitFbo(int width, int height, boolean getError, CallbackInfo ci) {
31 | scaleMultiplier = (float) width / MinecraftClient.getInstance().getWindow().getWidth();
32 | isMipmapped = Config.getInstance().mipmapHighRes && scaleMultiplier > 2.0f;
33 | }
34 |
35 |
36 | @Redirect(method = "*", at = @At(value = "INVOKE",
37 | target = "Lcom/mojang/blaze3d/platform/GlStateManager;_texParameter(III)V"))
38 | private void onSetTexFilter(int target, int pname, int param) {
39 | if (pname == GL11.GL_TEXTURE_MIN_FILTER) {
40 | GlStateManager._texParameter(target, pname,
41 | ResolutionControlMod.getInstance().getUpscaleAlgorithm().getId(isMipmapped));
42 | } else if (pname == GL11.GL_TEXTURE_MAG_FILTER) {
43 | GlStateManager._texParameter(target, pname,
44 | ResolutionControlMod.getInstance().getDownscaleAlgorithm().getId(false));
45 | } else if (pname == GL11.GL_TEXTURE_WRAP_S || pname == GL11.GL_TEXTURE_WRAP_T) {
46 | // Fix linear scaling creating black borders
47 | GlStateManager._texParameter(target, pname, GL12.GL_CLAMP_TO_EDGE);
48 | } else {
49 | GlStateManager._texParameter(target, pname, param);
50 | }
51 | }
52 |
53 | @Redirect(method = "initFbo", at = @At(value = "INVOKE",
54 | target = "Lcom/mojang/blaze3d/platform/GlStateManager;_texImage2D(IIIIIIIILjava/nio/IntBuffer;)V"))
55 | private void onTexImage(int target, int level, int internalFormat, int width, int height, int border, int format,
56 | int type, IntBuffer pixels) {
57 | if (isMipmapped) {
58 | int mipmapLevel = MathHelper.ceil(Math.log(scaleMultiplier) / Math.log(2));
59 | for (int i = 0; i < mipmapLevel; i++) {
60 | GlStateManager._texImage2D(target, i, internalFormat,
61 | width << i, height << i,
62 | border, format, type, pixels);
63 | }
64 | } else {
65 | GlStateManager._texImage2D(target, 0, internalFormat, width, height, border, format, type, pixels);
66 | }
67 |
68 | }
69 |
70 | @Inject(method = "drawInternal", at = @At("HEAD"))
71 | private void onDraw(int width, int height, boolean bl, CallbackInfo ci) {
72 | if (isMipmapped) {
73 | GlStateManager._bindTexture(this.getColorAttachment());
74 | GL45.glGenerateMipmap(GL11.GL_TEXTURE_2D);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/mixin/GameRendererMixin.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.mixin;
2 |
3 | import net.minecraft.client.render.GameRenderer;
4 | import org.spongepowered.asm.mixin.Mixin;
5 | import org.spongepowered.asm.mixin.injection.At;
6 | import org.spongepowered.asm.mixin.injection.Inject;
7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
8 | import io.github.ultimateboomer.resolutioncontrol.ResolutionControlMod;
9 |
10 | @Mixin(GameRenderer.class)
11 | public abstract class GameRendererMixin {
12 | @Inject(at = @At("HEAD"), method = "renderWorld")
13 | private void onRenderWorldBegin(CallbackInfo callbackInfo) {
14 | ResolutionControlMod.getInstance().setShouldScale(true);
15 | }
16 |
17 | @Inject(at = @At("RETURN"), method = "renderWorld")
18 | private void onRenderWorldEnd(CallbackInfo callbackInfo) {
19 | ResolutionControlMod.getInstance().setShouldScale(false);
20 | }
21 |
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/mixin/MinecraftClientMixin.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.mixin;
2 |
3 | import io.github.ultimateboomer.resolutioncontrol.ResolutionControlMod;
4 | import net.minecraft.client.MinecraftClient;
5 | import org.spongepowered.asm.mixin.Mixin;
6 | import org.spongepowered.asm.mixin.injection.At;
7 | import org.spongepowered.asm.mixin.injection.Inject;
8 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
9 |
10 | @Mixin(MinecraftClient.class)
11 | public abstract class MinecraftClientMixin {
12 | @Inject(method = "", at = @At(value = "NEW", target = "net/minecraft/client/gl/WindowFramebuffer"))
13 | private void onInitFramebuffer(CallbackInfo ci) {
14 | ResolutionControlMod mod = ResolutionControlMod.getInstance();
15 | if (mod.isScreenshotFramebufferAlwaysAllocated()) {
16 | mod.initScreenshotFramebuffer();
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/mixin/WindowMixin.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.mixin;
2 |
3 | import io.github.ultimateboomer.resolutioncontrol.ResolutionControlMod;
4 | import net.minecraft.client.util.Window;
5 | import net.minecraft.util.math.MathHelper;
6 | import org.spongepowered.asm.mixin.Mixin;
7 | import org.spongepowered.asm.mixin.injection.At;
8 | import org.spongepowered.asm.mixin.injection.Inject;
9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
11 |
12 | @Mixin(Window.class)
13 | public abstract class WindowMixin {
14 | @Inject(at = @At("RETURN"), method = "getFramebufferWidth", cancellable = true)
15 | private void getFramebufferWidth(CallbackInfoReturnable ci) {
16 | if (ResolutionControlMod.getInstance().isScreenshotting()) {
17 | ci.setReturnValue(ResolutionControlMod.getInstance().getScreenshotWidth());
18 | } else {
19 | ci.setReturnValue(scale(ci.getReturnValueI()));
20 | }
21 | }
22 |
23 | @Inject(at = @At("RETURN"), method = "getFramebufferHeight", cancellable = true)
24 | private void getFramebufferHeight(CallbackInfoReturnable ci) {
25 | if (ResolutionControlMod.getInstance().isScreenshotting()) {
26 | ci.setReturnValue(ResolutionControlMod.getInstance().getScreenshotHeight());
27 | } else {
28 | ci.setReturnValue(scale(ci.getReturnValueI()));
29 | }
30 | }
31 |
32 | private int scale(int value) {
33 | double scaleFactor = ResolutionControlMod.getInstance().getCurrentScaleFactor();
34 | return Math.max(MathHelper.ceil((double) value * scaleFactor), 1);
35 | }
36 |
37 | @Inject(at = @At("RETURN"), method = "getScaleFactor", cancellable = true)
38 | private void getScaleFactor(CallbackInfoReturnable ci) {
39 | ci.setReturnValue(ci.getReturnValueD() * ResolutionControlMod.getInstance().getCurrentScaleFactor());
40 | }
41 |
42 | @Inject(at = @At("RETURN"), method = "onFramebufferSizeChanged")
43 | private void onFramebufferSizeChanged(CallbackInfo ci) {
44 | ResolutionControlMod.getInstance().onResolutionChanged();
45 | }
46 |
47 | @Inject(at = @At("RETURN"), method = "updateFramebufferSize")
48 | private void onUpdateFramebufferSize(CallbackInfo ci) {
49 | ResolutionControlMod.getInstance().onResolutionChanged();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/mixin/WorldRendererMixin.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.mixin;
2 |
3 | import io.github.ultimateboomer.resolutioncontrol.ResolutionControlMod;
4 | import net.minecraft.client.gl.Framebuffer;
5 | import net.minecraft.client.render.WorldRenderer;
6 | import org.spongepowered.asm.mixin.Mixin;
7 | import org.spongepowered.asm.mixin.Shadow;
8 | import org.spongepowered.asm.mixin.injection.At;
9 | import org.spongepowered.asm.mixin.injection.Inject;
10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
11 |
12 | @Mixin(WorldRenderer.class)
13 | public class WorldRendererMixin {
14 | @Shadow
15 | private Framebuffer entityOutlinesFramebuffer;
16 |
17 | @Inject(at = @At("RETURN"), method = "loadEntityOutlineShader")
18 | private void onLoadEntityOutlineShader(CallbackInfo ci) {
19 | ResolutionControlMod.getInstance().resizeMinecraftFramebuffers();
20 | }
21 |
22 | @Inject(at = @At("RETURN"), method = "onResized")
23 | private void onOnResized(CallbackInfo ci) {
24 | if (entityOutlinesFramebuffer == null) return;
25 | ResolutionControlMod.getInstance().resizeMinecraftFramebuffers();
26 | }
27 |
28 | // @Inject(at = @At("RETURN"), method = "loadTransparencyShader")
29 | // private void onLoadTransparencyShader(CallbackInfo ci) {
30 | // ResolutionControlMod.getInstance().resizeMinecraftFramebuffers();
31 | // }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/util/Config.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.util;
2 |
3 | public final class Config {
4 | public float scaleFactor = 1.0f;
5 |
6 | public ScalingAlgorithm upscaleAlgorithm = ScalingAlgorithm.NEAREST;
7 | public ScalingAlgorithm downscaleAlgorithm = ScalingAlgorithm.LINEAR;
8 |
9 | public boolean mipmapHighRes = false;
10 |
11 | public boolean overrideScreenshotScale = true;
12 |
13 | public int screenshotWidth = 3840;
14 | public int screenshotHeight = 2160;
15 |
16 | public boolean screenshotFramebufferAlwaysAllocated = false;
17 |
18 | public boolean enableDynamicResolution = false;
19 |
20 | public float drMinScale = 0.5f;
21 | public float drMaxScale = 2.0f;
22 | public float drResStep = 0.0625f;
23 | public int drMinFps = 60;
24 | public int drMaxFps = 70;
25 | public int drFpsSmoothAmount = 10;
26 |
27 | public static Config getInstance() {
28 | return ConfigHandler.instance.getConfig();
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/util/ConfigHandler.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.util;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.GsonBuilder;
5 | import net.minecraft.client.MinecraftClient;
6 | import io.github.ultimateboomer.resolutioncontrol.ResolutionControlMod;
7 |
8 | import java.io.*;
9 |
10 | public final class ConfigHandler {
11 | public static final ConfigHandler instance = new ConfigHandler();
12 |
13 | private static File configFile() {
14 | return new File(MinecraftClient.getInstance().runDirectory, "config/" + ResolutionControlMod.MOD_ID + ".json");
15 | }
16 |
17 | private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
18 | private Config config = new Config();
19 |
20 | public Config getConfig() {
21 | return config;
22 | }
23 |
24 | private ConfigHandler() {
25 | loadConfig();
26 | }
27 |
28 | public void loadConfig() {
29 | File configFile = configFile();
30 | if (!configFile.exists()) {
31 | config = new Config();
32 | saveConfig();
33 | }
34 |
35 | try (FileReader reader = new FileReader(configFile)) {
36 | config = gson.fromJson(reader, Config.class);
37 | } catch (IOException e) {
38 | System.err.println("Could not load config file at " + configFile.getAbsolutePath());
39 | e.printStackTrace();
40 | }
41 | }
42 |
43 | public void saveConfig() {
44 | File configFile = configFile();
45 | configFile.getParentFile().mkdirs();
46 |
47 | try (final Writer writer = new FileWriter(configFile)) {
48 | writer.write(gson.toJson(config));
49 | } catch (IOException e) {
50 | System.err.println("Could not save config file at " + configFile.getAbsolutePath());
51 | e.printStackTrace();
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/util/DynamicResolutionHandler.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.util;
2 |
3 | import io.github.ultimateboomer.resolutioncontrol.ResolutionControlMod;
4 | import it.unimi.dsi.fastutil.floats.FloatArrayList;
5 | import it.unimi.dsi.fastutil.floats.FloatList;
6 | import net.minecraft.client.MinecraftClient;
7 |
8 | public class DynamicResolutionHandler {
9 | public static final DynamicResolutionHandler INSTANCE = new DynamicResolutionHandler();
10 | private final FloatList scales = new FloatArrayList();
11 | private int baseScale;
12 |
13 | private int timer = 10;
14 | private int currentScale;
15 |
16 | private DynamicResolutionHandler() {
17 | reset();
18 | }
19 |
20 | public void tick() {
21 | timer--;
22 |
23 | if (timer <= 0) {
24 | update();
25 | }
26 | }
27 |
28 | public void reset() {
29 | for (float i = Config.getInstance().drMinScale; i <= Config.getInstance().drMaxScale;
30 | i += Config.getInstance().drResStep) {
31 | scales.add(i);
32 |
33 | if (i == 1.0f) {
34 | baseScale = scales.size() - 1;
35 | currentScale = baseScale;
36 | }
37 | }
38 | }
39 |
40 | private void update() {
41 | MinecraftClient client = MinecraftClient.getInstance();
42 |
43 | final int smoothAmount = Config.getInstance().drFpsSmoothAmount;
44 | float sum = 0;
45 | for (int i = client.metricsData.getCurrentIndex() - smoothAmount;
46 | i < client.metricsData.getCurrentIndex(); i++) {
47 |
48 | sum += client.metricsData.getSamples()[Math.floorMod(i, 240)];
49 | }
50 | float fps = 1_000_000_000.0f / (sum / smoothAmount);
51 |
52 | if (fps > Config.getInstance().drMaxFps) {
53 | setCurrentScale(Math.min(currentScale + 1, scales.size() - 1));
54 | timer = 15;
55 | } else if (fps < Config.getInstance().drMinFps) {
56 | setCurrentScale(Math.max(currentScale - 1, 0));
57 | timer = 5;
58 | } else {
59 | timer = 3;
60 | }
61 | }
62 |
63 | private void setCurrentScale(int currentScale) {
64 | boolean equal = this.currentScale == currentScale;
65 | this.currentScale = currentScale;
66 |
67 | if (!equal) {
68 | ResolutionControlMod.getInstance().updateFramebufferSize();
69 | }
70 | }
71 |
72 | public double getCurrentScale() {
73 | return scales.getFloat(currentScale);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/util/RCUtil.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.util;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.text.DateFormat;
6 | import java.text.NumberFormat;
7 | import java.text.SimpleDateFormat;
8 | import java.util.Date;
9 |
10 | public final class RCUtil {
11 | private static final String[] UNITS = "KMGTPE".split("");
12 | private static final NumberFormat FORMAT = NumberFormat.getNumberInstance();
13 |
14 | private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss");
15 |
16 | /**
17 | * Format number with metric conversion (K, M, G etc)
18 | */
19 | public static String formatMetric(long n) {
20 | if (n < 1000) {
21 | return n + " ";
22 | }
23 |
24 | int log10 = (int) Math.log10(n);
25 | int decimalPlace = (int) Math.pow(10, 2 - log10 % 3);
26 | int displayDigits = (int) (n / Math.pow(10, log10 - 2));
27 |
28 | float result = (float) displayDigits / decimalPlace;
29 | return String.format("%s %s", FORMAT.format(result), UNITS[log10 / 3 - 1]);
30 | }
31 |
32 | public static File getScreenshotFilename(File directory) {
33 | String string = DATE_FORMAT.format(new Date());
34 | int i = 1;
35 |
36 | while (true) {
37 | File file = new File(new File(""), "fb" + string + (i == 1 ? "" : "_" + i) + ".png");
38 | File entireDirectory = new File(new File(directory, "screenshots"), file.toString());
39 | if (!entireDirectory.exists()) {
40 | try {
41 | entireDirectory.createNewFile();
42 | } catch (IOException e) {
43 | throw new IllegalStateException(e);
44 | }
45 | return file;
46 | }
47 |
48 | ++i;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/io/github/ultimateboomer/resolutioncontrol/util/ScalingAlgorithm.java:
--------------------------------------------------------------------------------
1 | package io.github.ultimateboomer.resolutioncontrol.util;
2 |
3 | import net.minecraft.text.Text;
4 | import net.minecraft.text.TranslatableText;
5 | import org.lwjgl.opengl.GL11;
6 |
7 | public enum ScalingAlgorithm {
8 | NEAREST(new TranslatableText("resolutioncontrol.settings.main.nearest"),
9 | GL11.GL_NEAREST, GL11.GL_NEAREST_MIPMAP_NEAREST),
10 | LINEAR(new TranslatableText("resolutioncontrol.settings.main.linear"),
11 | GL11.GL_LINEAR, GL11.GL_LINEAR_MIPMAP_NEAREST);
12 |
13 | private final Text text;
14 | private final int id;
15 | private final int idMipped;
16 |
17 | ScalingAlgorithm(Text text, int id, int idMipped) {
18 | this.text = text;
19 | this.id = id;
20 | this.idMipped = idMipped;
21 | }
22 |
23 | public Text getText() {
24 | return text;
25 | }
26 |
27 | public int getId(boolean mipped) {
28 | return mipped ? idMipped : id;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/resources/assets/resolutioncontrol/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trisk/Resolution-Control/c6a679090351b1ba0a1383dccc44ef2c28344eaf/src/main/resources/assets/resolutioncontrol/icon.png
--------------------------------------------------------------------------------
/src/main/resources/assets/resolutioncontrol/lang/en_us.json:
--------------------------------------------------------------------------------
1 | {
2 | "key.categories.resolutioncontrol": "Resolution Control",
3 |
4 | "key.resolutioncontrol.settings": "Resolution Control Settings",
5 | "key.resolutioncontrol.screenshot": "Screenshot Framebuffer",
6 |
7 | "resolutioncontrol.settings.title": "Resolution Control",
8 |
9 | "resolutioncontrol.settings.main": "Main",
10 |
11 | "resolutioncontrol.settings.main.upscale": "Hi-res filter:",
12 | "resolutioncontrol.settings.main.downscale": "Lo-res filter:",
13 | "resolutioncontrol.settings.main.linear": "Linear",
14 | "resolutioncontrol.settings.main.nearest": "Nearest",
15 | "resolutioncontrol.settings.main.estimate": "Est: %s",
16 |
17 | "resolutioncontrol.settings.screenshot": "Screenshots",
18 | "resolutioncontrol.settings.screenshot.overrideSize": "Use Set Size:",
19 | "resolutioncontrol.settings.screenshot.alwaysAllocated": "Always Allocated:",
20 | "resolutioncontrol.settings.screenshot.size": "Size:",
21 |
22 | "resolutioncontrol.settings.info": "Info",
23 | "resolutioncontrol.settings.info.gpu": "GPU:",
24 | "resolutioncontrol.settings.info.maxTextureSize": "Max Texture Size:",
25 |
26 | "resolutioncontrol.screenshot.wait": "Processing screenshot..."
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/resources/assets/resolutioncontrol/textures/gui/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trisk/Resolution-Control/c6a679090351b1ba0a1383dccc44ef2c28344eaf/src/main/resources/assets/resolutioncontrol/textures/gui/settings.png
--------------------------------------------------------------------------------
/src/main/resources/assets/resolutioncontrol/textures/gui/settings.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trisk/Resolution-Control/c6a679090351b1ba0a1383dccc44ef2c28344eaf/src/main/resources/assets/resolutioncontrol/textures/gui/settings.psd
--------------------------------------------------------------------------------
/src/main/resources/fabric.mod.json:
--------------------------------------------------------------------------------
1 | {
2 | "schemaVersion": 1,
3 | "id": "resolutioncontrol",
4 | "version": "${version}",
5 |
6 | "name": "ResolutionControl+",
7 | "description": "Gives you more control over minecraft's render resolution.",
8 | "authors": [
9 | "juliand665", "UltimateBoomer"
10 | ],
11 | "contact": {
12 | "homepage": "https://ultimateboomer.github.io/",
13 | "sources": "https://github.com/UltimateBoomer/Resolution-Control",
14 | "issues": "https://github.com/UltimateBoomer/Resolution-Control/issues"
15 | },
16 |
17 | "license": "MIT",
18 | "icon": "assets/resolutioncontrol/icon.png",
19 |
20 | "environment": "client",
21 | "entrypoints": {
22 | "main": [
23 | "io.github.ultimateboomer.resolutioncontrol.ResolutionControlMod"
24 | ],
25 | "modmenu": [
26 | "io.github.ultimateboomer.resolutioncontrol.compat.modmenu.ModMenuInfo"
27 | ]
28 | },
29 | "mixins": [
30 | "resolutioncontrol.mixins.json"
31 | ],
32 | "accessWidener": "resolutioncontrol.accesswidener",
33 | "depends": {
34 | "fabricloader": ">=0.12.8",
35 | "fabric": "*",
36 | "minecraft": ">=1.18",
37 | "java": ">=17"
38 | },
39 | "suggests": {
40 | "flamingo": "*"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/resources/resolutioncontrol.accesswidener:
--------------------------------------------------------------------------------
1 | accessWidener v1 named
2 |
3 | accessible field net/minecraft/client/MinecraftClient framebuffer Lnet/minecraft/client/gl/Framebuffer;
4 | accessible field net/minecraft/client/MinecraftClient currentFps I
5 |
6 | mutable field net/minecraft/client/MinecraftClient framebuffer Lnet/minecraft/client/gl/Framebuffer;
7 |
8 | accessible field net/minecraft/client/render/WorldRenderer entityOutlinesFramebuffer Lnet/minecraft/client/gl/Framebuffer;
9 | accessible field net/minecraft/client/render/WorldRenderer translucentFramebuffer Lnet/minecraft/client/gl/Framebuffer;
10 | accessible field net/minecraft/client/render/WorldRenderer entityFramebuffer Lnet/minecraft/client/gl/Framebuffer;
11 | accessible field net/minecraft/client/render/WorldRenderer particlesFramebuffer Lnet/minecraft/client/gl/Framebuffer;
12 | accessible field net/minecraft/client/render/WorldRenderer weatherFramebuffer Lnet/minecraft/client/gl/Framebuffer;
13 | accessible field net/minecraft/client/render/WorldRenderer cloudsFramebuffer Lnet/minecraft/client/gl/Framebuffer;
14 |
15 | accessible field net/minecraft/client/util/Window framebufferWidth I
16 | accessible field net/minecraft/client/util/Window framebufferHeight I
--------------------------------------------------------------------------------
/src/main/resources/resolutioncontrol.mixins.json:
--------------------------------------------------------------------------------
1 | {
2 | "required": true,
3 | "package": "io.github.ultimateboomer.resolutioncontrol.mixin",
4 | "compatibilityLevel": "JAVA_17",
5 | "client": [
6 | "GameRendererMixin",
7 | "MinecraftClientMixin",
8 | "WindowMixin",
9 | "FramebufferMixin",
10 | "WorldRendererMixin"
11 | ],
12 | "injectors": {
13 | "defaultRequire": 1
14 | }
15 | }
16 |
--------------------------------------------------------------------------------