+18
.gitignore
+18
.gitignore
+7
.idea/fileColors.xml
+7
.idea/fileColors.xml
+3
.idea/scopes/Gradle.xml
+3
.idea/scopes/Gradle.xml
+3
.idea/scopes/Qodana.xml
+3
.idea/scopes/Qodana.xml
+10
CHANGELOG.md
+10
CHANGELOG.md
···
1
+
# Changelog
2
+
3
+
All notable changes to this project will be documented in this file.
4
+
5
+
The format is based on [Keep a Changelog](https://keepachangelog.com),
6
+
and this project follows the [Ragnarök Versioning Convention](https://github.com/Red-Studio-Ragnarok/Commons/blob/main/Ragnar%C3%B6k%20Versioning%20Convention.md).
7
+
8
+
## OptiNotFine Version 1.0 Changelog - 2026-01-09
9
+
10
+
Initial Release
+21
LICENSE
+21
LICENSE
···
1
+
MIT License
2
+
3
+
Copyright (c) 2025 Luna Mira Lage (Desoroxxx)
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.
+59
README.md
+59
README.md
···
1
+
[](https://www.curseforge.com/minecraft/mc-mods/optinotfine)
2
+
[](https://modrinth.com/mod/optinotfine)
3
+
4
+
[](https://www.buymeacoffee.com/desoroxxx)
5
+
[](https://discord.gg/hKpUYx7VwS)
6
+
7
+
[](https://adoptium.net/temurin/releases/?version=8)
8
+
[](https://gradle.org/)
9
+
[](http://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.12.2.html)
10
+
11
+
# OptiNotFine
12
+
13
+
Fixing OptiFine, until something better comes.
14
+
15
+
## Features
16
+
17
+
- **Stop Log Spam**
18
+
- This removes some logging calls that OptiFine will spam over and over again, here’s a list of them:
19
+
- `[HH:MM:SS] [Client thread/WARN] [Config]: [OptiFine] Ambiguous shader option: {}`
20
+
- `[HH:MM:SS] [Client thread/WARN] [Config]: [OptiFine] Unknown macro value: {}`
21
+
- `[HH:MM:SS] [Client thread/WARN] [Config]: [OptiFine] Shaders: Item not found: {}`
22
+
- `[HH:MM:SS] [Client thread/WARN] [Config]: [OptiFine] Shaders: Block not found for name: {}`
23
+
- `[HH:MM:SS] [Client thread/WARN] [Config]: [OptiFine] Shaders: Invalid item ID mapping: {}`
24
+
- `[HH:MM:SS] [Client thread/WARN] [Config]: [OptiFine] Shaders: Invalid block ID mapping: {}`
25
+
- `[HH:MM:SS] [Client thread/WARN] [Config]: [OptiFine] Shaders: Invalid entity ID mapping: {}`
26
+
- `[HH:MM:SS] [Client thread/INFO] [net.optifine.shaders.SMCLog]: {}`
27
+
28
+
## Fixes
29
+
30
+
- **Fix missing methods**
31
+
- Currently, OptiNotFine re-adds missing methods that OptiFine accidentally removes; here’s a list of them:
32
+
- `net.minecraft.client.resources.AbstractResourcePack#getResourcePackFile()`
33
+
- **Fix broken F3 native memory usage display on Java 17+**
34
+
- In Java 17+ the classes OptiFine tries to use to display native memory usage have been moved to a different package, OptiNotFine fixes this by adding the new locations to the list of classes to use.
35
+
36
+
## FAQ
37
+
38
+
- What happens if OptiFine isn’t present?
39
+
- Nothing, this can be safely included in any modpack.
40
+
- This doesn't fix X?
41
+
- If you have found an issue with OptiFine, create an issue.
42
+
43
+
---
44
+
45
+
[](https://bisecthosting.com/Desoroxxx?r=OptiNotFine+Tangled)
46
+
47
+
# Want to have your own mod or support me?
48
+
49
+
If you're looking for a mod but don't have the development skills or time, consider commissioning me!
50
+
My commissions are currently open, and I’d be happy to create a custom mod to fit your needs as long as you provide assets.
51
+
52
+
[Commissions](https://www.buymeacoffee.com/desoroxxx/commissions)
53
+
54
+
You can also support me on a monthly basis by becoming a member.
55
+
To thank you, you’ll have the possibility to access exclusive post and messages, Discord channel for WIP content, and even access to unreleased Prototypes or WIP Projects.
56
+
57
+
[Membership](https://www.buymeacoffee.com/desoroxxx/membership)
58
+
59
+
You can also [buy me a hot chocolate](https://www.buymeacoffee.com/desoroxxx).
+178
build.gradle.kts
+178
build.gradle.kts
···
1
+
import org.gradle.plugins.ide.idea.model.IdeaLanguageLevel
2
+
import org.jetbrains.gradle.ext.runConfigurations
3
+
import org.jetbrains.gradle.ext.settings
4
+
5
+
plugins {
6
+
id("org.jetbrains.gradle.plugin.idea-ext") version "1.3"
7
+
id("com.gtnewhorizons.retrofuturagradle") version "2.0.2"
8
+
id("com.github.gmazzo.buildconfig") version "6.0.7"
9
+
id("io.freefair.lombok") version "9.1.0"
10
+
}
11
+
12
+
group = "dev.redstudio"
13
+
version = "1.0" // Versioning must follow Ragnarök versioning convention: https://github.com/Red-Studio-Ragnarok/Commons/blob/main/Ragnar%C3%B6k%20Versioning%20Convention.md
14
+
15
+
val id = project.name.lowercase()
16
+
val plugin = "${project.group}.${id}.asm.${project.name}Plugin"
17
+
18
+
val jvmCommonArgs = "-Dfile.encoding=UTF-8 -XX:+UseStringDeduplication"
19
+
20
+
val redCoreVersion = "0.7"
21
+
22
+
val configAnytimeVersion = "3.0"
23
+
24
+
minecraft {
25
+
mcVersion = "1.12.2"
26
+
username = "Desoroxxx"
27
+
extraRunJvmArguments = listOf("-Dforge.logging.console.level=debug", "-Dfml.coreMods.load=${plugin}") + jvmCommonArgs.split(" ")
28
+
}
29
+
30
+
repositories {
31
+
arrayOf("Release", "Beta", "Dev").forEach { repoType ->
32
+
maven {
33
+
name = "Red Studio - $repoType"
34
+
url = uri("https://repo.redstudio.dev/${repoType.lowercase()}")
35
+
content {
36
+
includeGroup("dev.redstudio")
37
+
}
38
+
}
39
+
}
40
+
41
+
maven {
42
+
name = "Cleanroom"
43
+
url = uri("https://maven.cleanroommc.com")
44
+
content {
45
+
includeGroup("com.cleanroommc")
46
+
}
47
+
}
48
+
49
+
mavenCentral()
50
+
mavenLocal()
51
+
}
52
+
53
+
dependencies {
54
+
implementation("dev.redstudio:Red-Core-MC:1.8-1.12-$redCoreVersion")
55
+
56
+
implementation("com.cleanroommc:configanytime:$configAnytimeVersion")
57
+
}
58
+
59
+
buildConfig {
60
+
packageName("${project.group}.${id}")
61
+
className("ProjectConstants")
62
+
documentation.set("This class defines constants for ${project.name}.\n<p>\nThey are automatically updated by Gradle.")
63
+
useJavaOutput()
64
+
65
+
// Details
66
+
buildConfigField("ID",id)
67
+
buildConfigField("NAME", project.name)
68
+
buildConfigField("VERSION", project.version.toString())
69
+
70
+
// Versions
71
+
buildConfigField("RED_CORE_VERSION", redCoreVersion)
72
+
buildConfigField("CONFIG_ANYTIME_VERSION", configAnytimeVersion)
73
+
74
+
// Loggers
75
+
buildConfigField("org.apache.logging.log4j.Logger", "LOGGER", "org.apache.logging.log4j.LogManager.getLogger(NAME)")
76
+
buildConfigField("dev.redstudio.redcore.logging.RedLogger", "RED_LOGGER", """new RedLogger(NAME, "https://tangled.org/desoroxxx.redstudio.dev/${project.name}/issues/new", LOGGER)""")
77
+
}
78
+
79
+
// Set the toolchain version to decouple the Java we run Gradle with from the Java used to compile and run the mod
80
+
java {
81
+
toolchain {
82
+
languageVersion.set(JavaLanguageVersion.of(8))
83
+
vendor.set(JvmVendorSpec.ADOPTIUM)
84
+
}
85
+
if (!project.version.toString().contains("Dev"))
86
+
withSourcesJar() // Generate sources jar, for releases
87
+
}
88
+
89
+
tasks {
90
+
processResources {
91
+
val expandProperties = mapOf(
92
+
"version" to project.version,
93
+
"name" to project.name,
94
+
"id" to id
95
+
)
96
+
97
+
inputs.properties(expandProperties)
98
+
99
+
filesMatching("**/*.*") {
100
+
val exclusions = arrayOf(".png")
101
+
if (!exclusions.any { path.endsWith(it) })
102
+
expand(expandProperties)
103
+
}
104
+
}
105
+
106
+
val processReadme by registering {
107
+
val input = rootProject.file("README.md")
108
+
val output = layout.buildDirectory.file("processed-readme/README.md")
109
+
110
+
inputs.file(input)
111
+
outputs.file(output)
112
+
113
+
doLast {
114
+
val content = input.readText()
115
+
val firstHash = content.indexOf('#')
116
+
val lastSeparator = content.lastIndexOf("---")
117
+
118
+
val trimmed = when {
119
+
firstHash != -1 && lastSeparator > firstHash -> content.substring(firstHash, lastSeparator)
120
+
firstHash != -1 -> content.substring(firstHash)
121
+
else -> content
122
+
}
123
+
124
+
output.get().asFile.parentFile.mkdirs()
125
+
output.get().asFile.writeText(trimmed.trim())
126
+
}
127
+
}
128
+
129
+
withType<Jar> {
130
+
dependsOn(processReadme)
131
+
from(processReadme.map { it.outputs.files })
132
+
from(rootProject.file("LICENSE"))
133
+
from(rootProject.file("CHANGELOG.md"))
134
+
135
+
manifest {
136
+
attributes(
137
+
"ModSide" to "CLIENT",
138
+
"FMLCorePlugin" to plugin,
139
+
"FMLCorePluginContainsFMLMod" to "true",
140
+
"ForceLoadAsMod" to "true"
141
+
)
142
+
}
143
+
}
144
+
145
+
withType<JavaCompile>{
146
+
options.encoding = "UTF-8"
147
+
148
+
options.isFork = true
149
+
options.forkOptions.jvmArgs = jvmCommonArgs.split(" ")
150
+
}
151
+
}
152
+
153
+
idea {
154
+
module {
155
+
inheritOutputDirs = true
156
+
excludeDirs.addAll(setOf(".gradle", ".idea", "build", "gradle", "run", "gradlew", "gradlew.bat", "desktop.ini").map(::file))
157
+
}
158
+
159
+
project {
160
+
settings {
161
+
jdkName = "1.8"
162
+
languageLevel = IdeaLanguageLevel("JDK_1_8")
163
+
164
+
165
+
runConfigurations {
166
+
listOf("Client", "Server", "Obfuscated Client", "Obfuscated Server", "Vanilla Client", "Vanilla Server").forEach { name ->
167
+
create(name, org.jetbrains.gradle.ext.Gradle::class.java) {
168
+
val prefix = name.substringBefore(" ").let { if (it == "Obfuscated") "Obf" else it }
169
+
val suffix = name.substringAfter(" ").takeIf { it != prefix } ?: ""
170
+
taskNames = setOf("run$prefix$suffix")
171
+
172
+
jvmArgs = jvmCommonArgs
173
+
}
174
+
}
175
+
}
176
+
}
177
+
}
178
+
}
+8
gradle.properties
+8
gradle.properties
···
1
+
org.gradle.jvmargs = -Dfile.encoding=UTF-8 -XX:+UseStringDeduplication -XX:+UseCompactObjectHeaders
2
+
3
+
org.gradle.caching = true
4
+
org.gradle.configuration-cache = true
5
+
org.gradle.configuration-cache.parallel = true
6
+
org.gradle.configureondemand = true
7
+
8
+
org.gradle.parallel = true
gradle/wrapper/gradle-wrapper.jar
gradle/wrapper/gradle-wrapper.jar
This is a binary file and will not be displayed.
+7
gradle/wrapper/gradle-wrapper.properties
+7
gradle/wrapper/gradle-wrapper.properties
+248
gradlew
+248
gradlew
···
1
+
#!/bin/sh
2
+
3
+
#
4
+
# Copyright © 2015 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
+
# SPDX-License-Identifier: Apache-2.0
19
+
#
20
+
21
+
##############################################################################
22
+
#
23
+
# Gradle start up script for POSIX generated by Gradle.
24
+
#
25
+
# Important for running:
26
+
#
27
+
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
28
+
# noncompliant, but you have some other compliant shell such as ksh or
29
+
# bash, then to run this script, type that shell name before the whole
30
+
# command line, like:
31
+
#
32
+
# ksh Gradle
33
+
#
34
+
# Busybox and similar reduced shells will NOT work, because this script
35
+
# requires all of these POSIX shell features:
36
+
# * functions;
37
+
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
38
+
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
39
+
# * compound commands having a testable exit status, especially «case»;
40
+
# * various built-in commands including «command», «set», and «ulimit».
41
+
#
42
+
# Important for patching:
43
+
#
44
+
# (2) This script targets any POSIX shell, so it avoids extensions provided
45
+
# by Bash, Ksh, etc; in particular arrays are avoided.
46
+
#
47
+
# The "traditional" practice of packing multiple parameters into a
48
+
# space-separated string is a well documented source of bugs and security
49
+
# problems, so this is (mostly) avoided, by progressively accumulating
50
+
# options in "$@", and eventually passing that to Java.
51
+
#
52
+
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
53
+
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
54
+
# see the in-line comments for details.
55
+
#
56
+
# There are tweaks for specific operating systems such as AIX, CygWin,
57
+
# Darwin, MinGW, and NonStop.
58
+
#
59
+
# (3) This script is generated from the Groovy template
60
+
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
61
+
# within the Gradle project.
62
+
#
63
+
# You can find Gradle at https://github.com/gradle/gradle/.
64
+
#
65
+
##############################################################################
66
+
67
+
# Attempt to set APP_HOME
68
+
69
+
# Resolve links: $0 may be a link
70
+
app_path=$0
71
+
72
+
# Need this for daisy-chained symlinks.
73
+
while
74
+
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
75
+
[ -h "$app_path" ]
76
+
do
77
+
ls=$( ls -ld "$app_path" )
78
+
link=${ls#*' -> '}
79
+
case $link in #(
80
+
/*) app_path=$link ;; #(
81
+
*) app_path=$APP_HOME$link ;;
82
+
esac
83
+
done
84
+
85
+
# This is normally unused
86
+
# shellcheck disable=SC2034
87
+
APP_BASE_NAME=${0##*/}
88
+
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
89
+
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
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
+
118
+
119
+
# Determine the Java command to use to start the JVM.
120
+
if [ -n "$JAVA_HOME" ] ; then
121
+
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
122
+
# IBM's JDK on AIX uses strange locations for the executables
123
+
JAVACMD=$JAVA_HOME/jre/sh/java
124
+
else
125
+
JAVACMD=$JAVA_HOME/bin/java
126
+
fi
127
+
if [ ! -x "$JAVACMD" ] ; then
128
+
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
129
+
130
+
Please set the JAVA_HOME variable in your environment to match the
131
+
location of your Java installation."
132
+
fi
133
+
else
134
+
JAVACMD=java
135
+
if ! command -v java >/dev/null 2>&1
136
+
then
137
+
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
138
+
139
+
Please set the JAVA_HOME variable in your environment to match the
140
+
location of your Java installation."
141
+
fi
142
+
fi
143
+
144
+
# Increase the maximum file descriptors if we can.
145
+
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
146
+
case $MAX_FD in #(
147
+
max*)
148
+
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
149
+
# shellcheck disable=SC2039,SC3045
150
+
MAX_FD=$( ulimit -H -n ) ||
151
+
warn "Could not query maximum file descriptor limit"
152
+
esac
153
+
case $MAX_FD in #(
154
+
'' | soft) :;; #(
155
+
*)
156
+
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
157
+
# shellcheck disable=SC2039,SC3045
158
+
ulimit -n "$MAX_FD" ||
159
+
warn "Could not set maximum file descriptor limit to $MAX_FD"
160
+
esac
161
+
fi
162
+
163
+
# Collect all arguments for the java command, stacking in reverse order:
164
+
# * args from the command line
165
+
# * the main class name
166
+
# * -classpath
167
+
# * -D...appname settings
168
+
# * --module-path (only if needed)
169
+
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
170
+
171
+
# For Cygwin or MSYS, switch paths to Windows format before running java
172
+
if "$cygwin" || "$msys" ; then
173
+
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
174
+
175
+
JAVACMD=$( cygpath --unix "$JAVACMD" )
176
+
177
+
# Now convert the arguments - kludge to limit ourselves to /bin/sh
178
+
for arg do
179
+
if
180
+
case $arg in #(
181
+
-*) false ;; # don't mess with options #(
182
+
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183
+
[ -e "$t" ] ;; #(
184
+
*) false ;;
185
+
esac
186
+
then
187
+
arg=$( cygpath --path --ignore --mixed "$arg" )
188
+
fi
189
+
# Roll the args list around exactly as many times as the number of
190
+
# args, so each arg winds up back in the position where it started, but
191
+
# possibly modified.
192
+
#
193
+
# NB: a `for` loop captures its iteration list before it begins, so
194
+
# changing the positional parameters here affects neither the number of
195
+
# iterations, nor the values presented in `arg`.
196
+
shift # remove old arg
197
+
set -- "$@" "$arg" # push replacement arg
198
+
done
199
+
fi
200
+
201
+
202
+
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203
+
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204
+
205
+
# Collect all arguments for the java command:
206
+
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207
+
# and any embedded shellness will be escaped.
208
+
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209
+
# treated as '${Hostname}' itself on the command line.
210
+
211
+
set -- \
212
+
"-Dorg.gradle.appname=$APP_BASE_NAME" \
213
+
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
214
+
"$@"
215
+
216
+
# Stop when "xargs" is not available.
217
+
if ! command -v xargs >/dev/null 2>&1
218
+
then
219
+
die "xargs is not available"
220
+
fi
221
+
222
+
# Use "xargs" to parse quoted args.
223
+
#
224
+
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
225
+
#
226
+
# In Bash we could simply go:
227
+
#
228
+
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
229
+
# set -- "${ARGS[@]}" "$@"
230
+
#
231
+
# but POSIX shell has neither arrays nor command substitution, so instead we
232
+
# post-process each arg (as a line of input to sed) to backslash-escape any
233
+
# character that might be a shell metacharacter, then use eval to reverse
234
+
# that process (while maintaining the separation between arguments), and wrap
235
+
# the whole thing up as a single "set" statement.
236
+
#
237
+
# This will of course break if any of these variables contains a newline or
238
+
# an unmatched quote.
239
+
#
240
+
241
+
eval "set -- $(
242
+
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
243
+
xargs -n1 |
244
+
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
245
+
tr '\n' ' '
246
+
)" '"$@"'
247
+
248
+
exec "$JAVACMD" "$@"
+93
gradlew.bat
+93
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
+
@rem SPDX-License-Identifier: Apache-2.0
17
+
@rem
18
+
19
+
@if "%DEBUG%"=="" @echo off
20
+
@rem ##########################################################################
21
+
@rem
22
+
@rem Gradle startup script for Windows
23
+
@rem
24
+
@rem ##########################################################################
25
+
26
+
@rem Set local scope for the variables with windows NT shell
27
+
if "%OS%"=="Windows_NT" setlocal
28
+
29
+
set DIRNAME=%~dp0
30
+
if "%DIRNAME%"=="" set DIRNAME=.
31
+
@rem This is normally unused
32
+
set APP_BASE_NAME=%~n0
33
+
set APP_HOME=%DIRNAME%
34
+
35
+
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
36
+
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37
+
38
+
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39
+
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40
+
41
+
@rem Find java.exe
42
+
if defined JAVA_HOME goto findJavaFromJavaHome
43
+
44
+
set JAVA_EXE=java.exe
45
+
%JAVA_EXE% -version >NUL 2>&1
46
+
if %ERRORLEVEL% equ 0 goto execute
47
+
48
+
echo. 1>&2
49
+
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50
+
echo. 1>&2
51
+
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52
+
echo location of your Java installation. 1>&2
53
+
54
+
goto fail
55
+
56
+
:findJavaFromJavaHome
57
+
set JAVA_HOME=%JAVA_HOME:"=%
58
+
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59
+
60
+
if exist "%JAVA_EXE%" goto execute
61
+
62
+
echo. 1>&2
63
+
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64
+
echo. 1>&2
65
+
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66
+
echo location of your Java installation. 1>&2
67
+
68
+
goto fail
69
+
70
+
:execute
71
+
@rem Setup the command line
72
+
73
+
74
+
75
+
@rem Execute Gradle
76
+
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
77
+
78
+
:end
79
+
@rem End local scope for the variables with windows NT shell
80
+
if %ERRORLEVEL% equ 0 goto mainEnd
81
+
82
+
:fail
83
+
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
84
+
rem the _cmd.exe /c_ return code!
85
+
set EXIT_CODE=%ERRORLEVEL%
86
+
if %EXIT_CODE% equ 0 set EXIT_CODE=1
87
+
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
88
+
exit /b %EXIT_CODE%
89
+
90
+
:mainEnd
91
+
if "%OS%"=="Windows_NT" endlocal
92
+
93
+
:omega
+22
settings.gradle.kts
+22
settings.gradle.kts
···
1
+
rootProject.name = "OptiNotFine"
2
+
3
+
pluginManagement {
4
+
repositories {
5
+
maven {
6
+
// RetroFuturaGradle
7
+
name = "GTNH Maven"
8
+
url = uri("https://nexus.gtnewhorizons.com/repository/public/")
9
+
mavenContent {
10
+
includeGroupByRegex("com\\.gtnewhorizons\\..+")
11
+
includeGroup("com.gtnewhorizons")
12
+
}
13
+
}
14
+
gradlePluginPortal()
15
+
mavenCentral()
16
+
mavenLocal()
17
+
}
18
+
}
19
+
20
+
plugins {
21
+
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" // Automatic toolchain provisioning
22
+
}
+25
src/main/java/dev/redstudio/optinotfine/OptiNotFine.java
+25
src/main/java/dev/redstudio/optinotfine/OptiNotFine.java
···
1
+
package dev.redstudio.optinotfine;
2
+
3
+
import net.minecraftforge.fml.common.Mod;
4
+
5
+
import static dev.redstudio.optinotfine.ProjectConstants.*;
6
+
7
+
// /$$$$$$ /$$ /$$ /$$ /$$ /$$ /$$$$$$$$ /$$
8
+
// /$$__ $$ | $$ |__/| $$$ | $$ | $$ | $$_____/|__/
9
+
// | $$ \ $$ /$$$$$$ /$$$$$$ /$$| $$$$| $$ /$$$$$$ /$$$$$$ | $$ /$$ /$$$$$$$ /$$$$$$
10
+
// | $$ | $$ /$$__ $$|_ $$_/ | $$| $$ $$ $$ /$$__ $$|_ $$_/ | $$$$$ | $$| $$__ $$ /$$__ $$
11
+
// | $$ | $$| $$ \ $$ | $$ | $$| $$ $$$$| $$ \ $$ | $$ | $$__/ | $$| $$ \ $$| $$$$$$$$
12
+
// | $$ | $$| $$ | $$ | $$ /$$| $$| $$\ $$$| $$ | $$ | $$ /$$| $$ | $$| $$ | $$| $$_____/
13
+
// | $$$$$$/| $$$$$$$/ | $$$$/| $$| $$ \ $$| $$$$$$/ | $$$$/| $$ | $$| $$ | $$| $$$$$$$
14
+
// \______/ | $$____/ \___/ |__/|__/ \__/ \______/ \___/ |__/ |__/|__/ |__/ \_______/
15
+
// | $$
16
+
// | $$
17
+
// |__/
18
+
19
+
/// Main class of OptiNotFine.
20
+
///
21
+
/// @author Luna Mira Lage (Desoroxxx)
22
+
/// @since 1.0
23
+
@Mod(clientSideOnly = true, modid = ID, name = NAME, version = VERSION, dependencies = "required-after:configanytime@[" + CONFIG_ANYTIME_VERSION + ",);;required-after:redcore@[" + RED_CORE_VERSION + ",);", updateJSON = "https://forge.curseupdate.com/1409147/" + ID)
24
+
public final class OptiNotFine {
25
+
}
+22
src/main/java/dev/redstudio/optinotfine/asm/OptiNotFinePlugin.java
+22
src/main/java/dev/redstudio/optinotfine/asm/OptiNotFinePlugin.java
···
1
+
package dev.redstudio.optinotfine.asm;
2
+
3
+
import dev.redstudio.redcore.asm.RedLoadingPlugin;
4
+
import dev.redstudio.redcore.utils.OptiNotFine;
5
+
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
6
+
7
+
import static dev.redstudio.optinotfine.ProjectConstants.ID;
8
+
9
+
/// @author Luna Mira Lage (Desoroxxx)
10
+
/// @since 1.0
11
+
@IFMLLoadingPlugin.MCVersion("1.12.2")
12
+
@IFMLLoadingPlugin.TransformerExclusions("dev.redstudio." + ID)
13
+
public final class OptiNotFinePlugin extends RedLoadingPlugin {
14
+
15
+
@Override
16
+
public String[] getASMTransformerClass() {
17
+
if (!OptiNotFine.isOptiFineInstalled())
18
+
return null;
19
+
20
+
return new String[]{OptiNotFineTransformer.class.getName()};
21
+
}
22
+
}
+303
src/main/java/dev/redstudio/optinotfine/asm/OptiNotFineTransformer.java
+303
src/main/java/dev/redstudio/optinotfine/asm/OptiNotFineTransformer.java
···
1
+
package dev.redstudio.optinotfine.asm;
2
+
3
+
import dev.redstudio.optinotfine.config.OptiNotFineConfig;
4
+
import net.minecraft.client.resources.AbstractResourcePack;
5
+
import net.minecraft.launchwrapper.IClassTransformer;
6
+
import net.minecraftforge.fml.relauncher.FMLLaunchHandler;
7
+
import org.objectweb.asm.*;
8
+
import org.objectweb.asm.tree.*;
9
+
10
+
import java.lang.reflect.Field;
11
+
import java.util.function.BiPredicate;
12
+
import java.util.function.Function;
13
+
14
+
/// Transformer for OptiNotFine.
15
+
///
16
+
/// @author Luna Mira Lage (Desoroxxx)
17
+
/// @since 1.0
18
+
public final class OptiNotFineTransformer implements IClassTransformer {
19
+
20
+
private static final String RESOURCE_PACK_FILE_FIELD_NAME = FMLLaunchHandler.isDeobfuscatedEnvironment() ? "resourcePackFile" : "field_110597_b";
21
+
22
+
public static final String[][] BUFFER_ALLOCATED_PATHS = {
23
+
{"jdk.internal.access.SharedSecrets", "getJavaNioAccess", "getDirectBufferPool", "getMemoryUsed"},
24
+
{"jdk.internal.misc.SharedSecrets", "getJavaNioAccess", "getDirectBufferPool", "getMemoryUsed"},
25
+
{"sun.misc.SharedSecrets", "getJavaNioAccess", "getDirectBufferPool", "getMemoryUsed"}
26
+
};
27
+
28
+
@Override
29
+
public byte[] transform(final String name, final String transformedName, final byte[] basicClass) {
30
+
if (basicClass == null)
31
+
return null;
32
+
33
+
switch (transformedName) {
34
+
case "net.minecraftforge.fml.client.FMLClientHandler":
35
+
return transformFMLClientHandler(basicClass);
36
+
case "net.minecraft.client.resources.AbstractResourcePack":
37
+
return transformAbstractResourcePack(basicClass);
38
+
case "net.optifine.util.NativeMemory":
39
+
return transformNativeMemory(basicClass);
40
+
}
41
+
42
+
if (!OptiNotFineConfig.stopLogSpam)
43
+
return basicClass;
44
+
45
+
switch (transformedName) {
46
+
case "net.optifine.shaders.SMCLog":
47
+
return transformSMCLog(basicClass);
48
+
case "net.optifine.config.ConnectedParser":
49
+
return transformConnectedParser(basicClass);
50
+
case "net.optifine.shaders.ItemAliases":
51
+
return transformAliases(basicClass, "loadItemAliases", "item");
52
+
case "net.optifine.shaders.BlockAliases":
53
+
return transformAliases(basicClass, "loadBlockAliases", "block");
54
+
case "net.optifine.shaders.EntityAliases":
55
+
return transformAliases(basicClass, "loadEntityAliases", "entity");
56
+
case "net.optifine.shaders.config.MacroExpressionResolver":
57
+
return stripConfigWarn(basicClass, "getExpression", "(Ljava/lang/String;)Lnet/optifine/expr/IExpression;");
58
+
case "net.optifine.shaders.config.ShaderPackParser":
59
+
return stripConfigWarn(basicClass, "collectShaderOptions", "(Lnet/optifine/shaders/IShaderPack;Ljava/lang/String;Ljava/util/Map;)V");
60
+
}
61
+
62
+
return basicClass;
63
+
}
64
+
65
+
/// Add our branding to the main menu screen
66
+
private static byte[] transformFMLClientHandler(final byte[] basicClass) {
67
+
return transform(basicClass, classWriter -> targetMethod(classWriter, "getAdditionalBrandingInformation", methodVisitor ->
68
+
new MethodVisitor(Opcodes.ASM5, methodVisitor) {
69
+
@Override
70
+
public void visitLdcInsn(final Object value) {
71
+
super.visitLdcInsn(value.equals("Optifine %s") ? "OptiNotFine 1.0-Dev-1 on %s" : value);
72
+
}
73
+
}));
74
+
}
75
+
76
+
/// Add a missing getter method to [AbstractResourcePack].
77
+
///
78
+
/// That getter method was added by Cleanroom but is accidentally removed due to OptiFine's binary patching.
79
+
///
80
+
/// **Warning:** We currently do not check whether this method already exists as we have a guarantee that it does not.
81
+
/// The reason for that is since our transformer runs only when OptiFine is here, the method is definitely removed.
82
+
private static byte[] transformAbstractResourcePack(final byte[] basicClass) {
83
+
return transform(basicClass, classWriter -> new ClassVisitor(Opcodes.ASM5, classWriter) {
84
+
@Override
85
+
public void visitEnd() {
86
+
final MethodVisitor methodVisitor = visitMethod(Opcodes.ACC_PUBLIC, "getResourcePackFile", "()Ljava/io/File;", null, null);
87
+
88
+
methodVisitor.visitCode();
89
+
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
90
+
methodVisitor.visitFieldInsn(Opcodes.GETFIELD, "net/minecraft/client/resources/AbstractResourcePack", RESOURCE_PACK_FILE_FIELD_NAME, "Ljava/io/File;");
91
+
methodVisitor.visitInsn(Opcodes.ARETURN);
92
+
methodVisitor.visitMaxs(1, 1);
93
+
methodVisitor.visitEnd();
94
+
95
+
super.visitEnd();
96
+
}
97
+
});
98
+
}
99
+
100
+
/// Add `jdk.internal.access.SharedSecrets` to the paths OptiFine will search to see the native memory usage.
101
+
///
102
+
/// We can safely access this because [Imagine Breaker](https://github.com/Rongmario/ImagineBreaker) will remove the module system.
103
+
private static byte[] transformNativeMemory(final byte[] basicClass) {
104
+
final ClassNode classNode = new ClassNode();
105
+
new ClassReader(basicClass).accept(classNode, 0);
106
+
107
+
final String fieldName = findFieldName(BUFFER_ALLOCATED_PATHS);
108
+
109
+
for (final MethodNode methodNode : classNode.methods) {
110
+
if (!methodNode.name.equals("<clinit>"))
111
+
continue;
112
+
113
+
for (AbstractInsnNode instruction = methodNode.instructions.getFirst(); instruction != null; instruction = instruction.getNext()) {
114
+
if (!isTargetCall(instruction))
115
+
continue;
116
+
117
+
replaceArrayArgument(methodNode.instructions, instruction, fieldName);
118
+
break;
119
+
}
120
+
121
+
break;
122
+
}
123
+
124
+
final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
125
+
classNode.accept(classWriter);
126
+
return classWriter.toByteArray();
127
+
}
128
+
129
+
private static String findFieldName(final Object fieldValue) {
130
+
try {
131
+
for (final Field field : OptiNotFineTransformer.class.getFields())
132
+
if (field.get(null) == fieldValue)
133
+
return field.getName();
134
+
} catch (final IllegalAccessException illegalAccessException) {
135
+
throw new RuntimeException(illegalAccessException);
136
+
}
137
+
138
+
throw new IllegalStateException("Field not found for the given value, something is very wrong");
139
+
}
140
+
141
+
private static boolean isTargetCall(final AbstractInsnNode instructionNode) {
142
+
if (!(instructionNode instanceof MethodInsnNode))
143
+
return false;
144
+
145
+
final MethodInsnNode methodInstruction = (MethodInsnNode) instructionNode;
146
+
147
+
return methodInstruction.getOpcode() == Opcodes.INVOKESTATIC && methodInstruction.owner.equals("net/optifine/util/NativeMemory") && methodInstruction.name.equals("makeLongSupplier");
148
+
}
149
+
150
+
private static void replaceArrayArgument(final InsnList instructions, final AbstractInsnNode methodCall, final String fieldName) {
151
+
for (AbstractInsnNode current = methodCall.getPrevious(); current != null; current = current.getPrevious()) {
152
+
if (!(current instanceof TypeInsnNode))
153
+
continue;
154
+
155
+
final TypeInsnNode typeInstruction = (TypeInsnNode) current;
156
+
157
+
if (typeInstruction.getOpcode() != Opcodes.ANEWARRAY || !typeInstruction.desc.equals("[Ljava/lang/String;"))
158
+
continue;
159
+
160
+
removeInstructionsBetween(instructions, current.getPrevious(), methodCall);
161
+
instructions.insertBefore(methodCall, new FieldInsnNode(Opcodes.GETSTATIC, Type.getInternalName(OptiNotFineTransformer.class), fieldName, "[[Ljava/lang/String;"));
162
+
return;
163
+
}
164
+
165
+
throw new IllegalStateException("Could not find array argument for method call");
166
+
}
167
+
168
+
private static void removeInstructionsBetween(final InsnList instructions, final AbstractInsnNode start, final AbstractInsnNode end) {
169
+
if (start == null)
170
+
return;
171
+
172
+
AbstractInsnNode current = start;
173
+
while (current != end) {
174
+
final AbstractInsnNode next = current.getNext();
175
+
instructions.remove(current);
176
+
current = next;
177
+
}
178
+
}
179
+
180
+
/// Changes the logger call of `SMCLog.info` from `info` to `debug` as most of it is.
181
+
private static byte[] transformSMCLog(final byte[] basicClass) {
182
+
final String owner = "org/apache/logging/log4j/Logger";
183
+
return transform(basicClass, classWriter ->
184
+
targetMethod(classWriter, "info", methodVisitor ->
185
+
methodCallReplacer(methodVisitor, Opcodes.INVOKEINTERFACE, owner, "info", (methodVisitor1, descriptor, isInterface) ->
186
+
methodVisitor1.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, "debug", descriptor, isInterface))
187
+
));
188
+
}
189
+
190
+
/// Strip a few specific `Config#warn()`.
191
+
private static byte[] transformConnectedParser(final byte[] basicClass) {
192
+
return transform(basicClass, classWriter -> new ClassVisitor(Opcodes.ASM5, classWriter) {
193
+
@Override
194
+
public MethodVisitor visitMethod(final int access, final String name, final String descriptor, final String signature, final String[] exceptions) {
195
+
final MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
196
+
197
+
final String triggerString;
198
+
if (name.equals("parseItems")) {
199
+
triggerString = "Item not found: ";
200
+
} else if (name.equals("parseBlockPart")) {
201
+
triggerString = "Block not found for name: ";
202
+
} else {
203
+
return methodVisitor;
204
+
}
205
+
206
+
return triggerThenStripWarn(methodVisitor, triggerString, Opcodes.INVOKEVIRTUAL, "net/optifine/config/ConnectedParser", 2);
207
+
}
208
+
});
209
+
}
210
+
211
+
/// Strip a specific `Config#warn()`.
212
+
private static byte[] transformAliases(final byte[] basicClass, final String methodName, final String warnPrefix) {
213
+
final String trigger = "[Shaders] Invalid " + warnPrefix + " ID mapping: ";
214
+
215
+
return transform(basicClass, classWriter ->
216
+
targetMethod(classWriter, methodName, methodVisitor ->
217
+
triggerThenStripWarn(methodVisitor, trigger, Opcodes.INVOKESTATIC, "Config", 1)
218
+
));
219
+
}
220
+
221
+
private static MethodVisitor triggerThenStripWarn(final MethodVisitor parent, final String triggerString, final int targetOpcode, final String warnOwner, final int popCount) {
222
+
return new MethodVisitor(Opcodes.ASM5, parent) {
223
+
private boolean stripNextWarn = false;
224
+
225
+
@Override
226
+
public void visitLdcInsn(final Object value) {
227
+
if (triggerString.equals(value))
228
+
stripNextWarn = true;
229
+
230
+
super.visitLdcInsn(value);
231
+
}
232
+
233
+
@Override
234
+
public void visitMethodInsn(final int opcode, final String owner, final String name, final String descriptor, final boolean isInterface) {
235
+
if (stripNextWarn && opcode == targetOpcode && owner.equals(warnOwner) && name.equals("warn")) {
236
+
stripNextWarn = false;
237
+
238
+
for (int i = 0; i < popCount; i++)
239
+
super.visitInsn(Opcodes.POP);
240
+
241
+
return;
242
+
}
243
+
244
+
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
245
+
}
246
+
};
247
+
}
248
+
249
+
/// Strip any `Config#warn()` calls from a method.
250
+
private static byte[] stripConfigWarn(final byte[] basicClass, final String targetMethod, final String targetDescriptor) {
251
+
return transform(basicClass, classWriter ->
252
+
targetMethod(classWriter, (name, descriptor) -> name.equals(targetMethod) && descriptor.equals(targetDescriptor), methodVisitor ->
253
+
methodCallReplacer(methodVisitor, Opcodes.INVOKESTATIC, "Config", "warn", (methodVisitor1, descriptor, isInterface) ->
254
+
methodVisitor1.visitInsn(Opcodes.POP)
255
+
)));
256
+
}
257
+
258
+
private static MethodVisitor methodCallReplacer(final MethodVisitor parent, final int targetOpcode, final String targetOwner, final String targetName, final MethodCallReplacement replacement) {
259
+
return new MethodVisitor(Opcodes.ASM5, parent) {
260
+
@Override
261
+
public void visitMethodInsn(final int opcode, final String owner, final String name, final String descriptor, final boolean isInterface) {
262
+
if (opcode == targetOpcode && owner.equals(targetOwner) && name.equals(targetName)) {
263
+
replacement.apply(this, descriptor, isInterface);
264
+
} else {
265
+
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
266
+
}
267
+
}
268
+
};
269
+
}
270
+
271
+
@FunctionalInterface
272
+
interface MethodCallReplacement {
273
+
void apply(final MethodVisitor methodVisitor, final String descriptor, final boolean isInterface);
274
+
}
275
+
276
+
private static byte[] transform(final byte[] basicClass, final Function<ClassWriter, ClassVisitor> visitorFactory) {
277
+
final ClassReader classReader = new ClassReader(basicClass);
278
+
final ClassWriter classWriter = new ClassWriter(classReader, 0);
279
+
280
+
classReader.accept(visitorFactory.apply(classWriter), 0);
281
+
282
+
return classWriter.toByteArray();
283
+
}
284
+
285
+
private static ClassVisitor targetMethod(final ClassWriter classWriter, final String methodName, final MethodVisitorFactory factory) {
286
+
return targetMethod(classWriter, (name, descriptor) -> name.equals(methodName), factory);
287
+
}
288
+
289
+
private static ClassVisitor targetMethod(final ClassWriter classWriter, final BiPredicate<String, String> matcher, final MethodVisitorFactory factory) {
290
+
return new ClassVisitor(Opcodes.ASM5, classWriter) {
291
+
@Override
292
+
public MethodVisitor visitMethod(final int access, final String name, final String descriptor, final String signature, final String[] exceptions) {
293
+
final MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
294
+
return matcher.test(name, descriptor) ? factory.create(methodVisitor) : methodVisitor;
295
+
}
296
+
};
297
+
}
298
+
299
+
@FunctionalInterface
300
+
interface MethodVisitorFactory {
301
+
MethodVisitor create(final MethodVisitor parent);
302
+
}
303
+
}
+34
src/main/java/dev/redstudio/optinotfine/config/OptiNotFineConfig.java
+34
src/main/java/dev/redstudio/optinotfine/config/OptiNotFineConfig.java
···
1
+
package dev.redstudio.optinotfine.config;
2
+
3
+
import com.cleanroommc.configanytime.ConfigAnytime;
4
+
import net.minecraftforge.common.config.Config;
5
+
import net.minecraftforge.common.config.ConfigManager;
6
+
import net.minecraftforge.fml.client.event.ConfigChangedEvent;
7
+
import net.minecraftforge.fml.common.Mod;
8
+
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
9
+
10
+
import static dev.redstudio.optinotfine.ProjectConstants.*;
11
+
12
+
/// @author Luna Mira Lage (Desoroxxx)
13
+
/// @since 1.0
14
+
@Config(modid = ID, name = NAME)
15
+
@Mod.EventBusSubscriber(modid = ID)
16
+
public final class OptiNotFineConfig {
17
+
18
+
@Config.RequiresMcRestart
19
+
@Config.Name("Stop Log Spam")
20
+
@Config.Comment("Stop what is considered log spam but may still be useful when working with OptiFine features (Shaders, Resourcepacks, etc...), see README.md for a list.")
21
+
public static boolean stopLogSpam = true;
22
+
23
+
@SubscribeEvent
24
+
public static void onConfigChanged(final ConfigChangedEvent.OnConfigChangedEvent onConfigChangedEvent) {
25
+
if (!onConfigChangedEvent.getModID().equals(ID))
26
+
return;
27
+
28
+
ConfigManager.sync(ID, Config.Type.INSTANCE);
29
+
}
30
+
31
+
static {
32
+
ConfigAnytime.register(OptiNotFineConfig.class);
33
+
}
34
+
}
src/main/resources/logo.png
src/main/resources/logo.png
This is a binary file and will not be displayed.
+16
src/main/resources/mcmod.info
+16
src/main/resources/mcmod.info
···
1
+
[
2
+
{
3
+
"name": "${name}",
4
+
"modid": "${id}",
5
+
"version": "${version}",
6
+
"mcversion": "1.12.2",
7
+
"description": "§l${name}§r\\n\\nFixing OptiFine, until something better comes.",
8
+
"authorList": [
9
+
"Luna Mira Lage (Desoroxxx)"
10
+
],
11
+
"credits": "Luna Mira Lage (Desoroxxx), Romane Morgan Raven Tony Vitiello (Shuriken_n)",
12
+
"url": "https://tangled.org/desoroxxx.redstudio.dev/${name}",
13
+
"updateJSON": "https://forge.curseupdate.com/1409147/${id}",
14
+
"logoFile": "logo.png"
15
+
}
16
+
]