Compare commits

..
404 changed files with 5962 additions and 10055 deletions

No files matched your search

+2
View File
@@ -36,3 +36,5 @@ addon.local.gradle
addon.local.gradle.kts
addon.late.local.gradle
addon.late.local.gradle.kts
/*.csv
!gl_functions.csv
+8 -9
View File
@@ -7,41 +7,40 @@
* It's a cute angle -mitchej123
* It's the angel that pulls you out of OptiFine's hell -Roadhog360
## **Beta Quality - Here there be Dragons**
* Shaders - Beta
## **Beta Quality - Here there be Dragons**
* Shaders sneak preview - Please don't report issues yet, but please do submit fixes
* Complementary & BSL mostly function, but have some bugs
* Sildurs, Seus, and any other shader probably doesn't work
# Required Dependencies
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.19
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.7.0
* [UniMixins](https://github.com/LegacyModdingMC/UniMixins/releases) >= 0.1.14
* [GTNHLib](https://github.com/GTNewHorizons/GTNHLib/releases) >= 0.4.2
* NOTE: Some mods are not required, but a specific version is required if present - see: `Permanent Incompatibilities` below.
# Known (temporary) Incompatibilities
* Forge Relocation - Currently non Thread Safe; safe if not in use
* ProjectRed Frames - Currently non Thread Safe; safe if not in use
* ProjectRed Frames - Currently non Thread Safe; safe if not in use'
# Potential Incompatibilities
* EndlessIDs - Compat commits have been made, might work, might not - not fully tested
# Known Incompatibilities
* FalseTweaks - Not currently planned (Looking for volunteers) [Might work if you effectively disable most of the mod]
* SmoothFont - Not currently planned (Looking for volunteers)
* RPLE - Not currently planned (Looking for volunteers)
* LiteLoader - Install [LiteloaderLoader](https://github.com/Midnight145/LiteloaderLoader)
# Permanent Incompatibilities
* OptiFine [Disabled, won't fix]
* FastCraft [Disabled, won't fix]
* BetterFPS [Disabled, won't fix]
* OptimizationsAndTweaks [Disabled, won't fix]
* [Amazing Trophies 1.2.1 and below](https://github.com/GTNewHorizons/Amazing-Trophies/releases) - Use 1.2.2+
* [Amazing Trophies 1.2.1 and below](https://github.com/GTNewHorizons/Amazing-Trophies/releases) - use 1.2.2+
* [LWJGL3ify 1.5.4 and below](https://github.com/GTNewHorizons/lwjgl3ify/releases/) - Use the latest version available, 1.5.7+
* [ArchaicFix 0.6.2 and below](https://www.curseforge.com/minecraft/mc-mods/archaicfix/files/all?page=1&pageSize=20) - Use 0.7.0 or above
* [Hodgepodge 2.4.3 and below](https://github.com/GTNewHorizons/Hodgepodge/releases) - Use 0.4.5 or above
* CodeChicken[Core](https://github.com/GTNewHorizons/CodeChickenCore/releases)/[Lib](https://github.com/GTNewHorizons/CodeChickenLib/releases) <1.2.0 - Threading issues, use 1.3.0+
* Neodymium - Untested, but unlikely to be compatible. (Chunk Meshing is definitely incompatible, other features might work depending on config)
* SmoothFont - Equivalent functionality added
# Credits
* ShadersMod
@@ -65,7 +64,7 @@
* Makamys for [Neodymium](https://github.com/makamys/Neodymium) - Various Tessellator -> Quad functionality taken under the LGPL 3
* MCPatcherForge
* Original [MCPatcher](https://bitbucket.org/prupe/mcpatcher/src/master/): Paul Rupe
* [Forge version](https://github.com/mist475/MCPatcherForge): Mist475 under LGPL 3.0 with default ctm textures for glass and bookshelves by DarkBum under CC-BY-SA
* [Forge version](https://bitbucket.org/prupe/mcpatcher/src/master/): Mist475 under LGPL 3.0 with default ctm textures for glass and bookshelves by DarkBum under CC-BY-SA
* HUDCaching
* Created by [Moulberry](https://github.com/Moulberry/MCHUDCaching), backported from [Patcher](https://github.com/Sk1erLLC/Patcher) mod by Alexdoru under the CC-BY-SA-NC 4.0 license
* Angelica
-2
View File
@@ -4,8 +4,6 @@ plugins {
minecraft {
extraRunJvmArguments.add("-Dangelica.enableTestBlocks=true")
extraRunJvmArguments.add("-Dangelica.dumpClass=true")
//extraRunJvmArguments.add("-Dangelica.redirectorLogspam=true")
}
tasks.test {
+23 -19
View File
@@ -49,19 +49,19 @@ configurations {
}
dependencies {
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.6.107")
compileOnly("com.github.GTNewHorizons:Hodgepodge:2.5.78")
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:1.0.7") { transitive = false }
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:0.4.0") { transitive = false }
compileOnly("org.projectlombok:lombok:1.18.34") { transitive = false }
annotationProcessor("org.projectlombok:lombok:1.18.34")
compileOnly("org.projectlombok:lombok:1.18.22") { transitive = false }
annotationProcessor("org.projectlombok:lombok:1.18.22")
// Use modern models+textures to inject QuadProviders
devOnlyNonPublishable('com.github.GTNewHorizons:TX-Loader:1.8.5:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:TX-Loader:1.7.2:dev')
// Iris Shaders
compileOnly('org.jetbrains:annotations:26.0.2')
api("com.github.GTNewHorizons:GTNHLib:0.6.40:dev")
compileOnly('org.jetbrains:annotations:24.0.1')
api("com.github.GTNewHorizons:GTNHLib:0.5.20:dev")
shadowImplementation("org.anarres:jcpp:1.4.14") // Apache 2.0
shadowImplementation("org.taumc:glsl-transformation-lib:0.2.0-4.g6b42bca") {
exclude module: "antlr4" // we only want to shadow the runtime module
@@ -70,20 +70,21 @@ dependencies {
compileOnly "org.apache.ant:ant:1.8.2"
// Because who doesn't want NEI
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.7.90-GTNH:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.4.7:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:NotEnoughItems:2.6.45-GTNH:dev')
devOnlyNonPublishable('com.github.GTNewHorizons:CodeChickenCore:1.3.10:dev')
// Notfine Deps
compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev")
devOnlyNonPublishable("com.github.GTNewHorizons:Baubles-Expanded:2.1.15-GTNH:dev")
compileOnly("com.github.GTNewHorizons:twilightforest:2.7.13:dev") { transitive = false }
devOnlyNonPublishable("com.github.GTNewHorizons:Baubles:1.0.4:dev")
compileOnly("com.github.GTNewHorizons:twilightforest:2.6.35:dev") { transitive = false }
compileOnly(rfg.deobf('curse.maven:witchery-69673:2234410'))
compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.13.54-GTNH:dev") { transitive = false }
compileOnly("com.github.GTNewHorizons:Natura:2.8.9:dev")
compileOnly("com.github.GTNewHorizons:TinkersConstruct:1.12.14-GTNH:dev") { transitive = false }
compileOnly("com.github.GTNewHorizons:Natura:2.7.5:dev")
compileOnly("com.github.GTNewHorizons:ThaumicHorizons:1.7.8:dev")
compileOnly("com.github.GTNewHorizons:ThaumicHorizons:1.6.4:dev")
compileOnly("com.github.GTNewHorizons:Battlegear2:1.4.3:dev") { transitive = false }
compileOnly("com.github.GTNewHorizons:Battlegear2:1.4.1:dev") { transitive = false }
compileOnly(rfg.deobf('maven.modrinth:backhand:1.4.1')) { transitive = false }
compileOnly("com.falsepattern:chunkapi-mc1.7.10:0.5.1:api") { transitive = false }
compileOnly("com.falsepattern:endlessids-mc1.7.10:1.5-beta0003:dev") { transitive = false }
@@ -93,12 +94,13 @@ dependencies {
compileOnly("curse.maven:minefactory-reloaded-66672:2277486")
compileOnly("com.github.GTNewHorizons:NotEnoughIds:2.1.10:dev") // Mixin Version
compileOnly("com.github.GTNewHorizons:NotEnoughIds:2.1.6:dev") // Mixin Version
compileOnly("com.github.GTNewHorizons:NotEnoughIds-Legacy:1.4.7:dev") // ASM Version
compileOnly(rfg.deobf("curse.maven:campfirebackport-387444:4611675"))
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))
compileOnly(rfg.deobf("curse.maven:security-craft-64760:2818228"))
compileOnly("com.github.GTNewHorizons:HoloInventory:2.4.13-GTNH:dev")
runtimeOnlyNonPublishable(rfg.deobf("CoreTweaks:CoreTweaks:0.3.3.2"))
@@ -109,10 +111,12 @@ dependencies {
compileOnly("com.github.GTNewHorizons:BetterCrashes:1.4.0-GTNH:dev")
// Tests
testImplementation(platform('org.junit:junit-bom:5.11.4'))
testImplementation(platform('org.junit:junit-bom:5.9.2'))
testImplementation('org.junit.jupiter:junit-jupiter')
testImplementation("org.mockito:mockito-core:3.+")
testImplementation("org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209")
compileOnly("mega:fluidlogged-mc1.7.10:0.1.2")
devOnlyNonPublishable("com.github.javaparser:javaparser-symbol-solver-core:3.26.2") {
exclude module: "guava"
}
}
+2084
View File
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -14,7 +14,7 @@ modId = angelica
modGroup = com.gtnewhorizons.angelica
# Whether to use modGroup as the maven publishing group.
# When false, com.github.GTNewHorizons is used.
# Due to a history of using JitPack, the default is com.github.GTNewHorizons for all mods.
useModGroupForPublishing = false
# Updates your build.gradle and settings.gradle automatically whenever an update is available.
@@ -151,7 +151,7 @@ modrinthProjectId = angelica
# type can be one of [project, version],
# and the name is the Modrinth project or version slug/id of the other mod.
# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
# Note: UniMixins is automatically set as a required dependency if usesMixins = true.
# Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true
modrinthRelations = required-project\:gtnhlib
# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+3 -2
View File
@@ -86,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -205,7 +206,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
-7
View File
@@ -8,13 +8,6 @@ repositories {
includeGroup 'com.falsepattern'
}
}
maven {
name = 'mega'
url = 'https://mvn.falsepattern.com/gtmega_releases/'
content {
includeGroup 'mega'
}
}
exclusiveContent {
forRepository {
ivy {
+1 -1
View File
@@ -16,5 +16,5 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.41'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.29'
}
+120
View File
@@ -0,0 +1,120 @@
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.visitor.VoidVisitorWithDefaults;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Used to generate gl_functions.csv. Which contains a mapping of OpenGL functions provided by LWJGL
* corresponding to the earliest GL version they appear in.
*/
public class GLFunctionParser {
final static String glPath = "util/gl"; // Path to LWJGL OpenGL bindings source code
public static void main(String[] args) throws FileNotFoundException {
HashMap<String, Set<String>> byFunction = new HashMap<>();
File dir = new File(glPath);
for (File file : dir.listFiles()) {
final String filename = file.getName();
if (filename.endsWith(".java") && filename.startsWith("GL") && Character.isDigit(filename.charAt(2))) {
Path p = Paths.get(filename);
String parsedFile = removeFileExtension(p.getFileName().toString());
CompilationUnit cu = StaticJavaParser.parse(file);
cu.accept(new VoidVisitorWithDefaults<>() {
@Override
public void defaultAction(Node n, Object arg) {
for( Node child : n.getChildNodes()) {
if(child instanceof ClassOrInterfaceDeclaration cd) {
for (MethodDeclaration md : cd.getMethods()) {
String methodName = md.getNameAsString() + "(";
for (Parameter p : md.getParameters()) {
methodName = methodName + p.getTypeAsString();
}
methodName = methodName + ")";
if(methodName.startsWith("n")) continue;
Set<String> functionSet = byFunction.computeIfAbsent(methodName, k -> new HashSet<>());
functionSet.add(parsedFile);
}
}
}
}
}, null);
}
}
Map<String, String> functionVersionMap = new HashMap<>();
for(String function : byFunction.keySet()) {
Set<String> versionsForFunction = byFunction.get(function);
int lowestInt = 46;
String lowestString = "GL46";
for(String version : versionsForFunction) {
int i = Integer.parseInt(version.substring(2, 4));
if (i < lowestInt) {
lowestInt = i;
lowestString = version;
}
}
functionVersionMap.put(function, lowestString);
}
List<String[]> outputLines = new ArrayList<>();
for (Map.Entry<String, String> entry : functionVersionMap.entrySet()) {
outputLines.add(new String[] {entry.getKey(), entry.getValue()});
}
File output = new File("output.csv");
try (PrintWriter writer = new PrintWriter(output)) {
outputLines.stream().map(GLSMFunctionComparison::convertToCSV).forEach(writer::println);
}
}
public static String convertToCSV(String[] data) {
return Stream.of(data).map(GLSMFunctionComparison::escapeSpecialCharacters).collect(Collectors.joining(","));
}
public static String escapeSpecialCharacters(String data) {
if (data == null) {
throw new IllegalArgumentException("Input data cannot be null");
}
String escapedData = data.replaceAll("\\R", " ");
if (data.contains(",") || data.contains("\"") || data.contains("'")) {
data = data.replace("\"", "\"\"");
escapedData = "\"" + data + "\"";
}
return escapedData;
}
public static String removeFileExtension(String filename) {
if (filename == null || filename.isEmpty()) {
return filename;
}
String extPattern = "(?<!^)[.]" + "[^.]*$";
return filename.replaceAll(extPattern, "");
}
}
+135
View File
@@ -0,0 +1,135 @@
import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.visitor.VoidVisitorWithDefaults;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Used to compare OpenGL functions from gl_functions.csv to current GLStateManager
* source code to determine what functions have an implementation in GLSM, and what ones don't.
* Only considers functions which appear in GL 2.1 and lower
*/
public class GLSMFunctionComparison {
/**
* If set to true, then the outputted CSV will show functions which do have an implementation in GLSM
*/
final static boolean INCLUDE_TRUE = false;
final static String glFunctionsCsvPath = "gl_functions.csv";
final static String glsmPath = "src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java";
public static Map<String, String> parseGLFunctions() {
Map<String, String> map = new HashMap<>();
try (BufferedReader br = new BufferedReader(new FileReader(glFunctionsCsvPath))) {
String line;
while ((line = br.readLine()) != null) {
String[] values = line.split(",");
if (values.length == 2) {
String key = values[0].trim();
String value = values[1].trim();
int versionInt = Integer.parseInt(value.substring(2, 4));
if (versionInt < 30) map.put(key, value);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return map;
}
public static void main(String[] args) throws FileNotFoundException {
//GLSM will fail to parse without increasing language level
StaticJavaParser.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17);
Map<String, String> functionMap = parseGLFunctions();
Set<String> glsmFunctions = new HashSet<>();
File glsmFile = new File(glsmPath);
CompilationUnit cu = StaticJavaParser.parse(glsmFile);
cu.accept(new VoidVisitorWithDefaults<>() {
@Override
public void defaultAction(Node n, Object arg) {
for (Node child : n.getChildNodes()) {
if (child instanceof ClassOrInterfaceDeclaration cd) {
for (MethodDeclaration md : cd.getMethods()) {
String methodName = md.getNameAsString() + "(";
for (Parameter p : md.getParameters()) {
methodName = methodName + p.getTypeAsString();
}
methodName = methodName + ")";
glsmFunctions.add(methodName);
}
}
}
}
}, null);
Map<String, String> functionGLSMMap = new HashMap<>();
for(String function : functionMap.keySet()) {
if (!glsmFunctions.contains(function)) {
functionGLSMMap.put(function, "false");
} else if (INCLUDE_TRUE) {
functionGLSMMap.put(function, "true");
}
}
List<String[]> outputLines = new ArrayList<>();
for (Map.Entry<String, String> entry : functionGLSMMap.entrySet()) {
outputLines.add(new String[] {entry.getKey(), functionMap.get(entry.getKey()), entry.getValue()});
}
File output = new File("glsm_function_comparison.csv");
try (PrintWriter writer = new PrintWriter(output)) {
outputLines.stream().map(GLSMFunctionComparison::convertToCSV).forEach(writer::println);
}
}
public static String convertToCSV(String[] data) {
return Stream.of(data).map(GLSMFunctionComparison::escapeSpecialCharacters).collect(Collectors.joining(","));
}
public static String escapeSpecialCharacters(String data) {
if (data == null) {
throw new IllegalArgumentException("Input data cannot be null");
}
String escapedData = data.replaceAll("\\R", " ");
if (data.contains(",") || data.contains("\"") || data.contains("'")) {
data = data.replace("\"", "\"\"");
escapedData = "\"" + data + "\"";
}
return escapedData;
}
public static String removeFileExtension(String filename) {
if (filename == null || filename.isEmpty()) {
return filename;
}
String extPattern = "(?<!^)[.]" + "[^.]*$";
return filename.replaceAll(extPattern, "");
}
}
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica;
import com.gtnewhorizon.gtnhmixins.ILateMixinLoader;
import com.gtnewhorizon.gtnhmixins.LateMixin;
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
import com.gtnewhorizons.angelica.mixins.Mixins;
import java.util.List;
@@ -10,7 +9,6 @@ import java.util.Set;
@LateMixin
public class AngelicaLateMixins implements ILateMixinLoader {
@Override
public String getMixinConfig() {
return "mixins.angelica.late.json";
@@ -18,6 +16,7 @@ public class AngelicaLateMixins implements ILateMixinLoader {
@Override
public List<String> getMixins(Set<String> loadedMods) {
return IMixins.getLateMixins(Mixins.class, loadedMods);
return Mixins.getLateMixins(loadedMods);
}
}
@@ -15,13 +15,8 @@ import cpw.mods.fml.common.event.FMLPreInitializationEvent;
modid = "angelica",
name = "Angelica",
version = Tags.VERSION,
dependencies = """
required-after:gtnhlib@[0.6.6,);\
before:lwjgl3ify@[1.5.3,);\
after:hodgepodge@[2.4.4,);\
after:CodeChickenCore@[1.2.0,);\
after:archaicfix@[0.7.0,);\
""",
dependencies = " before:lwjgl3ify@[1.5.3,);" + " after:hodgepodge@[2.4.4,);" + " after:CodeChickenCore@[1.2.0,);"
+ " after:archaicfix@[0.7.0,);" + " required-after:gtnhlib@[0.4.2,);",
acceptedMinecraftVersions = "[1.7.10]",
acceptableRemoteVersions = "*",
guiFactory = "com.gtnewhorizons.angelica.config.AngelicaGuiConfigFactory")
@@ -1,8 +0,0 @@
package com.gtnewhorizons.angelica.api;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
public interface IBlockAccessExtended extends IBlockAccess {
World getWorld();
}
@@ -1,27 +0,0 @@
package com.gtnewhorizons.angelica.api;
import com.gtnewhorizons.angelica.mixins.interfaces.IPatchedTextureAtlasSprite;
import com.gtnewhorizons.angelica.mixins.interfaces.ITexturesCache;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.util.IIcon;
/**
* Exposes a method to the API that allow mods to add texture update compat
*/
@SuppressWarnings("unused")
public class TextureServices {
@SuppressWarnings("unused")
public static void updateBlockTextureAnimation(IIcon icon, RenderBlocks renderBlocks) {
if (renderBlocks instanceof ITexturesCache texturesCache) {
texturesCache.getRenderedTextures().add(icon);
}
}
@SuppressWarnings("unused")
public static void updateTextureAnimation(IIcon icon) {
if(icon instanceof IPatchedTextureAtlasSprite patchedSprite) {
patchedSprite.markNeedsAnimationUpdate();
}
}
}
@@ -1,30 +1,25 @@
package com.gtnewhorizons.angelica.client.font;
import com.google.common.collect.ImmutableSet;
import com.gtnewhorizons.angelica.config.FontConfig;
import com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.mixins.interfaces.FontRendererAccessor;
import cpw.mods.fml.client.SplashProgress;
import it.unimi.dsi.fastutil.chars.Char2ShortOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.coderbot.iris.gl.program.Program;
import net.coderbot.iris.gl.program.ProgramBuilder;
import jss.util.RandomXoshiro256StarStar;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.IOUtils;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*;
/**
* A batching replacement for {@code FontRenderer}
*
@@ -34,6 +29,8 @@ public class BatchingFontRenderer {
/** The underlying FontRenderer object that's being accelerated */
protected FontRenderer underlying;
/** Cached locations for each unicode page atlas */
private final ResourceLocation[] unicodePageLocations;
/** Array of width of all the characters in default.png */
protected int[] charWidth = new int[256];
/** Array of the start/end column (in upper/lower nibble) for every glyph in the /font directory. */
@@ -45,60 +42,50 @@ public class BatchingFontRenderer {
private int[] colorCode;
/** Location of the primary font atlas to bind. */
protected final ResourceLocation locationFontTexture;
/** The RenderEngine used to load and setup glyph textures. */
private final TextureManager renderEngine;
private final RandomXoshiro256StarStar fontRandom = new RandomXoshiro256StarStar();
private final int AAMode;
private final int AAStrength;
private final int texBoundAttrLocation;
private final int fontShaderId;
/** The full list of characters present in the default Minecraft font, excluding the Unicode font */
@SuppressWarnings("UnnecessaryUnicodeEscape")
private static final String MCFONT_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
final boolean isSGA;
final boolean isSplash;
private static final short[] MCFONT_ASCII_LUT = new short[512];
private static final Char2ShortOpenHashMap MCFONT_UNI_LUT = new Char2ShortOpenHashMap();
private static class FontAAShader {
private static Program fontShader = null;
public static Program getProgram() {
if (fontShader == null) {
String vsh, fsh;
try {
fsh = new String(IOUtils.toByteArray(Objects.requireNonNull(FontAAShader.class.getResourceAsStream("/assets/angelica/shaders/fontFilter.fsh"))), StandardCharsets.UTF_8);
vsh = new String(IOUtils.toByteArray(Objects.requireNonNull(FontAAShader.class.getResourceAsStream("/assets/angelica/shaders/fontFilter.vsh"))), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
ProgramBuilder builder = ProgramBuilder.begin("fontFilter", vsh, null, fsh, ImmutableSet.of(0));
fontShader = builder.build();
static {
Arrays.fill(MCFONT_ASCII_LUT, (short) -1);
for (short i = 0; i < MCFONT_CHARS.length(); i++) {
char ch = MCFONT_CHARS.charAt(i);
if (ch < MCFONT_ASCII_LUT.length) {
MCFONT_ASCII_LUT[ch] = i;
} else {
MCFONT_UNI_LUT.put(ch, i);
}
return fontShader;
}
}
public BatchingFontRenderer(FontRenderer underlying, int[] charWidth, byte[] glyphWidth,
int[] colorCode, ResourceLocation locationFontTexture) {
public static int lookupMcFontPosition(char ch) {
if (ch < MCFONT_ASCII_LUT.length) {
return MCFONT_ASCII_LUT[ch];
} else {
return MCFONT_UNI_LUT.getOrDefault(ch, (short) -1);
}
}
public BatchingFontRenderer(FontRenderer underlying, ResourceLocation[] unicodePageLocations, int[] charWidth,
byte[] glyphWidth, int[] colorCode, ResourceLocation locationFontTexture, TextureManager renderEngine) {
this.underlying = underlying;
this.unicodePageLocations = unicodePageLocations;
this.charWidth = charWidth;
this.glyphWidth = glyphWidth;
this.colorCode = colorCode;
this.locationFontTexture = locationFontTexture;
this.renderEngine = renderEngine;
for (int i = 0; i < 64; i++) {
batchCommandPool.add(new FontDrawCmd());
}
this.isSGA = Objects.equals(this.locationFontTexture.getResourcePath(), "textures/font/ascii_sga.png");
// noinspection deprecation
this.isSplash = underlying instanceof SplashProgress.SplashFontRenderer;
FontProviderMC.get(this.isSGA).charWidth = this.charWidth;
FontProviderMC.get(this.isSGA).locationFontTexture = this.locationFontTexture;
FontProviderUnicode.get().glyphWidth = this.glyphWidth;
//noinspection deprecation
fontShaderId = FontAAShader.getProgram().getProgramId();
AAMode = GL20.glGetUniformLocation(fontShaderId, "aaMode");
AAStrength = GL20.glGetUniformLocation(fontShaderId, "strength");
texBoundAttrLocation = GL20.glGetAttribLocation(fontShaderId, "texBounds");
}
// === Batched rendering
@@ -110,29 +97,24 @@ public class BatchingFontRenderer {
private static final int INITIAL_BATCH_SIZE = 256;
private static final ResourceLocation DUMMY_RESOURCE_LOCATION = new ResourceLocation("angelica$dummy",
"this is invalid!");
private FloatBuffer batchVtxPositions = memAllocFloat(INITIAL_BATCH_SIZE * 2);
private ByteBuffer batchVtxColors = memAlloc(INITIAL_BATCH_SIZE * 4);
private FloatBuffer batchVtxTexCoords = memAllocFloat(INITIAL_BATCH_SIZE * 2);
private IntBuffer batchIndices = memAllocInt(INITIAL_BATCH_SIZE / 2 * 3);
private FloatBuffer batchVtxTexBounds = memAllocFloat(INITIAL_BATCH_SIZE * 4);
private FloatBuffer batchVtxPositions = BufferUtils.createFloatBuffer(INITIAL_BATCH_SIZE * 2);
private ByteBuffer batchVtxColors = BufferUtils.createByteBuffer(INITIAL_BATCH_SIZE * 4);
private FloatBuffer batchVtxTexCoords = BufferUtils.createFloatBuffer(INITIAL_BATCH_SIZE * 2);
private IntBuffer batchIndices = BufferUtils.createIntBuffer(INITIAL_BATCH_SIZE / 2 * 3);
private final ObjectArrayList<FontDrawCmd> batchCommands = ObjectArrayList.wrap(new FontDrawCmd[64], 0);
private final ObjectArrayList<FontDrawCmd> batchCommandPool = ObjectArrayList.wrap(new FontDrawCmd[64], 0);
private int blendSrcRGB = GL11.GL_SRC_ALPHA;
private int blendDstRGB = GL11.GL_ONE_MINUS_SRC_ALPHA;
/** */
private void pushVtx(float x, float y, int rgba, float u, float v, float uMin, float uMax, float vMin, float vMax) {
private void pushVtx(float x, float y, int rgba, float u, float v) {
final int oldCap = batchVtxPositions.capacity() / 2;
if (vtxWriterIndex >= oldCap) {
final int newCap = oldCap * 2;
batchVtxPositions = memRealloc(batchVtxPositions, newCap * 2);
batchVtxColors = memRealloc(batchVtxColors, newCap * 4);
batchVtxTexCoords = memRealloc(batchVtxTexCoords, newCap * 2);
batchVtxTexBounds = memRealloc(batchVtxTexBounds, newCap * 4);
batchVtxPositions = CompatMemoryUtil.memReallocDirect(batchVtxPositions, newCap * 2);
batchVtxColors = CompatMemoryUtil.memReallocDirect(batchVtxColors, newCap * 4);
batchVtxTexCoords = CompatMemoryUtil.memReallocDirect(batchVtxTexCoords, newCap * 2);
final int oldIdxCap = batchIndices.capacity();
final int newIdxCap = oldIdxCap * 2;
batchIndices = memRealloc(batchIndices, newIdxCap);
batchIndices = CompatMemoryUtil.memReallocDirect(batchIndices, newIdxCap);
}
final int idx = vtxWriterIndex;
final int idx2 = idx * 2;
@@ -146,19 +128,15 @@ public class BatchingFontRenderer {
batchVtxColors.put(idx4 + 3, (byte) ((rgba >> 24) & 0xFF));
batchVtxTexCoords.put(idx2, u);
batchVtxTexCoords.put(idx2 + 1, v);
batchVtxTexBounds.put(idx4, uMin);
batchVtxTexBounds.put(idx4 + 1, uMax);
batchVtxTexBounds.put(idx4 + 2, vMin);
batchVtxTexBounds.put(idx4 + 3, vMax);
vtxWriterIndex++;
}
private void pushUntexRect(float x, float y, float w, float h, int rgba) {
final int vtxId = vtxWriterIndex;
pushVtx(x, y, rgba, 0, 0, 0, 0, 0, 0);
pushVtx(x, y + h, rgba, 0, 0, 0, 0, 0, 0);
pushVtx(x + w, y, rgba, 0, 0, 0, 0, 0, 0);
pushVtx(x + w, y + h, rgba, 0, 0, 0, 0, 0, 0);
pushVtx(x, y, rgba, 0, 0);
pushVtx(x, y + h, rgba, 0, 0);
pushVtx(x + w, y, rgba, 0, 0);
pushVtx(x + w, y + h, rgba, 0, 0);
pushQuadIdx(vtxId);
}
@@ -175,7 +153,7 @@ public class BatchingFontRenderer {
return idx;
}
private void pushDrawCmd(int startIdx, int idxCount, ResourceLocation texture, boolean isUnicode) {
private void pushDrawCmd(int startIdx, int idxCount, ResourceLocation texture) {
if (!batchCommands.isEmpty()) {
final FontDrawCmd lastCmd = batchCommands.get(batchCommands.size() - 1);
final int prevEndVtx = lastCmd.startVtx + lastCmd.idxCount;
@@ -191,7 +169,7 @@ public class BatchingFontRenderer {
}
}
final FontDrawCmd cmd = batchCommandPool.pop();
cmd.reset(startIdx, idxCount, texture, isUnicode);
cmd.reset(startIdx, idxCount, texture);
batchCommands.add(cmd);
}
@@ -199,14 +177,12 @@ public class BatchingFontRenderer {
public int startVtx;
public int idxCount;
public boolean isUnicode;
public ResourceLocation texture;
public void reset(int startVtx, int vtxCount, ResourceLocation texture, boolean isUnicode) {
public void reset(int startVtx, int vtxCount, ResourceLocation texture) {
this.startVtx = startVtx;
this.idxCount = vtxCount;
this.texture = texture;
this.isUnicode = isUnicode;
}
@Override
@@ -265,9 +241,6 @@ public class BatchingFontRenderer {
}
}
int lastActiveProgram;
int fontAAModeLast = -1;
int fontAAStrengthLast = -1;
private void flushBatch() {
// Sort&Draw
batchCommands.sort(FontDrawCmd.DRAW_ORDER_COMPARATOR);
@@ -280,23 +253,9 @@ public class BatchingFontRenderer {
GLStateManager.enableTexture();
GLStateManager.enableAlphaTest();
GLStateManager.enableBlend();
GLStateManager.tryBlendFuncSeparate(blendSrcRGB, blendDstRGB, GL11.GL_ONE, GL11.GL_ZERO);
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GLStateManager.glShadeModel(GL11.GL_FLAT);
if (FontConfig.fontAAMode != 0) {
GL20.glVertexAttribPointer(texBoundAttrLocation, 4, false, 0, batchVtxTexBounds);
GL20.glEnableVertexAttribArray(texBoundAttrLocation);
lastActiveProgram = GLStateManager.getActiveProgram();
GLStateManager.glUseProgram(fontShaderId);
if (FontConfig.fontAAMode != fontAAModeLast) {
fontAAModeLast = FontConfig.fontAAMode;
GL20.glUniform1i(AAMode, FontConfig.fontAAMode);
}
if (FontConfig.fontAAStrength != fontAAStrengthLast) {
fontAAStrengthLast = FontConfig.fontAAStrength;
GL20.glUniform1f(AAStrength, FontConfig.fontAAStrength / 120.f);
}
}
GL11.glTexCoordPointer(2, 0, batchVtxTexCoords);
GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
GL11.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, 0, batchVtxColors);
@@ -324,13 +283,8 @@ public class BatchingFontRenderer {
}
batchIndices.limit(cmd.startVtx + cmd.idxCount);
batchIndices.position(cmd.startVtx);
GL11.glDrawElements(GL11.GL_TRIANGLES, batchIndices);
}
if (FontConfig.fontAAMode != 0) {
GLStateManager.glUseProgram(lastActiveProgram);
GL20.glDisableVertexAttribArray(texBoundAttrLocation);
}
GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);
@@ -358,28 +312,17 @@ public class BatchingFontRenderer {
return (what >= fromInclusive) && (what <= toInclusive);
}
public boolean forceDefaults() {
return this.isSGA || this.isSplash;
}
public float getGlyphScaleX() {
return forceDefaults() ? 1 : (float) (FontConfig.glyphScale * Math.pow(2, FontConfig.glyphAspect)) * (FontStrategist.customFontInUse ? 1.5f : 1);
}
public float getGlyphScaleY() {
return forceDefaults() ? 1 : (float) (FontConfig.glyphScale / Math.pow(2, FontConfig.glyphAspect)) * (FontStrategist.customFontInUse ? 1.5f : 1);
}
public float getGlyphSpacing() {
return forceDefaults() ? 0 : FontConfig.glyphSpacing;
}
public float getWhitespaceScale() {
return forceDefaults() ? 1 : FontConfig.whitespaceScale;
}
public float getShadowOffset() {
return forceDefaults() ? 1 : FontConfig.fontShadowOffset;
private ResourceLocation getUnicodePageLocation(int page) {
final ResourceLocation lookup = unicodePageLocations[page];
if (lookup == null) {
final ResourceLocation rl = new ResourceLocation(String.format(
"textures/font/unicode_page_%02x.png",
page));
unicodePageLocations[page] = rl;
return rl;
} else {
return lookup;
}
}
private static final char FORMATTING_CHAR = 167; // §
@@ -392,9 +335,6 @@ public class BatchingFontRenderer {
}
final int shadowColor = (color & 0xfcfcfc) >> 2 | color & 0xff000000;
FontProviderMC.get(this.isSGA).charWidth = this.charWidth;
FontProviderMC.get(this.isSGA).locationFontTexture = this.locationFontTexture;
this.beginBatch();
float curX = anchorX;
try {
@@ -414,14 +354,10 @@ public class BatchingFontRenderer {
boolean curStrikethrough = false;
boolean curUnderline = false;
final float glyphScaleY = getGlyphScaleY();
final float heightNorth = anchorY + (underlying.FONT_HEIGHT - 1.0f) * (0.5f - glyphScaleY / 2);
final float heightSouth = (underlying.FONT_HEIGHT - 1.0f) * glyphScaleY;
final float underlineY = heightNorth + (underlying.FONT_HEIGHT - 1.0f) * glyphScaleY;
final float underlineY = anchorY + underlying.FONT_HEIGHT - 1.0f;
float underlineStartX = 0.0f;
float underlineEndX = 0.0f;
final float strikethroughY = heightNorth + ((float) (underlying.FONT_HEIGHT / 2) - 1.0f) * glyphScaleY;
final float strikethroughY = anchorY + (float) (underlying.FONT_HEIGHT / 2) - 1.0F;
float strikethroughStartX = 0.0f;
float strikethroughEndX = 0.0f;
@@ -433,8 +369,8 @@ public class BatchingFontRenderer {
if (curUnderline && underlineStartX != underlineEndX) {
final int ulIdx = idxWriterIndex;
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, glyphScaleY, curColor);
pushDrawCmd(ulIdx, 6, null, false);
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, 1.0f, curColor);
pushDrawCmd(ulIdx, 6, null);
underlineStartX = underlineEndX;
}
if (curStrikethrough && strikethroughStartX != strikethroughEndX) {
@@ -443,9 +379,9 @@ public class BatchingFontRenderer {
strikethroughStartX,
strikethroughY,
strikethroughEndX - strikethroughStartX,
glyphScaleY,
1.0f,
curColor);
pushDrawCmd(ulIdx, 6, null, false);
pushDrawCmd(ulIdx, 6, null);
strikethroughStartX = strikethroughEndX;
}
@@ -490,27 +426,66 @@ public class BatchingFontRenderer {
continue;
}
if (curRandom) {
chr = FontProviderMC.get(this.isSGA).getRandomReplacement(chr);
int lutIndex = lookupMcFontPosition(chr);
if (curRandom && lutIndex != -1) {
int randomReplacementIndex;
do {
randomReplacementIndex = fontRandom.nextInt(this.charWidth.length);
} while (this.charWidth[lutIndex] != this.charWidth[randomReplacementIndex]);
lutIndex = randomReplacementIndex;
chr = MCFONT_CHARS.charAt(lutIndex);
}
FontProvider fontProvider = FontStrategist.getFontProvider(this, chr, FontConfig.enableCustomFont, unicodeFlag);
final float shadowOffset = unicodeFlag ? 0.5F : 1.0F;
// Check ASCII space, NBSP, NNBSP
if (chr == ' ' || chr == '\u00A0' || chr == '\u202F') {
curX += 4 * this.getWhitespaceScale();
curX += 4;
continue;
}
final float uStart = fontProvider.getUStart(chr);
final float vStart = fontProvider.getVStart(chr);
final float xAdvance = fontProvider.getXAdvance(chr) * getGlyphScaleX();
final float glyphW = fontProvider.getGlyphW(chr) * getGlyphScaleX();
final float uSz = fontProvider.getUSize(chr);
final float vSz = fontProvider.getVSize(chr);
final float uStart;
final float vStart;
final float xAdvance;
final float glyphW;
final float uSz;
final float vSz;
final float itOff = curItalic ? 1.0F : 0.0F; // italic offset
final float shadowOffset = fontProvider.getShadowOffset();
final ResourceLocation texture = fontProvider.getTexture(chr);
final ResourceLocation texture;
if (lutIndex == -1 || unicodeFlag) {
if (glyphWidth[chr] == 0) {
continue;
}
// Draw unicode char
final int uniPage = chr / 256;
texture = getUnicodePageLocation(uniPage);
final int startColumn = this.glyphWidth[chr] >>> 4;
final int endColumn = this.glyphWidth[chr] & 15;
final float startColumnF = (float) startColumn;
final float endColumnF = (float) (endColumn + 1);
uStart = ((float) (chr % 16 * 16) + startColumnF) / 256.0f;
vStart = ((float) ((chr & 255) / 16 * 16)) / 256.0f;
final float chrWidth = endColumnF - startColumnF - 0.02F;
glyphW = chrWidth / 2.0f + 1.0f;
xAdvance = (endColumnF - startColumnF) / 2.0F + 1.0F;
uSz = chrWidth / 256.0f;
vSz = 15.98f / 256.0f;
} else {
// Draw "ASCII" char
uStart = ((lutIndex % 16) * 8) / 128.0F;
vStart = (float) ((lutIndex / 16) * 8) / 128.0F;
xAdvance = this.charWidth[lutIndex];
if (xAdvance == 0) {
continue;
}
glyphW = xAdvance - 0.01F;
uSz = (glyphW - 1.0F) / 128.0F;
vSz = 7.99F / 128.0F;
texture = locationFontTexture;
}
final int vtxId = vtxWriterIndex;
final int idxId = idxWriterIndex;
@@ -518,50 +493,81 @@ public class BatchingFontRenderer {
int vtxCount = 0;
if (enableShadow) {
pushVtx(curX + itOff + shadowOffset, heightNorth + shadowOffset, curShadowColor, uStart, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX - itOff + shadowOffset, heightNorth + heightSouth + shadowOffset, curShadowColor, uStart, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F + itOff + shadowOffset, heightNorth + shadowOffset, curShadowColor, uStart + uSz, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F - itOff + shadowOffset, heightNorth + heightSouth + shadowOffset, curShadowColor, uStart + uSz, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + itOff + shadowOffset, anchorY + shadowOffset, curShadowColor, uStart, vStart);
pushVtx(
curX - itOff + shadowOffset,
anchorY + 7.99F + shadowOffset,
curShadowColor,
uStart,
vStart + vSz);
pushVtx(curX + glyphW - 1.0F + itOff + shadowOffset,
anchorY + shadowOffset,
curShadowColor,
uStart + uSz,
vStart);
pushVtx(curX + glyphW - 1.0F - itOff + shadowOffset,
anchorY + 7.99F + shadowOffset,
curShadowColor,
uStart + uSz,
vStart + vSz);
pushQuadIdx(vtxId + vtxCount);
vtxCount += 4;
if (curBold) {
final float shadowOffset2 = 2.0f * shadowOffset;
pushVtx(curX + itOff + shadowOffset2, heightNorth + shadowOffset, curShadowColor, uStart, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX - itOff + shadowOffset2, heightNorth + heightSouth + shadowOffset, curShadowColor, uStart, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F + itOff + shadowOffset2, heightNorth + shadowOffset, curShadowColor, uStart + uSz, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F - itOff + shadowOffset2, heightNorth + heightSouth + shadowOffset, curShadowColor, uStart + uSz, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + itOff + shadowOffset2, anchorY + shadowOffset, curShadowColor, uStart, vStart);
pushVtx(
curX - itOff + shadowOffset2,
anchorY + 7.99F + shadowOffset,
curShadowColor,
uStart,
vStart + vSz);
pushVtx(
curX + glyphW - 1.0F + itOff + shadowOffset2,
anchorY + shadowOffset,
curShadowColor,
uStart + uSz,
vStart);
pushVtx(curX + glyphW - 1.0F - itOff + shadowOffset2,
anchorY + 7.99F + shadowOffset,
curShadowColor,
uStart + uSz,
vStart + vSz);
pushQuadIdx(vtxId + vtxCount);
vtxCount += 4;
}
}
pushVtx(curX + itOff, heightNorth, curColor, uStart, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX - itOff, heightNorth + heightSouth, curColor, uStart, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F + itOff, heightNorth, curColor, uStart + uSz, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F - itOff, heightNorth + heightSouth, curColor, uStart + uSz, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(curX + itOff, anchorY, curColor, uStart, vStart);
pushVtx(curX - itOff, anchorY + 7.99F, curColor, uStart, vStart + vSz);
pushVtx(curX + glyphW - 1.0F + itOff, anchorY, curColor, uStart + uSz, vStart);
pushVtx(curX + glyphW - 1.0F - itOff, anchorY + 7.99F, curColor, uStart + uSz, vStart + vSz);
pushQuadIdx(vtxId + vtxCount);
vtxCount += 4;
if (curBold) {
pushVtx(shadowOffset + curX + itOff, heightNorth, curColor, uStart, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(shadowOffset + curX - itOff, heightNorth + heightSouth, curColor, uStart, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(shadowOffset + curX + glyphW - 1.0F + itOff, heightNorth, curColor, uStart + uSz, vStart, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(shadowOffset + curX + glyphW - 1.0F - itOff, heightNorth + heightSouth, curColor, uStart + uSz, vStart + vSz, uStart, uStart + uSz, vStart, vStart + vSz);
pushVtx(shadowOffset + curX + itOff, anchorY, curColor, uStart, vStart);
pushVtx(shadowOffset + curX - itOff, anchorY + 7.99F, curColor, uStart, vStart + vSz);
pushVtx(shadowOffset + curX + glyphW - 1.0F + itOff, anchorY, curColor, uStart + uSz, vStart);
pushVtx(shadowOffset + curX + glyphW - 1.0F - itOff,
anchorY + 7.99F,
curColor,
uStart + uSz,
vStart + vSz);
pushQuadIdx(vtxId + vtxCount);
vtxCount += 4;
}
pushDrawCmd(idxId, vtxCount / 2 * 3, texture, chr > 255);
curX += (xAdvance + (curBold ? shadowOffset : 0.0f)) + getGlyphSpacing();
pushDrawCmd(idxId, vtxCount / 2 * 3, texture);
curX += xAdvance + (curBold ? shadowOffset : 0.0f);
underlineEndX = curX;
strikethroughEndX = curX;
}
if (curUnderline && underlineStartX != underlineEndX) {
final int ulIdx = idxWriterIndex;
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, glyphScaleY, curColor);
pushDrawCmd(ulIdx, 6, null, false);
pushUntexRect(underlineStartX, underlineY, underlineEndX - underlineStartX, 1.0f, curColor);
pushDrawCmd(ulIdx, 6, null);
}
if (curStrikethrough && strikethroughStartX != strikethroughEndX) {
final int ulIdx = idxWriterIndex;
@@ -569,9 +575,9 @@ public class BatchingFontRenderer {
strikethroughStartX,
strikethroughY,
strikethroughEndX - strikethroughStartX,
glyphScaleY,
1.0f,
curColor);
pushDrawCmd(ulIdx, 6, null, false);
pushDrawCmd(ulIdx, 6, null);
}
} finally {
@@ -580,25 +586,4 @@ public class BatchingFontRenderer {
return curX + (enableShadow ? 1.0f : 0.0f);
}
public float getCharWidthFine(char chr) {
if (chr == FORMATTING_CHAR) { return -1; }
if (chr == ' ' || chr == '\u00A0' || chr == '\u202F') {
return 4 * this.getWhitespaceScale();
}
FontProvider fp = FontStrategist.getFontProvider(this, chr, FontConfig.enableCustomFont, underlying.getUnicodeFlag());
return fp.getXAdvance(chr) * this.getGlyphScaleX();
}
public void overrideBlendFunc(int srcRgb, int dstRgb) {
blendSrcRGB = srcRgb;
blendDstRGB = dstRgb;
}
public void resetBlendFunc() {
blendSrcRGB = GL11.GL_SRC_ALPHA;
blendDstRGB = GL11.GL_ONE_MINUS_SRC_ALPHA;
}
}
@@ -1,20 +0,0 @@
package com.gtnewhorizons.angelica.client.font;
import net.minecraft.util.ResourceLocation;
public interface FontProvider {
/**
* For use with §k. Should fetch a character of the same width as provided.
*/
char getRandomReplacement(char chr);
boolean isGlyphAvailable(char chr);
float getUStart(char chr);
float getVStart(char chr);
float getXAdvance(char chr);
float getGlyphW(char chr);
float getUSize(char chr);
float getVSize(char chr);
float getShadowOffset();
ResourceLocation getTexture(char chr);
}
@@ -1,300 +0,0 @@
package com.gtnewhorizons.angelica.client.font;
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
import com.gtnewhorizons.angelica.config.FontConfig;
import lombok.Setter;
import lombok.Value;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.ITextureObject;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.renderer.texture.TextureUtil;
import net.minecraft.util.ResourceLocation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.imageio.ImageIO;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Objects;
public final class FontProviderCustom implements FontProvider {
public static final Logger LOGGER = LogManager.getLogger("Angelica");
public static final String FONT_DIR = "fonts/custom/";
static final int ATLAS_SIZE = 128;
static final int ATLAS_COUNT = 512;
private final byte id; // 0 - primary font, 1 - fallback font
private FontAtlas[] fontAtlases = new FontAtlas[ATLAS_COUNT];
private float currentFontQuality = FontConfig.customFontQuality;
@Setter
private Font font;
private FontProviderCustom(byte id) {
this.id = id;
Font[] availableFonts = FontStrategist.getAvailableFonts();
if (availableFonts.length == 0) {
LOGGER.fatal("There seem to be no fonts available on this system! Disabling custom font and throwing an exception in an attempt to restore the session.");
FontConfig.enableCustomFont = false;
ConfigurationManager.save(FontConfig.class);
throw new RuntimeException();
}
String myFontName = switch (this.id) {
case 0 -> FontConfig.customFontNamePrimary;
case 1 -> FontConfig.customFontNameFallback;
default -> null;
};
if (Objects.equals(myFontName, "(none)")) {
font = null;
return;
}
int fontPos = -1;
for (int i = 0; i < availableFonts.length; i++) {
if (Objects.equals(myFontName, availableFonts[i].getFontName())) {
fontPos = i;
break;
}
}
if (fontPos == -1) {
LOGGER.info("Could not find previously set font \"{}\". ", myFontName);
font = null;
return;
}
font = availableFonts[fontPos].deriveFont(currentFontQuality);
}
private static class InstLoader {
static final FontProviderCustom instance0 = new FontProviderCustom((byte)0);
static final FontProviderCustom instance1 = new FontProviderCustom((byte)1);
}
public static FontProviderCustom getPrimary() { return InstLoader.instance0; }
public static FontProviderCustom getFallback() { return InstLoader.instance1; }
public void reloadFont(int fontID) {
currentFontQuality = FontConfig.customFontQuality;
font = FontStrategist.getAvailableFonts()[fontID].deriveFont(currentFontQuality);
File[] files = new File(getFontDir()).listFiles();
if (files != null) {
for (File f : files) {
if (!Files.isSymbolicLink(f.toPath())) {
f.delete();
}
}
}
TextureManager tm = Minecraft.getMinecraft().getTextureManager();
Map mapTextureObjects = tm.mapTextureObjects;
for (int i = 0; i < ATLAS_COUNT; i++) {
ResourceLocation key = new ResourceLocation(getAtlasResourceName(i));
if (mapTextureObjects.containsKey(key)) {
ITextureObject obj = (ITextureObject) mapTextureObjects.get(key);
TextureUtil.deleteTexture(obj.getGlTextureId());
mapTextureObjects.remove(key);
}
}
fontAtlases = new FontAtlas[ATLAS_COUNT];
}
private String getFontDir() {
return FONT_DIR + "f" + this.id + "/";
}
private String getAtlasFilename(int atlasId) {
return "f" + this.id + "p" + atlasId;
}
String getAtlasResourceName(int atlasId) {
return "minecraft:angelica_c" + getAtlasFilename(atlasId);
}
String getAtlasFullPath(int atlasId) {
return getFontDir() + getAtlasFilename(atlasId) + ".png";
}
@Value
private class GlyphData {
float uStart;
float vStart;
float xAdvance;
float glyphW;
float uSz;
float vSz;
}
private class FontAtlas {
GlyphData[] glyphData = new GlyphData[ATLAS_SIZE];
private ResourceLocation texture;
private final int id;
FontAtlas(int id) {
this.id = id;
}
void construct(Font font) {
float separator = currentFontQuality / 3f;
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
g2d.dispose();
int width = 0;
int actualChars = 0;
for (int i = 0; i < ATLAS_SIZE; i++) {
final char ch = (char)(i + ATLAS_SIZE * this.id);
if (font.canDisplay(ch)) {
width += (int) (separator + fm.charWidth(ch));
actualChars++;
}
}
if (actualChars == 0) {
return;
}
final int atlasTilesX = (int) Math.ceil(Math.sqrt(actualChars) * 1.5f);
final int atlasTilesY = (int) Math.ceil((double) actualChars / atlasTilesX);
LOGGER.info("constructing custom font atlas (ID {}) of nominal size {} chars, actual: {}, {} rows by {} columns",
this.id, ATLAS_SIZE, actualChars, atlasTilesX, atlasTilesY);
width = 0;
actualChars = 0;
int maxRowWidth = 0;
for (int i = 0; i < ATLAS_SIZE; i++) {
if (actualChars % atlasTilesX == 0) {
maxRowWidth = Math.max(maxRowWidth, width);
width = 0;
}
final char ch = (char)(i + ATLAS_SIZE * this.id);
if (font.canDisplay(ch)) {
width += (int) (separator + fm.charWidth(ch));
actualChars++;
}
}
maxRowWidth = Math.max(maxRowWidth, width);
final int lineHeight = fm.getHeight();
final float desc = fm.getDescent();
final int imageWidth = (int) (maxRowWidth + separator);
final int imageHeight = (int) ((separator + lineHeight) * atlasTilesY + separator);
image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
g2d = image.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setFont(font);
fm = g2d.getFontMetrics();
int tileX = 0, tileY = 0; // position in atlas tiles
int imgX = (int) separator; // position in pixels
for (int i = 0; i < ATLAS_SIZE; i++) {
final char ch = (char)(i + ATLAS_SIZE * this.id);
if (!font.canDisplay(ch)) { continue; }
if (tileX >= atlasTilesX) {
tileX = 0;
imgX = (int) separator;
tileY++;
}
final int charWidth = fm.charWidth(ch);
final float charAspectRatio = (float) charWidth / lineHeight;
final float inset = currentFontQuality / 16;
g2d.drawString(Character.toString(ch), imgX, (lineHeight + separator) * (tileY + 1) - desc);
final float uStart = (float) (imgX - inset * charAspectRatio) / imageWidth;
final float vStart = ((lineHeight + separator) * (tileY + 1) - lineHeight - inset) / imageHeight;
final float xAdvance = charAspectRatio * 8 * charWidth / (charWidth + 2 * inset * charAspectRatio);
final float glyphW = charAspectRatio * 8 + 1;
final float uSz = (float) (charWidth + 2 * inset * charAspectRatio) / imageWidth;
final float vSz = (float) (lineHeight + 2 * inset) / imageHeight;
glyphData[i] = new GlyphData(uStart, vStart, xAdvance, glyphW, uSz, vSz);
imgX += (int) (charWidth + separator);
tileX++;
}
g2d.dispose();
try {
LOGGER.info("writing custom font atlas texture ({}x{} px) to {}", imageWidth, imageHeight, getAtlasFullPath(this.id));
Files.createDirectories(Paths.get(getFontDir()));
ImageIO.write(image, "png", new File(getAtlasFullPath(this.id)));
} catch (IOException e) {
e.printStackTrace();
}
this.texture = new ResourceLocation(getAtlasResourceName(this.id));
}
}
private FontAtlas getAtlas(char chr) {
int id = chr / ATLAS_SIZE;
FontAtlas fa = fontAtlases[id];
if (fa == null) {
fa = new FontAtlas(id);
fa.construct(font);
}
fontAtlases[id] = fa;
return fa;
}
@Override
public boolean isGlyphAvailable(char chr) {
if (font == null) { return false; }
return (getAtlas(chr).glyphData[chr % ATLAS_SIZE] != null);
}
@Override
public char getRandomReplacement(char chr) {
return chr;
}
@Override
public float getUStart(char chr) {
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].uStart;
}
@Override
public float getVStart(char chr) {
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].vStart;
}
@Override
public float getXAdvance(char chr) {
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].xAdvance;
}
@Override
public float getGlyphW(char chr) {
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].glyphW;
}
@Override
public float getUSize(char chr) {
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].uSz;
}
@Override
public float getVSize(char chr) {
return getAtlas(chr).glyphData[chr % ATLAS_SIZE].vSz;
}
@Override
public float getShadowOffset() {
return FontConfig.fontShadowOffset;
}
@Override
public ResourceLocation getTexture(char chr) {
return getAtlas(chr).texture;
}
}
@@ -1,106 +0,0 @@
package com.gtnewhorizons.angelica.client.font;
import com.gtnewhorizons.angelica.config.FontConfig;
import it.unimi.dsi.fastutil.chars.Char2ShortOpenHashMap;
import jss.util.RandomXoshiro256StarStar;
import net.minecraft.util.ResourceLocation;
public final class FontProviderMC implements FontProvider {
/**
* The full list of characters present in the default Minecraft font, excluding the Unicode font
*/
@SuppressWarnings("UnnecessaryUnicodeEscape")
private static final String MCFONT_CHARS = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u2302\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";
private FontProviderMC() {}
private static class InstLoader {
static final FontProviderMC instance = new FontProviderMC();
static final FontProviderMC instanceSGA = new FontProviderMC();
}
public static FontProviderMC get(boolean isSGA) {
return (isSGA ? InstLoader.instanceSGA : InstLoader.instance);
}
private static final Char2ShortOpenHashMap MCFONT_ASCII_MAP = new Char2ShortOpenHashMap();
ResourceLocation locationFontTexture = null;
private final RandomXoshiro256StarStar fontRandom = new RandomXoshiro256StarStar();
public int[] charWidth;
static {
for (short i = 0; i < MCFONT_CHARS.length(); i++) {
MCFONT_ASCII_MAP.put(MCFONT_CHARS.charAt(i), i);
}
}
public int lookupMcFontPosition(char ch) {
if (isGlyphAvailable(ch)) {
return MCFONT_ASCII_MAP.get(ch);
}
return -1;
}
@Override
public boolean isGlyphAvailable(char chr) {
return MCFONT_ASCII_MAP.containsKey(chr);
}
public char getRandomReplacement(char chr){
int lutIndex = lookupMcFontPosition(chr);
if (lutIndex != -1) {
int randomReplacementIndex;
do {
randomReplacementIndex = fontRandom.nextInt(this.charWidth.length);
} while (this.charWidth[lutIndex] != this.charWidth[randomReplacementIndex]);
lutIndex = randomReplacementIndex;
return MCFONT_CHARS.charAt(lutIndex);
}
return chr;
}
@Override
public float getUStart(char chr) {
int lutIndex = lookupMcFontPosition(chr);
return ((lutIndex % 16) * 8) / 128.0F;
}
@Override
public float getVStart(char chr) {
int lutIndex = lookupMcFontPosition(chr);
return (float) ((lutIndex / 16) * 8) / 128.0F;
}
@Override
public float getXAdvance(char chr) {
int lutIndex = lookupMcFontPosition(chr);
return charWidth[lutIndex];
}
@Override
public float getGlyphW(char chr) {
int lutIndex = lookupMcFontPosition(chr);
return charWidth[lutIndex] - 0.01F;
}
@Override
public float getUSize(char chr) {
int lutIndex = lookupMcFontPosition(chr);
return (charWidth[lutIndex] - 1.01F) / 128.0F;
}
@Override
public float getVSize(char chr) {
return 7.99F / 128.0F;
}
@Override
public float getShadowOffset() {
return FontConfig.fontShadowOffset;
}
@Override
public ResourceLocation getTexture(char chr) {
return locationFontTexture;
}
}
@@ -1,93 +0,0 @@
package com.gtnewhorizons.angelica.client.font;
import com.gtnewhorizons.angelica.config.FontConfig;
import net.minecraft.util.ResourceLocation;
public final class FontProviderUnicode implements FontProvider {
private FontProviderUnicode() {}
private static class InstLoader { static final FontProviderUnicode instance = new FontProviderUnicode(); }
public static FontProviderUnicode get() { return FontProviderUnicode.InstLoader.instance; }
private static final ResourceLocation[] unicodePageLocations = new ResourceLocation[256];
public byte[] glyphWidth;
private ResourceLocation getUnicodePageLocation(int page) {
final ResourceLocation lookup = unicodePageLocations[page];
if (lookup == null) {
final ResourceLocation rl = new ResourceLocation(String.format(
"textures/font/unicode_page_%02x.png",
page));
unicodePageLocations[page] = rl;
return rl;
} else {
return lookup;
}
}
@Override
public boolean isGlyphAvailable(char chr) {
return true;
}
@Override
public char getRandomReplacement(char chr) {
return chr;
}
@Override
public float getUStart(char chr) {
final float startColumnF = (float)(this.glyphWidth[chr] >>> 4);
return ((float) (chr % 16 * 16) + startColumnF + 0.21f) / 256.0f;
}
@Override
public float getVStart(char chr) {
return ((float) ((chr & 255) / 16 * 16) + 0.21f) / 256.0f;
}
@Override
public float getXAdvance(char chr) {
final int startColumn = this.glyphWidth[chr] >>> 4;
final int endColumn = this.glyphWidth[chr] & 15;
final float startColumnF = (float) startColumn;
final float endColumnF = (float) (endColumn + 1);
return (endColumnF - startColumnF) / 2.0F + 1.0F;
}
@Override
public float getGlyphW(char chr) {
final int startColumn = this.glyphWidth[chr] >>> 4;
final int endColumn = this.glyphWidth[chr] & 15;
final float startColumnF = (float) startColumn;
final float endColumnF = (float) (endColumn + 1);
final float chrWidth = endColumnF - startColumnF - 0.02F;
return chrWidth / 2.0f + 1.0f;
}
@Override
public float getUSize(char chr) {
final int startColumn = this.glyphWidth[chr] >>> 4;
final int endColumn = this.glyphWidth[chr] & 15;
final float startColumnF = (float) startColumn;
final float endColumnF = (float) (endColumn + 1);
final float chrWidth = endColumnF - startColumnF - 0.02F;
return (chrWidth - 0.42f) / 256.0f;
}
@Override
public float getVSize(char chr) {
return (16.0f - 0.42f) / 256.0f;
}
@Override
public float getShadowOffset() {
return FontConfig.fontShadowOffset;
}
@Override
public ResourceLocation getTexture(char chr) {
final int uniPage = chr / 256;
return getUnicodePageLocation(uniPage);
}
}
@@ -1,128 +0,0 @@
package com.gtnewhorizons.angelica.client.font;
import com.google.common.collect.HashMultiset;
import com.gtnewhorizons.angelica.config.FontConfig;
import com.gtnewhorizons.angelica.mixins.interfaces.ResourceAccessor;
import lombok.Getter;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.DefaultResourcePack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.io.File;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
public class FontStrategist {
@Getter
private static final Font[] availableFonts;
public static final Logger LOGGER = LogManager.getLogger("Angelica");
public static boolean customFontInUse;
static {
// get available fonts without duplicates (250 copies of dialog.plain need not apply)
Font[] availableFontsDirty = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
HashMap<String, Font> fontSet = new HashMap<>();
HashMultiset<String> duplicates = HashMultiset.create(); // for debugging
for (Font font : availableFontsDirty) {
String fontName = font.getFontName();
if (fontSet.containsKey(fontName)) {
duplicates.add(fontName);
} else {
fontSet.put(fontName, font);
}
}
if (!duplicates.isEmpty()) {
StringBuilder sb = new StringBuilder(duplicates.size() + " duplicate font(s) found in the list reported by Java: ");
for (Iterator<String> iter = duplicates.stream().distinct().iterator(); iter.hasNext(); ) {
String dupe = iter.next();
sb.append(duplicates.count(dupe)).append("x ").append(dupe);
if (iter.hasNext()) {
sb.append(", ");
}
}
sb.append(". Some fonts may be missing from the font selection menu.");
LOGGER.warn(sb.toString());
}
availableFonts = fontSet.values().stream().sorted(Comparator.comparing(Font::getFontName)).toArray(Font[]::new);
// create and add the resource pack that provides fonts
HashMap<String, File> packMap = new HashMap<>();
for (int i = 0; i < FontProviderCustom.ATLAS_COUNT; i++) {
packMap.put(FontProviderCustom.getPrimary().getAtlasResourceName(i), new File(FontProviderCustom.getPrimary().getAtlasFullPath(i)));
packMap.put(FontProviderCustom.getFallback().getAtlasResourceName(i), new File(FontProviderCustom.getFallback().getAtlasFullPath(i)));
}
DefaultResourcePack fontResourcePack = new DefaultResourcePack(packMap);
List defaultResourcePacks = ((ResourceAccessor) Minecraft.getMinecraft()).angelica$getDefaultResourcePacks();
defaultResourcePacks.add(fontResourcePack);
Minecraft.getMinecraft().refreshResources();
// determine if a custom font is in use based on config values
boolean primaryFontFound = false;
boolean fallbackFontFound = false;
for (Font availableFont : availableFonts) {
if (Objects.equals(FontConfig.customFontNamePrimary, availableFont.getFontName())) {
primaryFontFound = true;
break;
}
if (Objects.equals(FontConfig.customFontNameFallback, availableFont.getFontName())) {
fallbackFontFound = true;
break;
}
}
customFontInUse = (FontConfig.enableCustomFont && (primaryFontFound || fallbackFontFound));
}
/**
Lets you get a FontProvider per char while respecting font priority and fallbacks, the unicode flag, whether or not
SGA is on, if we're in a splash screen, if a font can even display a character in the first place, etc.
*/
public static FontProvider getFontProvider(BatchingFontRenderer me, char chr, boolean customFontEnabled, boolean forceUnicode) {
if (me.isSGA && FontProviderMC.get(true).isGlyphAvailable(chr)) {
return FontProviderMC.get(true);
}
if (customFontEnabled && !me.isSplash) {
FontProvider fp;
fp = FontProviderCustom.getPrimary();
if (fp.isGlyphAvailable(chr)) { return fp; }
fp = FontProviderCustom.getFallback();
if (fp.isGlyphAvailable(chr)) { return fp; }
return FontProviderUnicode.get();
} else {
if (!forceUnicode && FontProviderMC.get(false).isGlyphAvailable(chr)) {
return FontProviderMC.get(false);
} else {
return FontProviderUnicode.get();
}
}
}
public static void reloadCustomFontProviders() {
FontProviderCustom.getPrimary().setFont(null);
FontProviderCustom.getFallback().setFont(null);
boolean primaryFontFound = false;
boolean fallbackFontFound = false;
for (int i = 0; i < availableFonts.length; i++) {
if (Objects.equals(FontConfig.customFontNamePrimary, availableFonts[i].getFontName())) {
FontProviderCustom.getPrimary().reloadFont(i);
primaryFontFound = true;
}
if (Objects.equals(FontConfig.customFontNameFallback, availableFonts[i].getFontName())) {
FontProviderCustom.getFallback().reloadFont(i);
fallbackFontFound = true;
}
if (primaryFontFound && fallbackFontFound) { break; }
}
customFontInUse = (FontConfig.enableCustomFont && (primaryFontFound || fallbackFontFound));
}
}
@@ -1,357 +0,0 @@
package com.gtnewhorizons.angelica.client.gui;
import com.gtnewhorizon.gtnhlib.config.ConfigException;
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
import com.gtnewhorizons.angelica.client.font.FontStrategist;
import com.gtnewhorizons.angelica.config.FontConfig;
import net.coderbot.iris.gui.element.widget.IrisButton;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiSlot;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.resources.I18n;
import org.lwjgl.input.Keyboard;
import java.awt.Font;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class FontConfigScreen extends GuiScreen {
private static final Font[] availableFonts = FontStrategist.getAvailableFonts();
private final GuiScreen parent;
private final String title;
private final String searchPrompt;
private String currentPrimaryFontName;
private String currentFallbackFontName;
private FontList fontList;
private int selectedPrimaryFontListPos = -1;
private int selectedFallbackFontListPos = -1;
private ArrayList<Font> displayedFonts;
private GuiTextField searchBox;
public FontConfigScreen(GuiScreen parent) {
this.title = I18n.format("options.angelica.fontconfig.title");
this.searchPrompt = I18n.format("options.angelica.fontconfig.searchprompt");
this.parent = parent;
this.currentPrimaryFontName = FontConfig.customFontNamePrimary;
this.currentFallbackFontName = FontConfig.customFontNameFallback;
this.displayedFonts = new ArrayList<>(Arrays.asList(availableFonts));
for (int i = 0; i < availableFonts.length; i++) {
if (Objects.equals(this.currentPrimaryFontName, availableFonts[i].getFontName())) {
selectedPrimaryFontListPos = i;
}
if (Objects.equals(this.currentFallbackFontName, availableFonts[i].getFontName())) {
selectedFallbackFontListPos = i;
}
}
}
SliderClone.Option optFontQuality = new SliderClone.Option(6, 72, 6);
SliderClone.Option optShadowOffset = new SliderClone.Option(0, 2, 0.05f);
SliderClone.Option optGlyphAspect = new SliderClone.Option(-1, 1, 0.05f);
SliderClone.Option optGlyphScale = new SliderClone.Option(0.1f, 3, 0.05f);
SliderClone.Option optWhitespaceScale = new SliderClone.Option(0.1f, 3, 0.05f);
SliderClone.Option optGlyphSpacing = new SliderClone.Option(-2f, 2f, 0.05f);
SliderClone.Option optFontAAMode = new SliderClone.Option(0, 2, 1);
SliderClone.Option optFontAAStrength = new SliderClone.Option(1, 24, 1);
public void initGui() {
Keyboard.enableRepeatEvents(true);
searchBox = new GuiTextField(this.fontRendererObj, this.width / 2 - 120, 24, 240, 20);
searchBox.setMaxStringLength(64);
fontList = new FontList();
initButtons();
}
private void initButtons() {
this.buttonList.add(new IrisButton(this.width / 2 - 81 - 165, this.height - 20 - 7, 162, 20,
FontConfig.enableCustomFont ? I18n.format("options.angelica.fontconfig.disable_custom_font") :
I18n.format("options.angelica.fontconfig.enable_custom_font"), this::toggleCustomFont));
this.buttonList.add(new IrisButton(this.width / 2 - 80, this.height - 20 - 7, 160, 20,
I18n.format("options.angelica.fontconfig.reset_values"), this::resetValues));
this.buttonList.add(new IrisButton(this.width / 2 - 81 + 165, this.height - 20 - 7, 162, 20,
I18n.format("gui.done"), button -> this.onClose()));
// might refactor later, feeling too lazy at the moment
this.buttonList.add(new SliderClone( this.width / 2 - 60 - 186, this.height - 40 - 11, 120, 20,
optFontQuality, FontConfig.customFontQuality, this::setFontQuality,
value -> I18n.format("options.angelica.fontconfig.font_quality", String.format("%2.0f", value)),
"options.angelica.fontconfig.font_quality.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 - 186, this.height - 60 - 15, 120, 20,
optShadowOffset, FontConfig.fontShadowOffset, value -> FontConfig.fontShadowOffset = value,
value -> I18n.format("options.angelica.fontconfig.shadow_offset", String.format("x%3.2f", value)),
"options.angelica.fontconfig.shadow_offset.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 - 62, this.height - 40 - 11, 120, 20,
optGlyphAspect, FontConfig.glyphAspect, value -> FontConfig.glyphAspect = value,
value -> I18n.format("options.angelica.fontconfig.glyph_aspect", String.format("%3.2f", value)),
"options.angelica.fontconfig.glyph_aspect.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 - 62, this.height - 60 - 15, 120, 20,
optGlyphScale, FontConfig.glyphScale, value -> FontConfig.glyphScale = value,
value -> I18n.format("options.angelica.fontconfig.glyph_scale", String.format("x%3.2f", value)),
"options.angelica.fontconfig.glyph_scale.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 + 62, this.height - 40 - 11, 120, 20,
optWhitespaceScale, FontConfig.whitespaceScale, value -> FontConfig.whitespaceScale = value,
value -> I18n.format("options.angelica.fontconfig.whitespace_scale", String.format("x%3.2f", value)),
"options.angelica.fontconfig.whitespace_scale.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 + 62, this.height - 60 - 15, 120, 20,
optGlyphSpacing, FontConfig.glyphSpacing, value -> FontConfig.glyphSpacing = value,
value -> I18n.format("options.angelica.fontconfig.glyph_spacing", String.format("%3.2f", value)),
"options.angelica.fontconfig.glyph_spacing.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 + 186, this.height - 40 - 11, 120, 20,
optFontAAStrength, FontConfig.fontAAStrength, value -> FontConfig.fontAAStrength = value.intValue(),
value -> I18n.format("options.angelica.fontconfig.font_aa_strength", String.format("%.0f", value)),
"options.angelica.fontconfig.font_aa_strength.tooltip"));
this.buttonList.add(new SliderClone( this.width / 2 - 60 + 186, this.height - 60 - 15, 120, 20,
optFontAAMode, FontConfig.fontAAMode, value -> FontConfig.fontAAMode = value.intValue(),
this::fontAAModeFormat,
"options.angelica.fontconfig.aamode.tooltip"));
}
private void onClose() {
applyChanges(true);
this.mc.displayGuiScreen(parent);
}
private void applyChanges(boolean finalApply) {
int pos;
pos = selectedPrimaryFontListPos;
if (pos >= 0 && pos < displayedFonts.size()) {
FontConfig.customFontNamePrimary = displayedFonts.get(pos).getFontName();
}
pos = selectedFallbackFontListPos;
if (pos >= 0 && pos < displayedFonts.size()) {
FontConfig.customFontNameFallback = displayedFonts.get(pos).getFontName();
}
FontStrategist.reloadCustomFontProviders();
if (finalApply) {
ConfigurationManager.save(FontConfig.class);
}
}
private void resetValues(IrisButton button) {
try {
ConfigurationManager.getConfigElements(FontConfig.class).forEach(elem -> elem.set(elem.getDefault()));
} catch (ConfigException e) {
throw new RuntimeException(e);
}
selectedPrimaryFontListPos = -1;
selectedFallbackFontListPos = -1;
FontStrategist.customFontInUse = false;
super.buttonList.clear();
this.initButtons();
}
private void toggleCustomFont(IrisButton button) {
FontConfig.enableCustomFont = !FontConfig.enableCustomFont;
applyChanges(false);
button.displayString = FontConfig.enableCustomFont ?
I18n.format("options.angelica.fontconfig.disable_custom_font") :
I18n.format("options.angelica.fontconfig.enable_custom_font");
}
private int qualityLast = FontConfig.customFontQuality;
private void setFontQuality(float quality) {
FontConfig.customFontQuality = (int) quality;
if (qualityLast != (int) quality) {
applyChanges(false);
}
qualityLast = (int) quality;
}
private String fontAAModeFormat(float AAmode) {
return switch ((int) AAmode) {
case 2 -> I18n.format("options.angelica.fontconfig.aamode.aa_16x");
case 1 -> I18n.format("options.angelica.fontconfig.aamode.aa_4x");
default -> I18n.format("options.angelica.fontconfig.aamode.aa_none");
};
}
private float lastMouseX = 0;
private float lastMouseY = 0;
private long lastStillTime = 0;
@Override
public void drawScreen(int mouseX, int mouseY, float delta) {
super.drawDefaultBackground();
fontList.drawScreen(mouseX, mouseY, delta);
searchBox.drawTextBox();
// I bet you thought this was drawn inside the search box, not on top of it.
if (!this.searchBox.isFocused() && this.searchBox.getText().isEmpty()) {
this.drawCenteredString(this.fontRendererObj, this.searchPrompt,
this.searchBox.xPosition + this.fontRendererObj.getStringWidth(this.searchPrompt) / 2 + 4,
this.searchBox.yPosition + this.searchBox.height / 2 - 4, 0xFFFFFF);
}
drawCenteredString(this.fontRendererObj, this.title, (int) (this.width * 0.5), 8, 0xFFFFFF);
drawCenteredString(this.fontRendererObj, I18n.format("options.angelica.fontconfig.currentfonts",
FontConfig.customFontNamePrimary, FontConfig.customFontNameFallback), (int) (this.width * 0.5), this.height - 92, 0xFFFFFF);
super.drawScreen(mouseX, mouseY, delta);
for (GuiButton guiButton : buttonList) {
if (!(guiButton instanceof SliderClone slider)) { continue; }
final int top = slider.yPosition;
final int bot = slider.yPosition + slider.height;
final int left = slider.xPosition;
final int right = slider.xPosition + slider.width;
if (mouseY < top || mouseY >= bot || mouseX < left || mouseX >= right) { continue; }
if (mouseX == lastMouseX && mouseY == lastMouseY) {
if (lastStillTime == 0) {
lastStillTime = System.currentTimeMillis();
}
if (lastStillTime + 500L < System.currentTimeMillis()) {
displayTooltip(mouseX, mouseY, slider.tooltipKey);
}
} else {
lastStillTime = 0;
}
break;
}
lastMouseX = mouseX;
lastMouseY = mouseY;
}
private void displayTooltip(int x, int y, String langKey) {
List<String> lines = this.fontRendererObj.listFormattedStringToWidth(I18n.format(langKey), this.width / 2);
this.drawHoveringText(lines, x, y, this.fontRendererObj);
}
@Override
protected void actionPerformed(GuiButton guiButton) {
if (guiButton.enabled) {
if (guiButton instanceof IrisButton irisButton) {
irisButton.onPress();
}
}
}
@Override
protected void keyTyped(char typedChar, int keyCode) {
if (this.searchBox.isFocused()) {
if (keyCode == Keyboard.KEY_ESCAPE) {
this.searchBox.setFocused(false);
}
this.searchBox.textboxKeyTyped(typedChar, keyCode);
this.displayedFonts = filterFonts(this.searchBox.getText().toLowerCase());
}
if (keyCode == Keyboard.KEY_ESCAPE) {
this.onClose();
}
}
@Override
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) {
super.mouseClicked(mouseX, mouseY, mouseButton);
this.searchBox.mouseClicked(mouseX, mouseY, mouseButton);
this.fontList.mouseClicked(mouseX, mouseY, mouseButton);
}
@Override
public void updateScreen() {
this.searchBox.updateCursorCounter();
}
// this could be more efficient, but it's good enough as is
private ArrayList<Font> filterFonts(String search) {
ArrayList<Font> results;
if (search == null || search.isEmpty()) {
results = new ArrayList<>(Arrays.asList(availableFonts));
} else {
results = Arrays.stream(availableFonts).filter((font -> font.getFontName().toLowerCase().contains(search))).collect(Collectors.toCollection(ArrayList::new));
}
selectedPrimaryFontListPos = -1;
selectedFallbackFontListPos = -1;
for (int i = 0; i < results.size(); i++) {
if (Objects.equals(currentPrimaryFontName, results.get(i).getFontName())) {
selectedPrimaryFontListPos = i;
}
if (Objects.equals(currentFallbackFontName, results.get(i).getFontName())) {
selectedFallbackFontListPos = i;
}
}
return results;
}
class FontList extends GuiSlot {
public FontList() {
super(FontConfigScreen.this.mc, FontConfigScreen.this.width, FontConfigScreen.this.height, 52, FontConfigScreen.this.height - 100, 18);
}
private void mouseClicked(int mouseX, int mouseY, int mouseButton) {
if (mouseY < this.top || mouseY > this.bottom) { return; }
int listLeftBound = this.width / 2 - this.getListWidth() / 2;
int listRightBound = this.width / 2 + this.getListWidth() / 2;
if (mouseX < listLeftBound || mouseX > listRightBound) { return; }
int mousePosInList = mouseY - this.top - this.headerPadding + (int)this.amountScrolled - 4;
int slotIndex = mousePosInList / this.slotHeight;
if (slotIndex < 0 || slotIndex >= this.getSize()) { return; }
if (mouseButton == 0) {
onElemClicked(slotIndex, false);
} else if (mouseButton == 1) {
onElemClicked(slotIndex, true);
}
}
protected void onElemClicked(int index, boolean rightClick) {
if (!rightClick) {
selectedPrimaryFontListPos = index;
currentPrimaryFontName = displayedFonts.get(index).getFontName();
} else {
selectedFallbackFontListPos = index;
currentFallbackFontName = displayedFonts.get(index).getFontName();
}
applyChanges(false);
}
protected boolean isSelected(int index) {
return (index == selectedPrimaryFontListPos || index == selectedFallbackFontListPos);
}
protected int getSize() {
return displayedFonts.size();
}
@Override
protected int getContentHeight() {
return this.getSize() * 18;
}
@Override
protected int getScrollBarX() {
return this.width * 11 / 12 - 5;
}
@Override
public int getListWidth() {
return this.width * 2 / 3;
}
// see onElemClicked for the proper version that supports right-clicking
protected void elementClicked(int index, boolean doubleClicked, int mouseX, int mouseY) {}
protected void drawBackground() {
drawDefaultBackground();
}
protected void drawSlot(int index, int x, int y, int p_148126_4_, Tessellator tessellator, int p_148126_6_, int p_148126_7_) {
int color = 0xffffff;
if (index == selectedPrimaryFontListPos) {
color &= 0xffff55;
}
if (index == selectedFallbackFontListPos) {
color &= 0x55ffff;
}
drawCenteredString(fontRendererObj, displayedFonts.get(index).getFontName(), this.width / 2, y + 1, color);
}
}
}
@@ -1,114 +0,0 @@
package com.gtnewhorizons.angelica.client.gui;
import lombok.AllArgsConstructor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import org.lwjgl.opengl.GL11;
import java.util.function.Consumer;
import java.util.function.Function;
public class SliderClone extends GuiButton {
private final Function<Float, String> displayStringFormatter;
private final Consumer<Float> valueSetter;
private final Option option;
private float sliderPercentage;
public final String tooltipKey;
public boolean mouseDragging;
@AllArgsConstructor
public static class Option {
float min;
float max;
float step;
float denormalize(float alpha) {
float lerp = min * (1 - alpha) + max * alpha;
return Math.round(lerp / step) * step;
}
float normalize(float value) {
return (value - min) / (max - min);
}
}
public SliderClone(int x, int y, int width, int height, Option option, float initialValue,
Consumer<Float> setter, Function<Float, String> formatter, String tooltipKey) {
super(0, x, y, width, height, "");
this.option = option;
this.sliderPercentage = option.normalize(initialValue);
this.displayString = formatter.apply(initialValue);
this.valueSetter = setter;
this.displayStringFormatter = formatter;
this.tooltipKey = tooltipKey;
}
/**
* Returns 0 if the button is disabled, 1 if the mouse is NOT hovering over this button and 2 if it IS hovering over
* this button.
*/
public int getHoverState(boolean mouseOver) {
return 0;
}
/**
* Fired when the mouse button is dragged. Equivalent of MouseListener.mouseDragged(MouseEvent e).
*/
protected void mouseDragged(Minecraft mc, int mouseX, int mouseY) {
if (this.visible) {
if (this.mouseDragging) {
this.sliderPercentage = (float)(mouseX - (this.xPosition + 4)) / (float)(this.width - 8);
if (this.sliderPercentage < 0.0F) {
this.sliderPercentage = 0.0F;
}
if (this.sliderPercentage > 1.0F) {
this.sliderPercentage = 1.0F;
}
float f = this.option.denormalize(this.sliderPercentage);
valueSetter.accept(f);
this.sliderPercentage = this.option.normalize(f);
this.displayString = displayStringFormatter.apply(f);
}
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
this.drawTexturedModalRect(this.xPosition + (int)(this.sliderPercentage * (float)(this.width - 8)), this.yPosition, 0, 66, 4, 20);
this.drawTexturedModalRect(this.xPosition + (int)(this.sliderPercentage * (float)(this.width - 8)) + 4, this.yPosition, 196, 66, 4, 20);
}
}
/**
* Returns true if the mouse has been pressed on this control. Equivalent of MouseListener.mousePressed(MouseEvent e).
*/
public boolean mousePressed(Minecraft mc, int mouseX, int mouseY) {
if (super.mousePressed(mc, mouseX, mouseY)) {
this.sliderPercentage = (float)(mouseX - (this.xPosition + 4)) / (float)(this.width - 8);
if (this.sliderPercentage < 0.0F) {
this.sliderPercentage = 0.0F;
}
if (this.sliderPercentage > 1.0F) {
this.sliderPercentage = 1.0F;
}
float f = this.option.denormalize(this.sliderPercentage);
valueSetter.accept(f);
this.sliderPercentage = this.option.normalize(f);
this.displayString = displayStringFormatter.apply(f);
this.mouseDragging = true;
return true;
} else {
return false;
}
}
/**
* Fired when the mouse button is released. Equivalent of MouseListener.mouseReleased(MouseEvent e).
*/
public void mouseReleased(int mouseX, int mouseY) {
this.mouseDragging = false;
}
}
@@ -26,7 +26,17 @@ public class BlockError extends Block {
public BlockError() {
super(Material.rock);
MinecraftForge.EVENT_BUS.register(new EventHandler());
MinecraftForge.EVENT_BUS.register(this);
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
public void onTextureStitch(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() == 0) {
for (int i = 0; i < icons.length; i++) {
icons[i] = event.map.registerIcon("angelica:error_block_" + i);
}
}
}
@Override
@@ -35,15 +45,4 @@ public class BlockError extends Block {
return meta >= 0 && meta < icons.length ? icons[meta] : null;
}
public static class EventHandler{
@SubscribeEvent
@SideOnly(Side.CLIENT)
public void onTextureStitch(TextureStitchEvent.Pre event) {
if (event.map.getTextureType() == 0) {
for (int i = 0; i < icons.length; i++) {
icons[i] = event.map.registerIcon("angelica:error_block_" + i);
}
}
}
}
}
@@ -1,6 +1,8 @@
package com.gtnewhorizons.angelica.compat;
import com.falsepattern.chunk.api.DataRegistry;
import com.gtnewhorizons.angelica.mixins.interfaces.ExtendedBlockStorageAccessor;
import com.gtnewhorizons.angelica.mixins.interfaces.ExtendedNibbleArray;
import com.gtnewhorizons.neid.mixins.interfaces.IExtendedBlockStorageMixin;
import net.minecraft.world.chunk.Chunk;
@@ -17,7 +19,7 @@ public class ExtendedBlockStorageExt extends ExtendedBlockStorage {
}
public ExtendedBlockStorageExt(Chunk chunk, ExtendedBlockStorage storage) {
super(storage.yBase, storage.getSkylightArray() != null);
super(((ExtendedBlockStorageAccessor) storage).getYBase(), storage.getSkylightArray() != null);
if (ModStatus.isChunkAPILoaded) {
if (storage.getSkylightArray() != null) {
@@ -31,7 +33,7 @@ public class ExtendedBlockStorageExt extends ExtendedBlockStorage {
System.arraycopy(((IExtendedBlockStorageMixin)(Object)storage).getBlock16BArray(), 0, block16BArray, 0, block16BArray.length);
if(storage.getBlockMSBArray() != null) {
this.setBlockMSBArray(new NibbleArray(block16BArray.length, 4));
copyNibbleArray(storage.getBlockMSBArray(), this.getBlockMSBArray());
copyNibbleArray((ExtendedNibbleArray) storage.getBlockMSBArray(), (ExtendedNibbleArray) this.getBlockMSBArray());
}
arrayLen = block16BArray.length;
if (ModStatus.isNEIDMetadataExtended) {
@@ -50,31 +52,31 @@ public class ExtendedBlockStorageExt extends ExtendedBlockStorage {
System.arraycopy(storage.getBlockLSBArray(), 0, blockLSBArray, 0, blockLSBArray.length);
if(storage.getBlockMSBArray() != null) {
this.setBlockMSBArray(new NibbleArray(blockLSBArray.length, 4));
copyNibbleArray(storage.getBlockMSBArray(), this.getBlockMSBArray());
copyNibbleArray((ExtendedNibbleArray) storage.getBlockMSBArray(), (ExtendedNibbleArray) this.getBlockMSBArray());
}
arrayLen = blockLSBArray.length;
}
if (!ModStatus.isNEIDMetadataExtended) copyNibbleArray(storage.getMetadataArray(), this.getMetadataArray());
copyNibbleArray(storage.getBlocklightArray(), this.getBlocklightArray());
if (!ModStatus.isNEIDMetadataExtended) copyNibbleArray((ExtendedNibbleArray) storage.getMetadataArray(), (ExtendedNibbleArray)this.getMetadataArray());
copyNibbleArray((ExtendedNibbleArray) storage.getBlocklightArray(), (ExtendedNibbleArray)this.getBlocklightArray());
if(storage.getSkylightArray() != null) {
hasSky = true;
if(this.getSkylightArray() == null) {
this.setSkylightArray(new NibbleArray(arrayLen, 4));
}
copyNibbleArray(storage.getSkylightArray(), this.getSkylightArray());
copyNibbleArray((ExtendedNibbleArray) storage.getSkylightArray(), (ExtendedNibbleArray) this.getSkylightArray());
}
}
this.blockRefCount = storage.blockRefCount;
((ExtendedBlockStorageAccessor) this).setBlockRefCount(((ExtendedBlockStorageAccessor) storage).getBlockRefCount());
}
private static void copyNibbleArray(NibbleArray srcArray, NibbleArray dstArray) {
private static void copyNibbleArray(ExtendedNibbleArray srcArray, ExtendedNibbleArray dstArray) {
if (srcArray == null || dstArray == null) {
throw new RuntimeException("NibbleArray is null src: " + (srcArray == null) + " dst: " + (dstArray == null));
}
final byte[] data = srcArray.data;
System.arraycopy(data, 0, dstArray.data, 0, data.length);
final byte[] data = srcArray.getData();
System.arraycopy(data, 0, dstArray.getData(), 0, data.length);
}
}
@@ -1,17 +1,14 @@
package com.gtnewhorizons.angelica.compat;
import com.gtnewhorizons.angelica.compat.backhand.BackhandReflectionCompat;
import com.gtnewhorizons.angelica.helpers.LoadControllerHelper;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.versioning.ArtifactVersion;
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
import mods.battlegear2.Battlegear;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.dries007.holoInventory.HoloInventory;
import xonin.backhand.Backhand;
public class ModStatus {
public static final Logger LOGGER = LogManager.getLogger("ModCompat");
public static BackhandReflectionCompat backhandCompat;
/**
* Mixin Version
*/
@@ -20,6 +17,7 @@ public class ModStatus {
* ASM Version
*/
public static boolean isOldNEIDLoaded;
public static boolean isBetterCrashesLoaded;
public static boolean isNEIDMetadataExtended;
public static boolean isLotrLoaded;
@@ -32,14 +30,8 @@ public class ModStatus {
public static boolean isThaumcraftLoaded;
public static boolean isThaumicHorizonsLoaded;
public static boolean isBaublesLoaded;
public static boolean isFluidLoggedLoaded;
public static void preInit(){
isBackhandLoaded = Loader.isModLoaded("backhand");
if(isBackhandLoaded) {
isBackhandLoaded = BackhandReflectionCompat.isBackhandLoaded();
}
isBetterCrashesLoaded = Loader.isModLoaded("bettercrashes");
isNEIDLoaded = Loader.isModLoaded("neid");
isOldNEIDLoaded = Loader.isModLoaded("notenoughIDs");
@@ -49,12 +41,17 @@ public class ModStatus {
isXaerosMinimapLoaded = Loader.isModLoaded("XaeroMinimap");
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
isBattlegearLoaded = Loader.isModLoaded("battlegear2");
isBackhandLoaded = Loader.isModLoaded("backhand");
isThaumcraftLoaded = Loader.isModLoaded("Thaumcraft");
isThaumicHorizonsLoaded = Loader.isModLoaded("ThaumicHorizons");
isBaublesLoaded = Loader.isModLoaded("Baubles");
isFluidLoggedLoaded = Loader.isModLoaded("fluidlogged");
isHoloInventoryLoaded = Loader.isModLoaded("holoinventory");
if (isHoloInventoryLoaded){
isHoloInventoryLoaded = new DefaultArtifactVersion("2.4.4-GTNH")
.compareTo(
LoadControllerHelper.getOwningMod(HoloInventory.class).getProcessedVersion()
) <= 0;
}
// remove compat with original release of BG2
if (isBattlegearLoaded){
@@ -64,9 +61,17 @@ public class ModStatus {
) <= 0;
}
if (isBackhandLoaded){
isBackhandLoaded = new DefaultArtifactVersion("1.4.1")
.compareTo(
LoadControllerHelper.getOwningMod(Backhand.class).getProcessedVersion()
) <= 0;
}
isNEIDMetadataExtended = false;
if (isNEIDLoaded) {
final int majorVersion = Integer.parseInt(Loader.instance().getIndexedModList().get("neid").getVersion().split("\\.")[0]);
int majorVersion = Integer.parseInt(Loader.instance().getIndexedModList().get("neid").getVersion().split("\\.")[0]);
if (majorVersion >= 2) {
isNEIDMetadataExtended = true;
}
@@ -1,76 +0,0 @@
package com.gtnewhorizons.angelica.compat.backhand;
import com.gtnewhorizons.angelica.helpers.LoadControllerHelper;
import cpw.mods.fml.common.versioning.DefaultArtifactVersion;
import cpw.mods.fml.relauncher.ReflectionHelper;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import static com.gtnewhorizons.angelica.compat.ModStatus.LOGGER;
public class BackhandReflectionCompat {
private static final String BACKHAND_CLASS = "xonin.backhand.Backhand";
private static final String ITEM_RENDERER_HOOKS_CLASS = "xonin.backhand.client.hooks.ItemRendererHooks";
private static final String BACKHAND_UTILS_CLASS = "xonin.backhand.api.core.BackhandUtils";
private static boolean isLoaded;
private static final MethodHandle renderOffhandReturn;
private static final MethodHandle getOffhandItem;
static {
MethodHandle renderOffhandReturnTemp = null;
MethodHandle getOffhandItemTemp = null;
try {
final Class<?> backhandClass = ReflectionHelper.getClass(BackhandReflectionCompat.class.getClassLoader(), BACKHAND_CLASS);
final boolean versionCheck = new DefaultArtifactVersion("1.6.9").compareTo(LoadControllerHelper.getOwningMod(backhandClass).getProcessedVersion()) <= 0;
if (versionCheck) {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
final Class<?> itemRendererHooksClass = ReflectionHelper.getClass(BackhandReflectionCompat.class.getClassLoader(), ITEM_RENDERER_HOOKS_CLASS);
final Class<?> backhandUtilsClass = ReflectionHelper.getClass(BackhandReflectionCompat.class.getClassLoader(), BACKHAND_UTILS_CLASS);
renderOffhandReturnTemp = lookup.findStatic(itemRendererHooksClass, "renderOffhandReturn", MethodType.methodType(void.class, float.class));
getOffhandItemTemp = lookup.findStatic(backhandUtilsClass, "getOffhandItem", MethodType.methodType(ItemStack.class, EntityPlayer.class));
isLoaded = true;
LOGGER.info("Backhand compat loaded");
}
} catch (Exception e) {
LOGGER.error("Failed to load Backhand compat", e);
isLoaded = false;
}
renderOffhandReturn = renderOffhandReturnTemp;
getOffhandItem = getOffhandItemTemp;
}
public static boolean isBackhandLoaded() {
return isLoaded;
}
public static void renderOffhand(float partialTicks) {
if (isLoaded && renderOffhandReturn != null) {
try {
renderOffhandReturn.invokeExact(partialTicks);
} catch (Throwable e) {
LOGGER.error("Failed to invoke Backhand renderOffhandReturn", e);
isLoaded = false;
}
}
}
public static ItemStack getOffhandItem(EntityPlayer player) {
if (isLoaded && getOffhandItem != null) {
try {
return (ItemStack) getOffhandItem.invokeExact(player);
} catch (Throwable e) {
LOGGER.error("Failed to invoke Backhand getOffhandItem", e);
isLoaded = false;
}
}
return null;
}
}
@@ -1,15 +0,0 @@
package com.gtnewhorizons.angelica.compat.battlegear2;
import mods.battlegear2.api.core.IInventoryPlayerBattle;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
/**
* This class exists to isolate classes that exist in Battlegear 2 but not
* in Battlegear 2 for Backhand in order to avoid class loading exceptions.
*/
public class Battlegear2Compat {
public static ItemStack getBattlegear2Offhand(EntityPlayer player) {
return ((IInventoryPlayerBattle) player.inventory).battlegear2$getCurrentOffhandWeapon();
}
}
@@ -1,93 +0,0 @@
package com.gtnewhorizons.angelica.compat.holoinventory;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import static com.gtnewhorizons.angelica.compat.ModStatus.LOGGER;
public class HoloInventoryReflectionCompat {
private static final String RENDERER_CLASS = "net.dries007.holoInventory.client.Renderer";
private static boolean isLoaded;
private static final MethodHandle renderEventPostMethod;
private static final MethodHandle renderEventMethod;
private static final MethodHandle angelicaOverrideSetter;
static {
MethodHandle renderEventPostMethodTemp = null;
MethodHandle renderEventMethodTemp = null;
MethodHandle angelicaOverrideSetterTemp = null;
final Object rendererInstance;
final Class<?> rendererClass;
try {
rendererClass = Class.forName(RENDERER_CLASS);
// Get and cache the Renderer.INSTANCE
final Field instanceField = rendererClass.getDeclaredField("INSTANCE");
instanceField.setAccessible(true);
rendererInstance = instanceField.get(null);
final MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
renderEventPostMethodTemp = lookup.findVirtual(rendererClass, "renderEvent", MethodType.methodType(void.class, RenderGameOverlayEvent.Post.class));
} catch (NoSuchMethodException e) {
// Fallback for 2.5.0-GTNH and earlier
renderEventMethodTemp = lookup.findVirtual(rendererClass, "renderEvent", MethodType.methodType(void.class, RenderGameOverlayEvent.class));
}
final Field angelicaOverrideField = rendererClass.getDeclaredField("angelicaOverride");
angelicaOverrideField.setAccessible(true);
angelicaOverrideSetterTemp = lookup.unreflectSetter(angelicaOverrideField);
// Bind the method handles to the instance
if (rendererInstance != null) {
if (renderEventPostMethodTemp != null) {
renderEventPostMethodTemp = renderEventPostMethodTemp.bindTo(rendererInstance);
}
if (renderEventMethodTemp != null) {
renderEventMethodTemp = renderEventMethodTemp.bindTo(rendererInstance);
}
angelicaOverrideSetterTemp = angelicaOverrideSetterTemp.bindTo(rendererInstance);
}
isLoaded = true;
LOGGER.info("Successfully initialized HoloInventory compatibility layer");
} catch (Exception e) {
LOGGER.warn("Failed to initialize HoloInventory compatibility layer", e);
isLoaded = false;
}
renderEventPostMethod = renderEventPostMethodTemp;
renderEventMethod = renderEventMethodTemp;
angelicaOverrideSetter = angelicaOverrideSetterTemp;
}
public static void renderEvent(RenderGameOverlayEvent event) {
if (!isLoaded) return;
try {
if (event instanceof RenderGameOverlayEvent.Post && renderEventPostMethod != null) {
renderEventPostMethod.invokeExact((RenderGameOverlayEvent.Post) event);
} else {
renderEventMethod.invokeExact(event);
}
} catch (Throwable e) {
LOGGER.error("Failed to invoke HoloInventory renderEvent", e);
isLoaded = false;
}
}
public static void setAngelicaOverride(boolean value) {
if (!isLoaded) return;
try {
angelicaOverrideSetter.invokeExact(value);
} catch (Throwable e) {
LOGGER.error("Failed to set HoloInventory angelicaOverride", e);
isLoaded = false;
}
}
}
@@ -0,0 +1,327 @@
package com.gtnewhorizons.angelica.compat.lwjgl;
import static com.gtnewhorizons.angelica.compat.lwjgl.Pointer.BITS64;
import it.unimi.dsi.fastutil.longs.LongPredicate;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
/**
* Backported from LWJGL3 under the BSD 3-Clause "New" or "Revised" License license
*
* <p>This class provides functionality for managing native memory.
*
* <p>All methods in this class will make use of {@link sun.misc.Unsafe} if it's available, for performance. If Unsafe is not available, the fallback
* implementations make use of reflection and, in the worst-case, JNI.</p>
*
* <p>Method names in this class are prefixed with {@code mem} to avoid ambiguities when used with static imports.</p>
*/
public class CompatMemoryUtil {
public static final long NULL = 0;
static final sun.misc.Unsafe UNSAFE;
private static final Class<? extends ByteBuffer> BUFFER_BYTE;
private static final Class<? extends IntBuffer> BUFFER_INT;
private static final Class<? extends FloatBuffer> BUFFER_FLOAT;
private static final long MARK;
private static final long POSITION;
private static final long LIMIT;
private static final long CAPACITY;
private static final long ADDRESS;
static {
ByteBuffer bb = ByteBuffer.allocateDirect(0).order(ByteOrder.nativeOrder());
BUFFER_BYTE = bb.getClass();
BUFFER_INT = bb.asIntBuffer().getClass();
BUFFER_FLOAT = bb.asFloatBuffer().getClass();
UNSAFE = getUnsafeInstance();
MARK = getMarkOffset();
POSITION = getPositionOffset();
LIMIT = getLimitOffset();
CAPACITY = getCapacityOffset();
ADDRESS = getAddressOffset();
}
public static ByteBuffer memReallocDirect(ByteBuffer old, int capacity) {
ByteBuffer newBuf = BufferUtils.createByteBuffer(capacity);
int oldPos = old.position();
old.rewind();
newBuf.put(old);
newBuf.position(Math.min(capacity, oldPos));
return newBuf;
}
public static IntBuffer memReallocDirect(IntBuffer old, int capacity) {
IntBuffer newBuf = BufferUtils.createIntBuffer(capacity);
int oldPos = old.position();
old.rewind();
newBuf.put(old);
newBuf.position(Math.min(capacity, oldPos));
return newBuf;
}
public static FloatBuffer memReallocDirect(FloatBuffer old, int capacity) {
FloatBuffer newBuf = BufferUtils.createFloatBuffer(capacity);
int oldPos = old.position();
old.rewind();
newBuf.put(old);
newBuf.position(Math.min(capacity, oldPos));
return newBuf;
}
/* -------------------------------------
-------------------------------------
UNSAFE MEMORY ACCESS API
-------------------------------------
------------------------------------- */
private static final int FILL_PATTERN_32 = Integer.divideUnsigned(-1, 255);
private static final long FILL_PATTERN_64 = Long.divideUnsigned(-1L, 255L);
/**
* Sets all bytes in a specified block of memory to a fixed value (usually zero).
*
* @param ptr the starting memory address
* @param value the value to set (memSet will convert it to unsigned byte)
* @param bytes the number of bytes to set
*/
public static void memSet(long ptr, int value, long bytes) {
if (/*DEBUG*/ false && (ptr == NULL || bytes < 0)) {
throw new IllegalArgumentException();
}
/*
- Unsafe.setMemory is very slow.
- A custom Java loop is fastest at small sizes, approximately up to 256 bytes.
- The native memset becomes fastest at bigger sizes, when the JNI overhead becomes negligible.
TODO: verify this
*/
//UNSAFE.setMemory(ptr, bytes, (byte)(value & 0xFF));
//if (bytes < 256L) {
int p = (int)ptr;
if (BITS64) {
if ((p & 7) == 0) {
memSet64(ptr, value, (int)bytes & 0xFF);
return;
}
} else {
if ((p & 3) == 0) {
memSet32(p, value, (int)bytes & 0xFF);
return;
}
}
//}
//nmemset(ptr, value, bytes);
}
private static void memSet64(long ptr, int value, int bytes) {
int aligned = bytes & ~7;
// Aligned body
long valuel = (value & 0xFF) * FILL_PATTERN_64;
for (int i = 0; i < aligned; i += 8) {
UNSAFE.putLong(null, ptr + i, valuel);
}
// Unaligned tail
byte valueb = (byte)(value & 0xFF);
for (int i = aligned; i < bytes; i++) {
UNSAFE.putByte(null, ptr + i, valueb);
}
}
private static void memSet32(int ptr, int value, int bytes) {
int aligned = bytes & ~3;
// Aligned body
int vi = (value & 0xFF) * FILL_PATTERN_32;
for (int i = 0; i < aligned; i += 4) {
UNSAFE.putInt(null, (ptr + i) & 0xFFFF_FFFFL, vi);
}
// Unaligned tail
byte vb = (byte)(value & 0xFF);
for (int i = aligned; i < bytes; i++) {
UNSAFE.putByte(null, (ptr + i) & 0xFFFF_FFFFL, vb);
}
}
public static boolean memGetBoolean(long ptr) { return UNSAFE.getByte(null, ptr) != 0; }
public static byte memGetByte(long ptr) { return UNSAFE.getByte(null, ptr); }
public static short memGetShort(long ptr) { return UNSAFE.getShort(null, ptr); }
public static int memGetInt(long ptr) { return UNSAFE.getInt(null, ptr); }
public static long memGetLong(long ptr) { return UNSAFE.getLong(null, ptr); }
public static float memGetFloat(long ptr) { return UNSAFE.getFloat(null, ptr); }
public static double memGetDouble(long ptr) { return UNSAFE.getDouble(null, ptr); }
public static void memPutByte(long ptr, byte value) { UNSAFE.putByte(null, ptr, value); }
public static void memPutShort(long ptr, short value) { UNSAFE.putShort(null, ptr, value); }
public static void memPutInt(long ptr, int value) { UNSAFE.putInt(null, ptr, value); }
public static void memPutLong(long ptr, long value) { UNSAFE.putLong(null, ptr, value); }
public static void memPutFloat(long ptr, float value) { UNSAFE.putFloat(null, ptr, value); }
public static void memPutDouble(long ptr, double value) { UNSAFE.putDouble(null, ptr, value); }
// -------------------------------------------------
// -------------------------------------------------
// -------------------------------------------------
private static sun.misc.Unsafe getUnsafeInstance() {
java.lang.reflect.Field[] fields = sun.misc.Unsafe.class.getDeclaredFields();
/*
Different runtimes use different names for the Unsafe singleton,
so we cannot use .getDeclaredField and we scan instead. For example:
Oracle: theUnsafe
PERC : m_unsafe_instance
Android: THE_ONE
*/
for (java.lang.reflect.Field field : fields) {
if (!field.getType().equals(sun.misc.Unsafe.class)) {
continue;
}
int modifiers = field.getModifiers();
if (!(java.lang.reflect.Modifier.isStatic(modifiers) && java.lang.reflect.Modifier.isFinal(modifiers))) {
continue;
}
try {
field.setAccessible(true);
return (sun.misc.Unsafe)field.get(null);
} catch (Exception ignored) {
}
break;
}
throw new UnsupportedOperationException("LWJGL requires sun.misc.Unsafe to be available.");
}
/**
* Recursively searches an object and its superclasses for a field with the same name and type. If the field isn't
* public, it may not be stable across library/JVM versions, and these calls are likely not portable. You have been
* warned!
*/
private static long getFieldOffset(Class<?> containerType, Class<?> fieldType, String name) {
Class<?> c = containerType;
while (c != Object.class) {
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
if (!field.getType().isAssignableFrom(fieldType) || Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) {
continue;
}
long offset = UNSAFE.objectFieldOffset(field);
if (field.getName().equals(name)) {
return offset;
}
}
c = c.getSuperclass();
}
throw new UnsupportedOperationException("Failed to find field offset in class.");
}
private static long getFieldOffset(Class<?> containerType, Class<?> fieldType, LongPredicate predicate) {
Class<?> c = containerType;
while (c != Object.class) {
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
if (!field.getType().isAssignableFrom(fieldType) || Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) {
continue;
}
long offset = UNSAFE.objectFieldOffset(field);
if (predicate.test(offset)) {
return offset;
}
}
c = c.getSuperclass();
}
throw new UnsupportedOperationException("Failed to find field offset in class.");
}
private static long getFieldOffsetInt(Object container, int value) {
return getFieldOffset(container.getClass(), int.class, offset -> UNSAFE.getInt(container, offset) == value);
}
private static long getAddressOffset() {
final ByteBuffer bb = ByteBuffer.allocateDirect(0);
return getFieldOffset(bb.getClass(), long.class, "address");
// TODO: do this portably
// long MAGIC_ADDRESS = 0xDEADBEEF8BADF00DL & (BITS32 ? 0xFFFF_FFFFL : 0xFFFF_FFFF_FFFF_FFFFL);
// ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(MAGIC_ADDRESS, 0));
//return getFieldOffset(bb.getClass(), long.class, offset -> UNSAFE.getLong(bb, offset) == MAGIC_ADDRESS);
}
private static final int MAGIC_CAPACITY = 0x0D15EA5E;
private static final int MAGIC_POSITION = 0x00FACADE;
private static long getMarkOffset() {
// TODO: is this actually reliable without NewDirectByteBuffer?
ByteBuffer bb = ByteBuffer.allocateDirect(0);
return getFieldOffsetInt(bb, -1);
// ByteBuffer bb = Objects.requireNonNull(NewDirectByteBuffer(1L, 0));
// return getFieldOffsetInt(bb, -1);
}
private static long getPositionOffset() {
ByteBuffer bb = ByteBuffer.allocateDirect(MAGIC_CAPACITY);
bb.position(MAGIC_POSITION);
return getFieldOffsetInt(bb, MAGIC_POSITION);
}
private static long getLimitOffset() {
ByteBuffer bb = ByteBuffer.allocateDirect(MAGIC_CAPACITY);
bb.limit(MAGIC_POSITION);
return getFieldOffsetInt(bb, MAGIC_POSITION);
}
private static long getCapacityOffset() {
ByteBuffer bb = ByteBuffer.allocateDirect(MAGIC_CAPACITY);
bb.limit(0);
return getFieldOffsetInt(bb, MAGIC_CAPACITY);
}
static IntBuffer wrapBufferInt(long address, int capacity) {
IntBuffer buffer;
try {
buffer = (IntBuffer)UNSAFE.allocateInstance(BUFFER_INT);
} catch (InstantiationException e) {
throw new UnsupportedOperationException(e);
}
UNSAFE.putLong(buffer, ADDRESS, address);
UNSAFE.putInt(buffer, MARK, -1);
UNSAFE.putInt(buffer, LIMIT, capacity);
UNSAFE.putInt(buffer, CAPACITY, capacity);
return buffer;
}
static FloatBuffer wrapBufferFloat(long address, int capacity) {
FloatBuffer buffer;
try {
buffer = (FloatBuffer)UNSAFE.allocateInstance(BUFFER_FLOAT);
} catch (InstantiationException e) {
throw new UnsupportedOperationException(e);
}
UNSAFE.putLong(buffer, ADDRESS, address);
UNSAFE.putInt(buffer, MARK, -1);
UNSAFE.putInt(buffer, LIMIT, capacity);
UNSAFE.putInt(buffer, CAPACITY, capacity);
return buffer;
}
}
@@ -0,0 +1,382 @@
/*
* This class is backported from LWJGL3 under the BSD 3-clause "New" or "Revised" License
*/
package com.gtnewhorizons.angelica.compat.lwjgl;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutDouble;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutFloat;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutInt;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutLong;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memPutShort;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.memSet;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.wrapBufferFloat;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.wrapBufferInt;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.BufferUtils;
import org.lwjgl.MemoryUtil;
/**
* An off-heap memory stack.
*
* <p>This class should be used in a thread-local manner for stack allocations.</p>
*
* @ see Configuration#STACK_SIZE
* @ see Configuration#DEBUG_STACK
*/
@SuppressWarnings("LombokGetterMayBeUsed")
public class MemoryStack extends Pointer.Default implements AutoCloseable {
private static final int DEFAULT_STACK_SIZE = /*Configuration.STACK_SIZE.get(64)*/ 64 * 1024;
private static final int DEFAULT_STACK_FRAMES = 8;
private static final ThreadLocal<MemoryStack> TLS = ThreadLocal.withInitial(MemoryStack::create);
static {
if (DEFAULT_STACK_SIZE < 0) {
throw new IllegalStateException("Invalid stack size.");
}
}
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private final @Nullable ByteBuffer container;
private final int size;
private int pointer;
private int[] frames;
protected int frameIndex;
/**
* Creates a new {@code MemoryStack} backed by the specified memory region.
*
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
*
* @param container the backing memory buffer, may be null
* @param address the backing memory address
* @param size the backing memory size
*/
protected MemoryStack(@Nullable ByteBuffer container, long address, int size) {
super(address);
this.container = container;
this.size = size;
this.pointer = size;
this.frames = new int[DEFAULT_STACK_FRAMES];
}
/**
* Creates a new {@code MemoryStack} with the default size.
*
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
*/
public static MemoryStack create() {
return create(DEFAULT_STACK_SIZE);
}
/**
* Creates a new {@code MemoryStack} with the specified size.
*
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
*
* @param capacity the maximum number of bytes that may be allocated on the stack
*/
public static MemoryStack create(int capacity) {
return create(BufferUtils.createByteBuffer(capacity));
}
/**
* Creates a new {@code MemoryStack} backed by the specified memory buffer.
*
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
*
* @param buffer the backing memory buffer
*/
public static MemoryStack create(ByteBuffer buffer) {
long address = MemoryUtil.getAddress(buffer);
int size = buffer.remaining();
return new MemoryStack(buffer, address, size);
}
/**
* Creates a new {@code MemoryStack} backed by the specified memory region.
*
* <p>In the initial state, there is no active stack frame. The {@link #push} method must be used before any allocations.</p>
*
* @param address the backing memory address
* @param size the backing memory size
*/
public static MemoryStack ncreate(long address, int size) {
return new MemoryStack(null, address, size);
}
/**
* Stores the current stack pointer and pushes a new frame to the stack.
*
* <p>This method should be called when entering a method, before doing any stack allocations. When exiting a method, call the {@link #pop} method to
* restore the previous stack frame.</p>
*
* <p>Pairs of push/pop calls may be nested. Care must be taken to:</p>
* <ul>
* <li>match every push with a pop</li>
* <li>not call pop before push has been called at least once</li>
* <li>not nest push calls to more than the maximum supported depth</li>
* </ul>
*
* @return this stack
*/
public MemoryStack push() {
if (frameIndex == frames.length) {
frameOverflow();
}
frames[frameIndex++] = pointer;
return this;
}
private void frameOverflow() {
if (/*DEBUG*/ false) {
AngelicaTweaker.LOGGER.warn("[WARNING] Out of frame stack space (" + frames.length + ") in thread: " + Thread.currentThread());
}
frames = Arrays.copyOf(frames, frames.length * 3 / 2);
}
/**
* Pops the current stack frame and moves the stack pointer to the end of the previous stack frame.
*
* @return this stack
*/
public MemoryStack pop() {
pointer = frames[--frameIndex];
return this;
}
/**
* Calls {@link #pop} on this {@code MemoryStack}.
*
* <p>This method should not be used directly. It is called automatically when the {@code MemoryStack} is used as a resource in a try-with-resources
* statement.</p>
*/
@Override
public void close() {
//noinspection resource
pop();
}
/**
* Returns the address of the backing off-heap memory.
*
* <p>The stack grows "downwards", so the bottom of the stack is at {@code address + size}, while the top is at {@code address}.</p>
*/
public long getAddress() {
return address;
}
/**
* Returns the size of the backing off-heap memory.
*
* <p>This is the maximum number of bytes that may be allocated on the stack.</p>
*/
public int getSize() {
return size;
}
/**
* Returns the current frame index.
*
* <p>This is the current number of nested {@link #push} calls.</p>
*/
public int getFrameIndex() {
return frameIndex;
}
/** Returns the memory address at the current stack pointer. */
public long getPointerAddress() {
return address + (pointer & 0xFFFF_FFFFL);
}
/**
* Returns the current stack pointer.
*
* <p>The stack grows "downwards", so when the stack is empty {@code pointer} is equal to {@code size}. On every allocation {@code pointer} is reduced by
* the allocated size (after alignment) and {@code address + pointer} points to the first byte of the last allocation.</p>
*
* <p>Effectively, this methods returns how many more bytes may be allocated on the stack.</p>
*/
public int getPointer() {
return pointer;
}
/**
* Sets the current stack pointer.
*
* <p>This method directly manipulates the stack pointer. Using it irresponsibly may break the internal state of the stack. It should only be used in rare
* cases or in auto-generated code.</p>
*/
public void setPointer(int pointer) {
if (/*CHECKS*/ false) {
checkPointer(pointer);
}
this.pointer = pointer;
}
private void checkPointer(int pointer) {
if (pointer < 0 || size < pointer) {
throw new IndexOutOfBoundsException("Invalid stack pointer");
}
}
private static void checkAlignment(int alignment) {
if (Integer.bitCount(alignment) != 1) {
throw new IllegalArgumentException("Alignment must be a power-of-two value.");
}
}
/**
* Calls {@link #nmalloc(int, int)} with {@code alignment} equal to {@link Pointer#POINTER_SIZE POINTER_SIZE}.
*
* @param size the allocation size
*
* @return the memory address on the stack for the requested allocation
*/
public long nmalloc(int size) {
return nmalloc(POINTER_SIZE, size);
}
/**
* Allocates a block of {@code size} bytes of memory on the stack. The content of the newly allocated block of memory is not initialized, remaining with
* indeterminate values.
*
* @param alignment the required alignment
* @param size the allocation size
*
* @return the memory address on the stack for the requested allocation
*/
public long nmalloc(int alignment, int size) {
// Align address to the specified alignment
long address = (this.address + pointer - size) & ~Integer.toUnsignedLong(alignment - 1);
pointer = (int)(address - this.address);
if (/*CHECKS*/ false && pointer < 0) {
throw new OutOfMemoryError("Out of stack space.");
}
return address;
}
/**
* Allocates a block of memory on the stack for an array of {@code num} elements, each of them {@code size} bytes long, and initializes all its bits to
* zero.
*
* @param alignment the required element alignment
* @param num num the number of elements to allocate
* @param size the size of each element
*
* @return the memory address on the stack for the requested allocation
*/
public long ncalloc(int alignment, int num, int size) {
int bytes = num * size;
long address = nmalloc(alignment, bytes);
memSet(address, 0, bytes);
return address;
}
// -------------------------------------------------
/** Unsafe version of {@link #shorts(short)}. */
public long nshort(short value) {
long a = nmalloc(2, 2);
memPutShort(a, value);
return a;
}
// -------------------------------------------------
/** Int version of {@link #malloc(int)}. */
public IntBuffer mallocInt(int size) { return wrapBufferInt(nmalloc(4, size << 2), size); }
/** Unsafe version of {@link #ints(int)}. */
public long nint(int value) {
long a = nmalloc(4, 4);
memPutInt(a, value);
return a;
}
// -------------------------------------------------
/** Unsafe version of {@link #longs(long)}. */
public long nlong(long value) {
long a = nmalloc(8, 8);
memPutLong(a, value);
return a;
}
// -------------------------------------------------
/** Float version of {@link #malloc(int)}. */
public FloatBuffer mallocFloat(int size) { return wrapBufferFloat(nmalloc(4, size << 2), size); }
/** Unsafe version of {@link #floats(float)}. */
public long nfloat(float value) {
long a = nmalloc(4, 4);
memPutFloat(a, value);
return a;
}
// -------------------------------------------------
/** Unsafe version of {@link #doubles(double)}. */
public long ndouble(double value) {
long a = nmalloc(8, 8);
memPutDouble(a, value);
return a;
}
// -------------------------------------------------
// -------------------------------------------------
// -------------------------------------------------
/** Returns the stack of the current thread. */
public static MemoryStack stackGet() {
return TLS.get();
}
/**
* Calls {@link #push} on the stack of the current thread.
*
* @return the stack of the current thread.
*/
public static MemoryStack stackPush() {
return stackGet().push();
}
/**
* Calls {@link #pop} on the stack of the current thread.
*
* @return the stack of the current thread.
*/
public static MemoryStack stackPop() {
return stackGet().pop();
}
/** Thread-local version of {@link #nmalloc(int)}. */
public static long nstackMalloc(int size) { return stackGet().nmalloc(size); }
/** Thread-local version of {@link #nmalloc(int, int)}. */
public static long nstackMalloc(int alignment, int size) { return stackGet().nmalloc(alignment, size); }
/** Thread-local version of {@link #ncalloc}. */
public static long nstackCalloc(int alignment, int num, int size) { return stackGet().ncalloc(alignment, num, size); }
// -------------------------------------------------
}
@@ -0,0 +1,93 @@
/*
* This class is backported from LWJGL3 under the BSD 3-clause "New" or "Revised" License
*/
package com.gtnewhorizons.angelica.compat.lwjgl;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.NULL;
import static com.gtnewhorizons.angelica.compat.lwjgl.CompatMemoryUtil.UNSAFE;
import static org.lwjgl.LWJGLUtil.CHECKS;
import net.minecraft.util.Util;
import org.jetbrains.annotations.Nullable;
/**
* Pointer interface.
*
* <p>LWJGL can run on both 32bit and 64bit architectures. Since LWJGL applications deal with native memory directly, this interface provides necessary
* information about the underlying architecture of the running JVM process.</p>
*
* <p>When interacting with native functions, pointer values are mapped to Java {@code long}. LWJGL automatically converts long values to the correct pointer
* addresses when used in native code. Native functions sometimes require arrays of pointer values; the {@link PointerBuffer} class may be used for that
* purpose. It has an API similar to a {@link java.nio.LongBuffer} but handles pointer casts automatically.</p>
*/
public interface Pointer {
/** The pointer size in bytes. Will be 4 on a 32bit JVM and 8 on a 64bit one. */
int POINTER_SIZE = UNSAFE.addressSize();
/** The pointer size power-of-two. Will be 2 on a 32bit JVM and 3 on a 64bit one. */
int POINTER_SHIFT = POINTER_SIZE == 8 ? 3 : 2;
/** The value of {@code sizeof(long)} for the current platform. */
int CLONG_SIZE = POINTER_SIZE == 8 && Util.getOSType() == Util.EnumOS.WINDOWS ? 4 : POINTER_SIZE;
/** The value of {@code sizeof(long)} as a power-of-two. */
int CLONG_SHIFT = CLONG_SIZE == 8 ? 3 : 2;
/** Will be true on a 32bit JVM. */
boolean BITS32 = POINTER_SIZE * 8 == 32;
/** Will be true on a 64bit JVM. */
boolean BITS64 = POINTER_SIZE * 8 == 64;
/**
* Returns the raw pointer address as a {@code long} value.
*
* @return the pointer address
*/
long address();
/** Default {@link Pointer} implementation. */
abstract class Default implements Pointer {
// Removed final due to JDK-8139758. TODO: Restore if the fix is backported to JDK 8.
protected long address;
protected Default(long address) {
if (CHECKS && address == NULL) {
throw new NullPointerException();
}
this.address = address;
}
@Override
public long address() {
return address;
}
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Pointer)) {
return false;
}
Pointer that = (Pointer)o;
return address == that.address();
}
public int hashCode() {
return (int)(address ^ (address >>> 32));
}
@Override
public String toString() {
return String.format("%s pointer [0x%X]", getClass().getSimpleName(), address);
}
}
}
@@ -15,7 +15,6 @@ public class ByteBufferBackedInputStream extends InputStream {
this.buf = buf;
}
@Override
public int read() throws IOException {
if (!buf.hasRemaining()) {
return -1;
@@ -40,7 +40,6 @@ public class ChunkPos {
return (long)x & 4294967295L | ((long)z & 4294967295L) << 32;
}
@Override
public int hashCode()
{
final int i = 1664525 * this.x + 1013904223;
@@ -48,7 +47,6 @@ public class ChunkPos {
return i ^ j;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
@@ -1,23 +1,10 @@
package com.gtnewhorizons.angelica.compat.mojang;
import com.gtnewhorizons.angelica.compat.ModStatus;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
public enum InteractionHand {
MAIN_HAND,
OFF_HAND;
InteractionHand() {
}
public ItemStack getItemInHand(EntityPlayer player){
if (ModStatus.isBackhandLoaded && this == InteractionHand.OFF_HAND){ // off hand (requires backhand)
return ModStatus.backhandCompat.getOffhandItem(player);
}
else { // main hand
return player.getHeldItem();
}
}
}
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.compat.mojang;
import com.gtnewhorizons.angelica.compat.lwjgl.MemoryStack;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
@@ -7,8 +8,6 @@ import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import javax.imageio.ImageIO;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities;
import lombok.Getter;
import net.coderbot.iris.Iris;
import org.lwjgl.opengl.GL11;
@@ -59,20 +58,15 @@ public class NativeImage extends BufferedImage {
// final int width = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, level, GL11.GL_TEXTURE_WIDTH);
// final int height = GL11.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, level, GL11.GL_TEXTURE_HEIGHT);
final IntBuffer buffer = MemoryUtilities.memAllocInt(size);
try {
try (final MemoryStack stack = MemoryStack.stackPush()) {
final IntBuffer buffer = stack.mallocInt(size);
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, level, format.glFormat, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
int[] data = new int[size];
buffer.get(data);
setRGB(0, 0, width, height, data, 0, width);
} finally {
MemoryUtilities.memFree(buffer);
}
}
public void writeToFile(File file) throws IOException{
try {
ImageIO.write(this, "png", file);
@@ -110,15 +104,18 @@ public class NativeImage extends BufferedImage {
public enum Format {
RGBA(4, GL11.GL_RGBA),
RGB(3, GL11.GL_RGB);
RGBA(4, GL11.GL_RGBA, BufferedImage.TYPE_INT_ARGB),
RGB(3, GL11.GL_RGB, BufferedImage.TYPE_INT_RGB);
private final int components;
private final int glFormat;
private final int imageType;
Format(int components, int glFormat) {
Format(int components, int glFormat, int imageType) {
this.components = components;
this.glFormat = glFormat;
this.imageType = imageType;
}
}
@@ -72,6 +72,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
private final MultiPhaseParameters phases;
private final int hash;
private final Optional<RenderLayer> affectedOutline;
private final boolean outline;
private MultiPhase(String name, VertexFormat vertexFormat, int drawMode, int expectedBufferSize, boolean hasCrumbling, boolean translucent, MultiPhaseParameters phases) {
super(name, vertexFormat, drawMode, expectedBufferSize, () -> {
@@ -83,6 +84,7 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
this.affectedOutline = phases.outlineMode == RenderLayer.OutlineMode.AFFECTS_OUTLINE ? phases.texture.getId().map((arg2) -> {
return getOutline(arg2, phases.cull);
}) : Optional.empty();
this.outline = phases.outlineMode == RenderLayer.OutlineMode.IS_OUTLINE;
this.hash = Objects.hash(new Object[]{super.hashCode(), phases});
}
@@ -95,23 +97,20 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
return this.affectedOutline;
}
@Override
public boolean equals(@Nullable Object object) {
return this == object;
}
@Override
public int hashCode() {
return this.hash;
}
@Override
public String toString() {
return "RenderType[" + this.phases + ']';
}
static {
CACHE = new ObjectOpenCustomHashSet<>(RenderLayer.MultiPhase.HashStrategy.INSTANCE);
CACHE = new ObjectOpenCustomHashSet(RenderLayer.MultiPhase.HashStrategy.INSTANCE);
}
static enum HashStrategy implements Hash.Strategy<MultiPhase> {
@@ -169,7 +168,6 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
this.phases = ImmutableList.of(this.texture, this.transparency, this.diffuseLighting, this.shadeModel, this.alpha, this.depthTest, this.cull, this.lightmap, this.fog, this.layering, this.target, this.texturing, this.writeMaskState);
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
@@ -181,12 +179,10 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
}
}
@Override
public int hashCode() {
return Objects.hash(new Object[]{this.phases, this.outlineMode});
}
@Override
public String toString() {
return "CompositeState[" + this.phases + ", outlineProperty=" + this.outlineMode + ']';
}
@@ -297,7 +293,6 @@ public abstract class RenderLayer extends RenderPhase { // Aka: RenderType (Iris
this.name = name;
}
@Override
public String toString() {
return this.name;
}
@@ -11,6 +11,7 @@ import org.lwjgl.opengl.GL11;
import javax.annotation.Nullable;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalDouble;
public abstract class RenderPhase {
@@ -97,7 +98,6 @@ public abstract class RenderPhase {
this.endAction.run();
}
@Override
public boolean equals(@Nullable Object object) {
if (this == object) {
return true;
@@ -109,12 +109,10 @@ public abstract class RenderPhase {
}
}
@Override
public int hashCode() {
return this.name.hashCode();
}
@Override
public String toString() {
return this.name;
}
@@ -38,7 +38,7 @@ public class AngelicaConfig {
public static boolean enableIris;
@Config.Comment("Enable MCPatcherForge features, still in Alpha. Individual features are toggled in mcpatcher.json")
@Config.DefaultBoolean(true)
@Config.DefaultBoolean(false)
@Config.RequiresMcRestart
public static boolean enableMCPatcherForgeFeatures;
@@ -133,11 +133,6 @@ public class AngelicaConfig {
@Config.RequiresMcRestart
public static boolean enableDebugLogging;
@Config.Comment("Enables PBR atlas dumping")
@Config.DefaultBoolean(false)
@Config.Name("Enable PBR Debug")
public static boolean enablePBRDebug;
@Config.Comment("Enable Zoom")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
@@ -148,22 +143,9 @@ public class AngelicaConfig {
@Config.RequiresMcRestart
public static boolean optimizeInWorldItemRendering;
@Config.Comment("Upper limit for the amount of VBO's to cache for optimized item rendering. Higher number can potentially use more VRAM.")
@Config.DefaultInt(512)
@Config.RangeInt(min = 256, max = 1024)
public static int itemRendererCacheSize;
@Config.Comment("Max amount of display lists to cache for optimized item rendering. Higher number will use more VRAM")
@Config.DefaultInt(256)
@Config.RangeInt(min = 64, max = 1024)
public static int itemRendererDisplayListCacheSize;
@Config.Comment("Render distance for the spinning mob inside mod spawners")
@Config.DefaultDouble(16D)
@Config.RangeDouble(min = 16D, max = 64D)
public static double mobSpawnerRenderDistance;
@Config.Comment("Switches to an alternate FPS limiter that gives more stable frametimes, in exchange for slightly " +
"more latency. Will never introduce more than one frame of latency, and has a lower impact at higher framerates.")
@Config.DefaultBoolean(false)
public static boolean sleepBeforeSwap;
@Config.Comment("Allows unicode languages to use an odd gui scale")
@Config.DefaultBoolean(true)
public static boolean removeUnicodeEvenScaling;
}
@@ -6,12 +6,6 @@ import net.minecraft.client.gui.GuiScreen;
public class AngelicaGuiConfig extends SimpleGuiConfig {
public AngelicaGuiConfig(GuiScreen parent) throws ConfigException {
super(parent,
"angelica",
"Angelica",
true,
AngelicaConfig.class,
FontConfig.class
);
super(parent, AngelicaConfig.class, "angelica", "Angelica");
}
}
@@ -39,9 +39,4 @@ public class CompatConfig {
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public static boolean fixImmersiveEngineering;
@Config.Comment("Apply fixes to Security Craft")
@Config.DefaultBoolean(true)
@Config.RequiresMcRestart
public static boolean fixSecurityCraft;
}
@@ -1,58 +0,0 @@
package com.gtnewhorizons.angelica.config;
import com.gtnewhorizon.gtnhlib.config.Config;
@Config(modid = "angelica", category = "customfont")
public class FontConfig {
@Config.Comment("Whether or not to use custom fonts when drawing text.")
@Config.DefaultBoolean(false)
public static boolean enableCustomFont;
@Config.Comment("Name of the primary custom font. Best not to set it from here.")
@Config.DefaultString("(none)")
public static String customFontNamePrimary;
@Config.Comment("Name of the fallback custom font. Best not to set it from here.")
@Config.DefaultString("(none)")
public static String customFontNameFallback;
@Config.Comment("The quality at which custom fonts are rendered, making them less pixelated but increasing memory usage.")
@Config.DefaultInt(30)
@Config.RangeInt(min = 6, max = 72)
public static int customFontQuality;
@Config.Comment("Controls the distance at which the font's shadow is drawn.")
@Config.DefaultFloat(1F)
@Config.RangeFloat(min = 0F, max = 2F)
public static float fontShadowOffset;
@Config.Comment("Influences the aspect ratio of each glyph.")
@Config.DefaultFloat(0F)
@Config.RangeFloat(min = -1F, max = 1F)
public static float glyphAspect;
@Config.Comment("Scale of each glyph, whitespace excluded.")
@Config.DefaultFloat(1F)
@Config.RangeFloat(min = 0.1F, max = 3F)
public static float glyphScale;
@Config.Comment("Whitespace scale.")
@Config.DefaultFloat(1F)
@Config.RangeFloat(min = 0.1F, max = 3F)
public static float whitespaceScale;
@Config.Comment("Adds extra spacing between glyphs.")
@Config.DefaultFloat(0F)
@Config.RangeFloat(min = -2F, max = 2F)
public static float glyphSpacing;
@Config.Comment("Controls font antialiasing. 0 = none, 1 = 4x MSAA, 2 = 16x MSAA.")
@Config.DefaultInt(2)
@Config.RangeInt(min = 0, max = 2)
public static int fontAAMode;
@Config.Comment("Affects font antialiasing sample spacing. Higher values increase blur.")
@Config.DefaultInt(7)
@Config.RangeInt(min = 1, max = 24)
public static int fontAAStrength;
}
@@ -28,10 +28,9 @@ import static org.lwjgl.opengl.GL20.glUniform1;
import static org.lwjgl.opengl.GL20.glUniform1f;
import static org.lwjgl.opengl.GL20.glUniform1i;
import static org.lwjgl.opengl.GL20.glVertexAttribPointer;
import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryStack.*;
import com.gtnewhorizon.gtnhlib.bytebuf.MemoryStack;
import com.gtnewhorizon.gtnhlib.client.renderer.shader.ShaderProgram;
import com.gtnewhorizons.angelica.compat.lwjgl.MemoryStack;
import java.nio.FloatBuffer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
@@ -132,7 +131,7 @@ public abstract class F3Graph {
// Load vertex buffer
vertBuf = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vertBuf);
try (final MemoryStack stack = stackPush()) {
try (final MemoryStack stack = MemoryStack.stackPush()) {
final FloatBuffer vertices = stack.mallocFloat(VERT_COUNT * VERT_FLOATS);
// Since we use a triangle strip, we only need 4 vertices. The quad extends to the top of the screen so spikes
// don't get truncated. The max height is replaced in the vert shader, no need to be precise.
@@ -6,18 +6,19 @@ import com.gtnewhorizon.gtnhlib.blockpos.IBlockPos;
import com.gtnewhorizon.gtnhlib.util.CoordinatePacker;
import com.gtnewhorizons.angelica.api.IDynamicLightProducer;
import com.gtnewhorizons.angelica.compat.ModStatus;
import com.gtnewhorizons.angelica.compat.battlegear2.Battlegear2Compat;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
import mods.battlegear2.api.core.IBattlePlayer;
import mods.battlegear2.api.core.IInventoryPlayerBattle;
import net.irisshaders.iris.api.v0.IrisApi;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.monster.EntityCreeper;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
@@ -27,6 +28,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.MathHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xonin.backhand.api.core.BackhandUtils;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -376,13 +378,13 @@ public class DynamicLights {
player instanceof IBattlePlayer battlePlayer &&
battlePlayer.battlegear2$isBattlemode()
) {
ItemStack offhand = Battlegear2Compat.getBattlegear2Offhand(player);
ItemStack offhand = ((IInventoryPlayerBattle) player.inventory).battlegear2$getCurrentOffhandWeapon();
if (offhand != null) {
luminance = Math.max(luminance, getLuminanceFromItemStack(offhand, inWater));
}
}
else if (ModStatus.isBackhandLoaded && living instanceof EntityPlayer player){
ItemStack offhand = ModStatus.backhandCompat.getOffhandItem(player);
ItemStack offhand = BackhandUtils.getOffhandItem(player);
if (offhand != null) {
luminance = Math.max(luminance, getLuminanceFromItemStack(offhand, inWater));
}
@@ -7,11 +7,9 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -21,42 +19,24 @@ public class Feature {
GL13.GL_MULTISAMPLE_BIT, GL11.GL_PIXEL_MODE_BIT, GL11.GL_POINT_BIT, GL11.GL_POLYGON_BIT, GL11.GL_POLYGON_STIPPLE_BIT, GL11.GL_SCISSOR_BIT,
GL11.GL_STENCIL_BUFFER_BIT, GL11.GL_TEXTURE_BIT, GL11.GL_TRANSFORM_BIT, GL11.GL_VIEWPORT_BIT };
static final Int2ObjectMap<List<IStateStack<?>>> maskToFeaturesMap = new Int2ObjectOpenHashMap<>();
static final Int2ObjectMap<Set<IStateStack<?>>> maskToFeaturesMap = new Int2ObjectOpenHashMap<>();
static List<IStateStack<?>> maskToFeatures(int mask) {
static Set<IStateStack<?>> maskToFeatures(int mask) {
if(maskToFeaturesMap.containsKey(mask)) {
return maskToFeaturesMap.get(mask);
}
final Set<IStateStack<?>> features = new HashSet<>();
for(int attrib : Feature.supportedAttribs) {
if((mask & attrib) == attrib) {
features.addAll(getFeatures(attrib));
}
}
final List<IStateStack<?>> asList = new ArrayList<>(features);
maskToFeaturesMap.put(mask, asList);
return asList;
maskToFeaturesMap.put(mask, features);
return features;
}
private static final Map<Integer, Set<IStateStack<?>>> attribToFeatures = new HashMap<>();
/**
* Helper method to add all texture gen states (S, T, R, Q) for all texture units to a collection.
* Used by both GL_ENABLE_BIT and GL_TEXTURE_BIT.
*/
private static void addTextureGenStates(Set<IStateStack<?>> collection) {
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
collection.add(GLStateManager.textures.getTexGenSStates(i));
collection.add(GLStateManager.textures.getTexGenTStates(i));
collection.add(GLStateManager.textures.getTexGenRStates(i));
collection.add(GLStateManager.textures.getTexGenQStates(i));
}
}
static {
attribToFeatures.put(GL11.GL_COLOR_BUFFER_BIT, ImmutableSet.of(
GLStateManager.alphaTest // GL_ALPHA_TEST enable bit
@@ -65,10 +45,10 @@ public class Feature {
, GLStateManager.blendState // Blending source and destination functions
// Constant blend color
// Blending equation
, GLStateManager.colorLogicOpState // GL_COLOR_LOGIC_OP enable bit
, GLStateManager.ditherState // GL_DITHER enable bit
, GLStateManager.drawBuffer // GL_DRAW_BUFFER setting
, GLStateManager.indexLogicOpState // GL_INDEX_LOGIC_OP enable bit
// GL_DITHER enable bit
// GL_DRAW_BUFFER setting
// GL_COLOR_LOGIC_OP enable bit
// GL_INDEX_LOGIC_OP enable bit
// Logic op function
, GLStateManager.colorMask // Color-mode and index-mode writemasks
, GLStateManager.clearColor // Color-mode and index-mode clear values
@@ -94,15 +74,14 @@ public class Feature {
final HashSet<IStateStack<?>> enableBits = new HashSet<>(ImmutableSet.of(
GLStateManager.alphaTest // GL_ALPHA_TEST flag
, GLStateManager.autoNormalState // GL_AUTO_NORMAL flag
// GL_AUTO_NORMAL flag
, GLStateManager.blendMode // GL_BLEND flag
, GLStateManager.colorLogicOpState // GL_COLOR_LOGIC_OP flag
// Enable bits for the user-definable clipping planes
, GLStateManager.colorMaterial // GL_COLOR_MATERIAL
, GLStateManager.cullState // GL_CULL_FACE flag
, GLStateManager.depthTest // GL_DEPTH_TEST flag
, GLStateManager.ditherState // GL_DITHER flag
// GL_DITHER flag
, GLStateManager.fogMode // GL_FOG flag
, GLStateManager.indexLogicOpState // GL_INDEX_LOGIC_OP flag
, GLStateManager.lightStates[0] // GL_LIGHT0
, GLStateManager.lightStates[1] // GL_LIGHT1
, GLStateManager.lightStates[2] // GL_LIGHT2
@@ -112,57 +91,36 @@ public class Feature {
, GLStateManager.lightStates[6] // GL_LIGHT6
, GLStateManager.lightStates[7] // GL_LIGHT7
, GLStateManager.lightingState // GL_LIGHTING flag
, GLStateManager.lineSmoothState // GL_LINE_SMOOTH flag
, GLStateManager.lineStippleState // GL_LINE_STIPPLE flag
, GLStateManager.map1Color4State // GL_MAP1_COLOR_4
, GLStateManager.map1IndexState // GL_MAP1_INDEX
, GLStateManager.map1NormalState // GL_MAP1_NORMAL
, GLStateManager.map1TextureCoord1State // GL_MAP1_TEXTURE_COORD_1
, GLStateManager.map1TextureCoord2State // GL_MAP1_TEXTURE_COORD_2
, GLStateManager.map1TextureCoord3State // GL_MAP1_TEXTURE_COORD_3
, GLStateManager.map1TextureCoord4State // GL_MAP1_TEXTURE_COORD_4
, GLStateManager.map1Vertex3State // GL_MAP1_VERTEX_3
, GLStateManager.map1Vertex4State // GL_MAP1_VERTEX_4
, GLStateManager.map2Color4State // GL_MAP2_COLOR_4
, GLStateManager.map2IndexState // GL_MAP2_INDEX
, GLStateManager.map2NormalState // GL_MAP2_NORMAL
, GLStateManager.map2TextureCoord1State // GL_MAP2_TEXTURE_COORD_1
, GLStateManager.map2TextureCoord2State // GL_MAP2_TEXTURE_COORD_2
, GLStateManager.map2TextureCoord3State // GL_MAP2_TEXTURE_COORD_3
, GLStateManager.map2TextureCoord4State // GL_MAP2_TEXTURE_COORD_4
, GLStateManager.map2Vertex3State // GL_MAP2_VERTEX_3
, GLStateManager.map2Vertex4State // GL_MAP2_VERTEX_4
, GLStateManager.multisampleState // GL_MULTISAMPLE flag
, GLStateManager.normalizeState // GL_NORMALIZE flag
, GLStateManager.pointSmoothState // GL_POINT_SMOOTH flag
, GLStateManager.polygonOffsetPointState // GL_POLYGON_OFFSET_POINT flag
, GLStateManager.polygonOffsetLineState // GL_POLYGON_OFFSET_LINE flag
, GLStateManager.polygonOffsetFillState // GL_POLYGON_OFFSET_FILL flag
, GLStateManager.polygonSmoothState // GL_POLYGON_SMOOTH flag
, GLStateManager.polygonStippleState // GL_POLYGON_STIPPLE flag
, GLStateManager.rescaleNormalState // GL_RESCALE_NORMAL flag
, GLStateManager.sampleAlphaToCoverageState // GL_SAMPLE_ALPHA_TO_COVERAGE flag
, GLStateManager.sampleAlphaToOneState // GL_SAMPLE_ALPHA_TO_ONE flag
, GLStateManager.sampleCoverageState // GL_SAMPLE_COVERAGE flag
// GL_LINE_SMOOTH flag
// GL_LINE_STIPPLE flag
// GL_INDEX_LOGIC_OP flag
// GL_COLOR_LOGIC_OP flag
// GL_MAP1_x where x is a map type
// GL_MAP2_x where x is a map type
// GL_MULTISAMPLE flag
// GL_NORMALIZE flag
// GL_POINT_SMOOTH flag
// GL_POLYGON_OFFSET_LINE flag
// GL_POLYGON_OFFSET_FILL flag
// GL_POLYGON_OFFSET_POINT flag
// GL_POLYGON_SMOOTH flag
// GL_POLYGON_STIPPLE flag
// GL_SAMPLE_ALPHA_TO_COVERAGE flag
// GL_SAMPLE_ALPHA_TO_ONE flag
// GL_SAMPLE_COVERAGE flag
, GLStateManager.scissorTest // GL_SCISSOR_TEST flag
, GLStateManager.stencilTest // GL_STENCIL_TEST flag
// GL_STENCIL_TEST flag
// GL_TEXTURE_1D flag
// GL_TEXTURE_2D flag - Below
// GL_TEXTURE_3D flag
// Flags GL_TEXTURE_GEN_x where x is S, T, R, or Q
));
// Enable bits for the user-definable clipping planes
for(int i = 0; i < GLStateManager.clipPlaneStates.length; i++) {
enableBits.add(GLStateManager.clipPlaneStates[i]);
}
// GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D flags
// GL_TEXTURE_2D flag
for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
enableBits.add(GLStateManager.textures.getTexture1DStates(i));
enableBits.add(GLStateManager.textures.getTextureUnitStates(i));
enableBits.add(GLStateManager.textures.getTexture3DStates(i));
}
// Flags GL_TEXTURE_GEN_x where x is S, T, R, or Q
addTextureGenStates(enableBits);
attribToFeatures.put(GL11.GL_ENABLE_BIT, enableBits);
attribToFeatures.put(GL11.GL_EVAL_BIT, ImmutableSet.of(
// GL_MAP1_x enable bits, where x is a map type
@@ -304,16 +262,13 @@ public class Feature {
// textureAttribs.add(GLStateManager.textures.getInfo(i))
}
// Enable bits GL_TEXTURE_GEN_x where x is S, T, R, or Q
addTextureGenStates(textureAttribs);
attribToFeatures.put(GL11.GL_TEXTURE_BIT, textureAttribs);
attribToFeatures.put(GL11.GL_TRANSFORM_BIT, ImmutableSet.of(
// Coefficients of the six clipping planes
// Enable bits for the user-definable clipping planes
GLStateManager.matrixMode
, GLStateManager.normalizeState // GL_NORMALIZE flag
// GL_NORMALIZE flag
, GLStateManager.rescaleNormalState // GL_RESCALE_NORMAL flag
));
attribToFeatures.put(GL11.GL_VIEWPORT_BIT, ImmutableSet.of(
@@ -26,7 +26,6 @@ import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
import com.gtnewhorizons.angelica.glsm.texture.TextureTracker;
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import cpw.mods.fml.relauncher.ReflectionHelper;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
@@ -36,7 +35,6 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import it.unimi.dsi.fastutil.objects.ObjectArraySet;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gbuffer_overrides.state.StateTracker;
import net.coderbot.iris.gl.blending.AlphaTestStorage;
@@ -67,37 +65,17 @@ import org.lwjgl.opengl.GLContext;
import org.lwjgl.opengl.KHRDebug;
import java.lang.Math;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.IntSupplier;
import static com.gtnewhorizons.angelica.glsm.Vendor.AMD;
import static com.gtnewhorizons.angelica.glsm.Vendor.INTEL;
import static com.gtnewhorizons.angelica.glsm.Vendor.MESA;
import static com.gtnewhorizons.angelica.glsm.Vendor.NVIDIA;
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
/**
* OpenGL State Manager - Provides cached state tracking and management for OpenGL operations.
*
* <p><b>IMPORTANT INITIALIZATION ORDER:</b></p>
* <ul>
* <li>This class performs GL queries in static initializers (MAX_CLIP_PLANES, MAX_TEXTURE_UNITS, etc.)</li>
* <li>The class MUST NOT be loaded until after the GL context has been created and made current</li>
* <li>Violating this requirement will cause crashes with "No current context" or return invalid values</li>
* <li>Call {@link #preInit()} after GL context creation to initialize runtime state</li>
* </ul>
*/
@SuppressWarnings("unused") // Used in ASM
public class GLStateManager {
public static ContextCapabilities capabilities;
@@ -109,14 +87,13 @@ public class GLStateManager {
public static final int MAX_PROJECTION_STACK_DEPTH = GL11.glGetInteger(GL11.GL_MAX_PROJECTION_STACK_DEPTH);
public static final int MAX_TEXTURE_STACK_DEPTH = GL11.glGetInteger(GL11.GL_MAX_TEXTURE_STACK_DEPTH);
public static final int MAX_TEXTURE_UNITS = GL11.glGetInteger(GL20.GL_MAX_TEXTURE_IMAGE_UNITS);
public static final int MAX_CLIP_PLANES = GL11.glGetInteger(GL11.GL_MAX_CLIP_PLANES);
public static final GLFeatureSet HAS_MULTIPLE_SET = new GLFeatureSet();
@Getter private static Vendor VENDOR;
// This setting varies depending on driver, so it gets queried at runtime
public static final int DEFAULT_DRAW_BUFFER = GL11.glGetInteger(GL11.GL_DRAW_BUFFER);
@Getter protected static boolean NVIDIA;
@Getter protected static boolean AMD;
@Getter protected static boolean INTEL;
@Getter protected static boolean MESA;
// GLStateManager State Trackers
private static final IntStack attribs = new IntArrayList(MAX_ATTRIB_STACK_DEPTH);
@@ -135,58 +112,12 @@ public class GLStateManager {
@Getter protected static final Color4Stack color = new Color4Stack();
@Getter protected static final Color4Stack clearColor = new Color4Stack(new Color4(0.0F, 0.0F, 0.0F, 0.0F));
@Getter protected static final ColorMaskStack colorMask = new ColorMaskStack();
@Getter protected static final IntegerStateStack drawBuffer = new IntegerStateStack(DEFAULT_DRAW_BUFFER);
@Getter protected static final BooleanStateStack cullState = new BooleanStateStack(GL11.GL_CULL_FACE);
@Getter protected static final AlphaStateStack alphaState = new AlphaStateStack();
@Getter protected static final BooleanStateStack alphaTest = new BooleanStateStack(GL11.GL_ALPHA_TEST);
@Getter protected static final BooleanStateStack lightingState = new BooleanStateStack(GL11.GL_LIGHTING);
@Getter protected static final BooleanStateStack rescaleNormalState = new BooleanStateStack(GL12.GL_RESCALE_NORMAL);
@Getter protected static final BooleanStateStack normalizeState = new BooleanStateStack(GL11.GL_NORMALIZE);
// Additional enable bit states tracked by GL_ENABLE_BIT
@Getter protected static final BooleanStateStack ditherState = new BooleanStateStack(GL11.GL_DITHER, true); // Defaults to true per OpenGL spec
@Getter protected static final BooleanStateStack stencilTest = new BooleanStateStack(GL11.GL_STENCIL_TEST);
@Getter protected static final BooleanStateStack lineSmoothState = new BooleanStateStack(GL11.GL_LINE_SMOOTH);
@Getter protected static final BooleanStateStack lineStippleState = new BooleanStateStack(GL11.GL_LINE_STIPPLE);
@Getter protected static final BooleanStateStack pointSmoothState = new BooleanStateStack(GL11.GL_POINT_SMOOTH);
@Getter protected static final BooleanStateStack polygonSmoothState = new BooleanStateStack(GL11.GL_POLYGON_SMOOTH);
@Getter protected static final BooleanStateStack polygonStippleState = new BooleanStateStack(GL11.GL_POLYGON_STIPPLE);
@Getter protected static final BooleanStateStack multisampleState = new BooleanStateStack(GL13.GL_MULTISAMPLE, true); // Defaults to true per OpenGL spec
@Getter protected static final BooleanStateStack sampleAlphaToCoverageState = new BooleanStateStack(GL13.GL_SAMPLE_ALPHA_TO_COVERAGE);
@Getter protected static final BooleanStateStack sampleAlphaToOneState = new BooleanStateStack(GL13.GL_SAMPLE_ALPHA_TO_ONE);
@Getter protected static final BooleanStateStack sampleCoverageState = new BooleanStateStack(GL13.GL_SAMPLE_COVERAGE);
@Getter protected static final BooleanStateStack colorLogicOpState = new BooleanStateStack(GL11.GL_COLOR_LOGIC_OP);
@Getter protected static final BooleanStateStack indexLogicOpState = new BooleanStateStack(GL11.GL_INDEX_LOGIC_OP);
// Polygon offset states
@Getter protected static final BooleanStateStack polygonOffsetPointState = new BooleanStateStack(GL11.GL_POLYGON_OFFSET_POINT);
@Getter protected static final BooleanStateStack polygonOffsetLineState = new BooleanStateStack(GL11.GL_POLYGON_OFFSET_LINE);
@Getter protected static final BooleanStateStack polygonOffsetFillState = new BooleanStateStack(GL11.GL_POLYGON_OFFSET_FILL);
// Evaluator states
@Getter protected static final BooleanStateStack autoNormalState = new BooleanStateStack(GL11.GL_AUTO_NORMAL);
@Getter protected static final BooleanStateStack map1Color4State = new BooleanStateStack(GL11.GL_MAP1_COLOR_4);
@Getter protected static final BooleanStateStack map1IndexState = new BooleanStateStack(GL11.GL_MAP1_INDEX);
@Getter protected static final BooleanStateStack map1NormalState = new BooleanStateStack(GL11.GL_MAP1_NORMAL);
@Getter protected static final BooleanStateStack map1TextureCoord1State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_1);
@Getter protected static final BooleanStateStack map1TextureCoord2State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_2);
@Getter protected static final BooleanStateStack map1TextureCoord3State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_3);
@Getter protected static final BooleanStateStack map1TextureCoord4State = new BooleanStateStack(GL11.GL_MAP1_TEXTURE_COORD_4);
@Getter protected static final BooleanStateStack map1Vertex3State = new BooleanStateStack(GL11.GL_MAP1_VERTEX_3);
@Getter protected static final BooleanStateStack map1Vertex4State = new BooleanStateStack(GL11.GL_MAP1_VERTEX_4);
@Getter protected static final BooleanStateStack map2Color4State = new BooleanStateStack(GL11.GL_MAP2_COLOR_4);
@Getter protected static final BooleanStateStack map2IndexState = new BooleanStateStack(GL11.GL_MAP2_INDEX);
@Getter protected static final BooleanStateStack map2NormalState = new BooleanStateStack(GL11.GL_MAP2_NORMAL);
@Getter protected static final BooleanStateStack map2TextureCoord1State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_1);
@Getter protected static final BooleanStateStack map2TextureCoord2State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_2);
@Getter protected static final BooleanStateStack map2TextureCoord3State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_3);
@Getter protected static final BooleanStateStack map2TextureCoord4State = new BooleanStateStack(GL11.GL_MAP2_TEXTURE_COORD_4);
@Getter protected static final BooleanStateStack map2Vertex3State = new BooleanStateStack(GL11.GL_MAP2_VERTEX_3);
@Getter protected static final BooleanStateStack map2Vertex4State = new BooleanStateStack(GL11.GL_MAP2_VERTEX_4);
// Clip plane states
@Getter protected static final BooleanStateStack[] clipPlaneStates = new BooleanStateStack[MAX_CLIP_PLANES];
@Getter protected static final MatrixModeStack matrixMode = new MatrixModeStack();
@Getter protected static final Matrix4fStack modelViewMatrix = new Matrix4fStack(MAX_MODELVIEW_STACK_DEPTH);
@@ -202,25 +133,11 @@ public class GLStateManager {
@Getter protected static final MaterialStateStack frontMaterial = new MaterialStateStack(GL11.GL_FRONT);
@Getter protected static final MaterialStateStack backMaterial = new MaterialStateStack(GL11.GL_BACK);
private static final MethodHandle MAT4_STACK_CURR_DEPTH;
static {
for (int i = 0; i < lightStates.length; i ++) {
lightStates[i] = new BooleanStateStack(GL11.GL_LIGHT0 + i);
lightDataStates[i] = new LightStateStack(GL11.GL_LIGHT0 + i);
}
for (int i = 0; i < MAX_CLIP_PLANES; i++) {
clipPlaneStates[i] = new BooleanStateStack(GL11.GL_CLIP_PLANE0 + i);
}
try {
final Field curr = ReflectionHelper.findField(Matrix4fStack.class, "curr");
curr.setAccessible(true);
MAT4_STACK_CURR_DEPTH = MethodHandles.lookup().unreflectGetter(curr);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
@Getter protected static final ViewPortStateStack viewportState = new ViewPortStateStack();
@@ -232,18 +149,11 @@ public class GLStateManager {
while(!attribs.isEmpty()) {
attribs.popInt();
}
final List<IStateStack<?>> stacks = Feature.maskToFeatures(GL11.GL_ALL_ATTRIB_BITS);
final int size = stacks.size();
for(int i = 0; i < size; i++) {
final IStateStack<?> stack = stacks.get(i);
for(IStateStack<?> stack : Feature.maskToFeatures(GL11.GL_ALL_ATTRIB_BITS)) {
while(!stack.isEmpty()) {
stack.pop();
}
}
modelViewMatrix.clear();
projectionMatrix.clear();
}
@@ -270,9 +180,6 @@ public class GLStateManager {
public static class GLFeatureSet extends IntOpenHashSet {
private static final long serialVersionUID = 8558779940775721010L;
public GLFeatureSet addFeature(int feature) {
super.add(feature);
return this;
@@ -311,12 +218,16 @@ public class GLStateManager {
.addFeature(GL11.GL_VIEWPORT);
String glVendor = GL11.glGetString(GL11.GL_VENDOR);
VENDOR = Vendor.getVendor(glVendor.toLowerCase());
NVIDIA = glVendor.toLowerCase().contains("nvidia");
AMD = glVendor.toLowerCase().contains("ati") || glVendor.toLowerCase().contains("amd");
INTEL = glVendor.toLowerCase().contains("intel");
MESA = glVendor.toLowerCase().contains("mesa");
if (vendorIsAMD()) {
if(AMD) {
// AMD Drivers seem to default to 0 for the matrix mode, so we need to set it to the default
GL11.glMatrixMode(GL11.GL_MODELVIEW);
}
}
public static void init() {
@@ -362,23 +273,12 @@ public class GLStateManager {
// LWJGL Overrides
public static void glEnable(int cap) {
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
clipPlaneStates[cap - GL11.GL_CLIP_PLANE0].enable();
return;
}
switch (cap) {
case GL11.GL_ALPHA_TEST -> enableAlphaTest();
case GL11.GL_AUTO_NORMAL -> autoNormalState.enable();
case GL11.GL_BLEND -> enableBlend();
case GL11.GL_COLOR_MATERIAL -> enableColorMaterial();
case GL11.GL_COLOR_LOGIC_OP -> colorLogicOpState.enable();
case GL11.GL_CULL_FACE -> enableCull();
case GL11.GL_DEPTH_TEST -> enableDepthTest();
case GL11.GL_DITHER -> ditherState.enable();
case GL11.GL_FOG -> enableFog();
case GL11.GL_INDEX_LOGIC_OP -> indexLogicOpState.enable();
case GL11.GL_LIGHTING -> enableLighting();
case GL11.GL_LIGHT0 -> enableLight(0);
case GL11.GL_LIGHT1 -> enableLight(1);
@@ -388,69 +288,21 @@ public class GLStateManager {
case GL11.GL_LIGHT5 -> enableLight(5);
case GL11.GL_LIGHT6 -> enableLight(6);
case GL11.GL_LIGHT7 -> enableLight(7);
case GL11.GL_LINE_SMOOTH -> lineSmoothState.enable();
case GL11.GL_LINE_STIPPLE -> lineStippleState.enable();
case GL11.GL_MAP1_COLOR_4 -> map1Color4State.enable();
case GL11.GL_MAP1_INDEX -> map1IndexState.enable();
case GL11.GL_MAP1_NORMAL -> map1NormalState.enable();
case GL11.GL_MAP1_TEXTURE_COORD_1 -> map1TextureCoord1State.enable();
case GL11.GL_MAP1_TEXTURE_COORD_2 -> map1TextureCoord2State.enable();
case GL11.GL_MAP1_TEXTURE_COORD_3 -> map1TextureCoord3State.enable();
case GL11.GL_MAP1_TEXTURE_COORD_4 -> map1TextureCoord4State.enable();
case GL11.GL_MAP1_VERTEX_3 -> map1Vertex3State.enable();
case GL11.GL_MAP1_VERTEX_4 -> map1Vertex4State.enable();
case GL11.GL_MAP2_COLOR_4 -> map2Color4State.enable();
case GL11.GL_MAP2_INDEX -> map2IndexState.enable();
case GL11.GL_MAP2_NORMAL -> map2NormalState.enable();
case GL11.GL_MAP2_TEXTURE_COORD_1 -> map2TextureCoord1State.enable();
case GL11.GL_MAP2_TEXTURE_COORD_2 -> map2TextureCoord2State.enable();
case GL11.GL_MAP2_TEXTURE_COORD_3 -> map2TextureCoord3State.enable();
case GL11.GL_MAP2_TEXTURE_COORD_4 -> map2TextureCoord4State.enable();
case GL11.GL_MAP2_VERTEX_3 -> map2Vertex3State.enable();
case GL11.GL_MAP2_VERTEX_4 -> map2Vertex4State.enable();
case GL13.GL_MULTISAMPLE -> multisampleState.enable();
case GL11.GL_NORMALIZE -> normalizeState.enable();
case GL11.GL_POINT_SMOOTH -> pointSmoothState.enable();
case GL11.GL_POLYGON_OFFSET_POINT -> polygonOffsetPointState.enable();
case GL11.GL_POLYGON_OFFSET_LINE -> polygonOffsetLineState.enable();
case GL11.GL_POLYGON_OFFSET_FILL -> polygonOffsetFillState.enable();
case GL11.GL_POLYGON_SMOOTH -> polygonSmoothState.enable();
case GL11.GL_POLYGON_STIPPLE -> polygonStippleState.enable();
case GL12.GL_RESCALE_NORMAL -> enableRescaleNormal();
case GL13.GL_SAMPLE_ALPHA_TO_COVERAGE -> sampleAlphaToCoverageState.enable();
case GL13.GL_SAMPLE_ALPHA_TO_ONE -> sampleAlphaToOneState.enable();
case GL13.GL_SAMPLE_COVERAGE -> sampleCoverageState.enable();
case GL11.GL_COLOR_MATERIAL -> enableColorMaterial();
case GL11.GL_SCISSOR_TEST -> enableScissorTest();
case GL11.GL_STENCIL_TEST -> stencilTest.enable();
case GL11.GL_TEXTURE_1D -> textures.getTexture1DStates(activeTextureUnit.getValue()).enable();
case GL11.GL_TEXTURE_2D -> enableTexture();
case GL12.GL_TEXTURE_3D -> textures.getTexture3DStates(activeTextureUnit.getValue()).enable();
case GL11.GL_TEXTURE_GEN_S -> textures.getTexGenSStates(activeTextureUnit.getValue()).enable();
case GL11.GL_TEXTURE_GEN_T -> textures.getTexGenTStates(activeTextureUnit.getValue()).enable();
case GL11.GL_TEXTURE_GEN_R -> textures.getTexGenRStates(activeTextureUnit.getValue()).enable();
case GL11.GL_TEXTURE_GEN_Q -> textures.getTexGenQStates(activeTextureUnit.getValue()).enable();
case GL12.GL_RESCALE_NORMAL -> enableRescaleNormal();
default -> GL11.glEnable(cap);
}
}
public static void glDisable(int cap) {
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
clipPlaneStates[cap - GL11.GL_CLIP_PLANE0].disable();
return;
}
switch (cap) {
case GL11.GL_ALPHA_TEST -> disableAlphaTest();
case GL11.GL_AUTO_NORMAL -> autoNormalState.disable();
case GL11.GL_BLEND -> disableBlend();
case GL11.GL_COLOR_MATERIAL -> disableColorMaterial();
case GL11.GL_COLOR_LOGIC_OP -> colorLogicOpState.disable();
case GL11.GL_CULL_FACE -> disableCull();
case GL11.GL_DEPTH_TEST -> disableDepthTest();
case GL11.GL_DITHER -> ditherState.disable();
case GL11.GL_FOG -> disableFog();
case GL11.GL_INDEX_LOGIC_OP -> indexLogicOpState.disable();
case GL11.GL_LIGHTING -> disableLighting();
case GL11.GL_LIGHT0 -> disableLight(0);
case GL11.GL_LIGHT1 -> disableLight(1);
@@ -460,47 +312,10 @@ public class GLStateManager {
case GL11.GL_LIGHT5 -> disableLight(5);
case GL11.GL_LIGHT6 -> disableLight(6);
case GL11.GL_LIGHT7 -> disableLight(7);
case GL11.GL_LINE_SMOOTH -> lineSmoothState.disable();
case GL11.GL_LINE_STIPPLE -> lineStippleState.disable();
case GL11.GL_MAP1_COLOR_4 -> map1Color4State.disable();
case GL11.GL_MAP1_INDEX -> map1IndexState.disable();
case GL11.GL_MAP1_NORMAL -> map1NormalState.disable();
case GL11.GL_MAP1_TEXTURE_COORD_1 -> map1TextureCoord1State.disable();
case GL11.GL_MAP1_TEXTURE_COORD_2 -> map1TextureCoord2State.disable();
case GL11.GL_MAP1_TEXTURE_COORD_3 -> map1TextureCoord3State.disable();
case GL11.GL_MAP1_TEXTURE_COORD_4 -> map1TextureCoord4State.disable();
case GL11.GL_MAP1_VERTEX_3 -> map1Vertex3State.disable();
case GL11.GL_MAP1_VERTEX_4 -> map1Vertex4State.disable();
case GL11.GL_MAP2_COLOR_4 -> map2Color4State.disable();
case GL11.GL_MAP2_INDEX -> map2IndexState.disable();
case GL11.GL_MAP2_NORMAL -> map2NormalState.disable();
case GL11.GL_MAP2_TEXTURE_COORD_1 -> map2TextureCoord1State.disable();
case GL11.GL_MAP2_TEXTURE_COORD_2 -> map2TextureCoord2State.disable();
case GL11.GL_MAP2_TEXTURE_COORD_3 -> map2TextureCoord3State.disable();
case GL11.GL_MAP2_TEXTURE_COORD_4 -> map2TextureCoord4State.disable();
case GL11.GL_MAP2_VERTEX_3 -> map2Vertex3State.disable();
case GL11.GL_MAP2_VERTEX_4 -> map2Vertex4State.disable();
case GL13.GL_MULTISAMPLE -> multisampleState.disable();
case GL11.GL_NORMALIZE -> normalizeState.disable();
case GL11.GL_POINT_SMOOTH -> pointSmoothState.disable();
case GL11.GL_POLYGON_OFFSET_POINT -> polygonOffsetPointState.disable();
case GL11.GL_POLYGON_OFFSET_LINE -> polygonOffsetLineState.disable();
case GL11.GL_POLYGON_OFFSET_FILL -> polygonOffsetFillState.disable();
case GL11.GL_POLYGON_SMOOTH -> polygonSmoothState.disable();
case GL11.GL_POLYGON_STIPPLE -> polygonStippleState.disable();
case GL12.GL_RESCALE_NORMAL -> disableRescaleNormal();
case GL13.GL_SAMPLE_ALPHA_TO_COVERAGE -> sampleAlphaToCoverageState.disable();
case GL13.GL_SAMPLE_ALPHA_TO_ONE -> sampleAlphaToOneState.disable();
case GL13.GL_SAMPLE_COVERAGE -> sampleCoverageState.disable();
case GL11.GL_COLOR_MATERIAL -> disableColorMaterial();
case GL11.GL_SCISSOR_TEST -> disableScissorTest();
case GL11.GL_STENCIL_TEST -> stencilTest.disable();
case GL11.GL_TEXTURE_1D -> textures.getTexture1DStates(activeTextureUnit.getValue()).disable();
case GL11.GL_TEXTURE_2D -> disableTexture();
case GL12.GL_TEXTURE_3D -> textures.getTexture3DStates(activeTextureUnit.getValue()).disable();
case GL11.GL_TEXTURE_GEN_S -> textures.getTexGenSStates(activeTextureUnit.getValue()).disable();
case GL11.GL_TEXTURE_GEN_T -> textures.getTexGenTStates(activeTextureUnit.getValue()).disable();
case GL11.GL_TEXTURE_GEN_R -> textures.getTexGenRStates(activeTextureUnit.getValue()).disable();
case GL11.GL_TEXTURE_GEN_Q -> textures.getTexGenQStates(activeTextureUnit.getValue()).disable();
case GL12.GL_RESCALE_NORMAL -> disableRescaleNormal();
default -> GL11.glDisable(cap);
}
}
@@ -509,22 +324,12 @@ public class GLStateManager {
if(shouldBypassCache()) {
return GL11.glIsEnabled(cap);
}
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
if (cap >= GL11.GL_CLIP_PLANE0 && cap < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
return clipPlaneStates[cap - GL11.GL_CLIP_PLANE0].isEnabled();
}
return switch (cap) {
case GL11.GL_ALPHA_TEST -> alphaTest.isEnabled();
case GL11.GL_AUTO_NORMAL -> autoNormalState.isEnabled();
case GL11.GL_BLEND -> blendMode.isEnabled();
case GL11.GL_COLOR_MATERIAL -> colorMaterial.isEnabled();
case GL11.GL_COLOR_LOGIC_OP -> colorLogicOpState.isEnabled();
case GL11.GL_CULL_FACE -> cullState.isEnabled();
case GL11.GL_DEPTH_TEST -> depthTest.isEnabled();
case GL11.GL_DITHER -> ditherState.isEnabled();
case GL11.GL_FOG -> fogMode.isEnabled();
case GL11.GL_INDEX_LOGIC_OP -> indexLogicOpState.isEnabled();
case GL11.GL_LIGHTING -> lightingState.isEnabled();
case GL11.GL_LIGHT0 -> lightStates[0].isEnabled();
case GL11.GL_LIGHT1 -> lightStates[1].isEnabled();
@@ -534,47 +339,10 @@ public class GLStateManager {
case GL11.GL_LIGHT5 -> lightStates[5].isEnabled();
case GL11.GL_LIGHT6 -> lightStates[6].isEnabled();
case GL11.GL_LIGHT7 -> lightStates[7].isEnabled();
case GL11.GL_LINE_SMOOTH -> lineSmoothState.isEnabled();
case GL11.GL_LINE_STIPPLE -> lineStippleState.isEnabled();
case GL11.GL_MAP1_COLOR_4 -> map1Color4State.isEnabled();
case GL11.GL_MAP1_INDEX -> map1IndexState.isEnabled();
case GL11.GL_MAP1_NORMAL -> map1NormalState.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_1 -> map1TextureCoord1State.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_2 -> map1TextureCoord2State.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_3 -> map1TextureCoord3State.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_4 -> map1TextureCoord4State.isEnabled();
case GL11.GL_MAP1_VERTEX_3 -> map1Vertex3State.isEnabled();
case GL11.GL_MAP1_VERTEX_4 -> map1Vertex4State.isEnabled();
case GL11.GL_MAP2_COLOR_4 -> map2Color4State.isEnabled();
case GL11.GL_MAP2_INDEX -> map2IndexState.isEnabled();
case GL11.GL_MAP2_NORMAL -> map2NormalState.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_1 -> map2TextureCoord1State.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_2 -> map2TextureCoord2State.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_3 -> map2TextureCoord3State.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_4 -> map2TextureCoord4State.isEnabled();
case GL11.GL_MAP2_VERTEX_3 -> map2Vertex3State.isEnabled();
case GL11.GL_MAP2_VERTEX_4 -> map2Vertex4State.isEnabled();
case GL13.GL_MULTISAMPLE -> multisampleState.isEnabled();
case GL11.GL_NORMALIZE -> normalizeState.isEnabled();
case GL11.GL_POINT_SMOOTH -> pointSmoothState.isEnabled();
case GL11.GL_POLYGON_OFFSET_POINT -> polygonOffsetPointState.isEnabled();
case GL11.GL_POLYGON_OFFSET_LINE -> polygonOffsetLineState.isEnabled();
case GL11.GL_POLYGON_OFFSET_FILL -> polygonOffsetFillState.isEnabled();
case GL11.GL_POLYGON_SMOOTH -> polygonSmoothState.isEnabled();
case GL11.GL_POLYGON_STIPPLE -> polygonStippleState.isEnabled();
case GL12.GL_RESCALE_NORMAL -> rescaleNormalState.isEnabled();
case GL13.GL_SAMPLE_ALPHA_TO_COVERAGE -> sampleAlphaToCoverageState.isEnabled();
case GL13.GL_SAMPLE_ALPHA_TO_ONE -> sampleAlphaToOneState.isEnabled();
case GL13.GL_SAMPLE_COVERAGE -> sampleCoverageState.isEnabled();
case GL11.GL_COLOR_MATERIAL -> colorMaterial.isEnabled();
case GL11.GL_SCISSOR_TEST -> scissorTest.isEnabled();
case GL11.GL_STENCIL_TEST -> stencilTest.isEnabled();
case GL11.GL_TEXTURE_1D -> textures.getTexture1DStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_2D -> textures.getTextureUnitStates(activeTextureUnit.getValue()).isEnabled();
case GL12.GL_TEXTURE_3D -> textures.getTexture3DStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_S -> textures.getTexGenSStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_T -> textures.getTexGenTStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_R -> textures.getTexGenRStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_Q -> textures.getTexGenQStates(activeTextureUnit.getValue()).isEnabled();
case GL12.GL_RESCALE_NORMAL -> rescaleNormalState.isEnabled();
default -> GL11.glIsEnabled(cap);
};
}
@@ -583,23 +351,13 @@ public class GLStateManager {
if(shouldBypassCache()) {
return GL11.glGetBoolean(pname);
}
// Handle clip planes dynamically (supports up to MAX_CLIP_PLANES)
if (pname >= GL11.GL_CLIP_PLANE0 && pname < GL11.GL_CLIP_PLANE0 + MAX_CLIP_PLANES) {
return clipPlaneStates[pname - GL11.GL_CLIP_PLANE0].isEnabled();
}
return switch (pname) {
case GL11.GL_ALPHA_TEST -> alphaTest.isEnabled();
case GL11.GL_AUTO_NORMAL -> autoNormalState.isEnabled();
case GL11.GL_BLEND -> blendMode.isEnabled();
case GL11.GL_COLOR_MATERIAL -> colorMaterial.isEnabled();
case GL11.GL_COLOR_LOGIC_OP -> colorLogicOpState.isEnabled();
case GL11.GL_CULL_FACE -> cullState.isEnabled();
case GL11.GL_DEPTH_TEST -> depthTest.isEnabled();
case GL11.GL_DEPTH_WRITEMASK -> depthState.isEnabled();
case GL11.GL_DITHER -> ditherState.isEnabled();
case GL11.GL_FOG -> fogMode.isEnabled();
case GL11.GL_INDEX_LOGIC_OP -> indexLogicOpState.isEnabled();
case GL11.GL_LIGHTING -> lightingState.isEnabled();
case GL11.GL_LIGHT0 -> lightStates[0].isEnabled();
case GL11.GL_LIGHT1 -> lightStates[1].isEnabled();
@@ -609,47 +367,10 @@ public class GLStateManager {
case GL11.GL_LIGHT5 -> lightStates[5].isEnabled();
case GL11.GL_LIGHT6 -> lightStates[6].isEnabled();
case GL11.GL_LIGHT7 -> lightStates[7].isEnabled();
case GL11.GL_LINE_SMOOTH -> lineSmoothState.isEnabled();
case GL11.GL_LINE_STIPPLE -> lineStippleState.isEnabled();
case GL11.GL_MAP1_COLOR_4 -> map1Color4State.isEnabled();
case GL11.GL_MAP1_INDEX -> map1IndexState.isEnabled();
case GL11.GL_MAP1_NORMAL -> map1NormalState.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_1 -> map1TextureCoord1State.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_2 -> map1TextureCoord2State.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_3 -> map1TextureCoord3State.isEnabled();
case GL11.GL_MAP1_TEXTURE_COORD_4 -> map1TextureCoord4State.isEnabled();
case GL11.GL_MAP1_VERTEX_3 -> map1Vertex3State.isEnabled();
case GL11.GL_MAP1_VERTEX_4 -> map1Vertex4State.isEnabled();
case GL11.GL_MAP2_COLOR_4 -> map2Color4State.isEnabled();
case GL11.GL_MAP2_INDEX -> map2IndexState.isEnabled();
case GL11.GL_MAP2_NORMAL -> map2NormalState.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_1 -> map2TextureCoord1State.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_2 -> map2TextureCoord2State.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_3 -> map2TextureCoord3State.isEnabled();
case GL11.GL_MAP2_TEXTURE_COORD_4 -> map2TextureCoord4State.isEnabled();
case GL11.GL_MAP2_VERTEX_3 -> map2Vertex3State.isEnabled();
case GL11.GL_MAP2_VERTEX_4 -> map2Vertex4State.isEnabled();
case GL13.GL_MULTISAMPLE -> multisampleState.isEnabled();
case GL11.GL_NORMALIZE -> normalizeState.isEnabled();
case GL11.GL_POINT_SMOOTH -> pointSmoothState.isEnabled();
case GL11.GL_POLYGON_OFFSET_POINT -> polygonOffsetPointState.isEnabled();
case GL11.GL_POLYGON_OFFSET_LINE -> polygonOffsetLineState.isEnabled();
case GL11.GL_POLYGON_OFFSET_FILL -> polygonOffsetFillState.isEnabled();
case GL11.GL_POLYGON_SMOOTH -> polygonSmoothState.isEnabled();
case GL11.GL_POLYGON_STIPPLE -> polygonStippleState.isEnabled();
case GL12.GL_RESCALE_NORMAL -> rescaleNormalState.isEnabled();
case GL13.GL_SAMPLE_ALPHA_TO_COVERAGE -> sampleAlphaToCoverageState.isEnabled();
case GL13.GL_SAMPLE_ALPHA_TO_ONE -> sampleAlphaToOneState.isEnabled();
case GL13.GL_SAMPLE_COVERAGE -> sampleCoverageState.isEnabled();
case GL11.GL_COLOR_MATERIAL -> colorMaterial.isEnabled();
case GL11.GL_SCISSOR_TEST -> scissorTest.isEnabled();
case GL11.GL_STENCIL_TEST -> stencilTest.isEnabled();
case GL11.GL_TEXTURE_1D -> textures.getTexture1DStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_2D -> textures.getTextureUnitStates(activeTextureUnit.getValue()).isEnabled();
case GL12.GL_TEXTURE_3D -> textures.getTexture3DStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_S -> textures.getTexGenSStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_T -> textures.getTexGenTStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_R -> textures.getTexGenRStates(activeTextureUnit.getValue()).isEnabled();
case GL11.GL_TEXTURE_GEN_Q -> textures.getTexGenQStates(activeTextureUnit.getValue()).isEnabled();
case GL12.GL_RESCALE_NORMAL -> rescaleNormalState.isEnabled();
default -> GL11.glGetBoolean(pname);
};
}
@@ -677,11 +398,6 @@ public class GLStateManager {
}
}
@SneakyThrows
public static int getMatrixStackDepth(Matrix4fStack stack) {
return (int) MAT4_STACK_CURR_DEPTH.invokeExact(stack);
}
public static int glGetInteger(int pname) {
if(shouldBypassCache()) {
return GL11.glGetInteger(pname);
@@ -701,8 +417,6 @@ public class GLStateManager {
case GL11.GL_COLOR_MATERIAL_FACE -> colorMaterialFace.getValue();
case GL11.GL_COLOR_MATERIAL_PARAMETER -> colorMaterialParameter.getValue();
case GL20.GL_CURRENT_PROGRAM -> activeProgram;
case GL11.GL_MODELVIEW_STACK_DEPTH -> getMatrixStackDepth(modelViewMatrix);
case GL11.GL_PROJECTION_STACK_DEPTH -> getMatrixStackDepth(projectionMatrix);
default -> GL11.glGetInteger(pname);
};
@@ -1115,16 +829,6 @@ public class GLStateManager {
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, FloatBuffer pixels) {
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, DoubleBuffer pixels) {
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
}
public static void glTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
TextureInfoCache.INSTANCE.onTexImage2D(target, level, internalformat, width, height, border, format, type, pixels != null ? pixels.asIntBuffer() : (IntBuffer) null);
GL11.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
@@ -1249,57 +953,37 @@ public class GLStateManager {
i = mipmap ? GL11.GL_LINEAR_MIPMAP_LINEAR : GL11.GL_LINEAR;
j = GL11.GL_LINEAR;
} else {
i = mipmap ? GL11.GL_LINEAR_MIPMAP_LINEAR : GL11.GL_NEAREST;
i = mipmap ? GL11.GL_NEAREST_MIPMAP_LINEAR : GL11.GL_NEAREST;
j = GL11.GL_NEAREST;
}
glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, i);
glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, j);
}
public static void trySyncProgram() {
if (AngelicaConfig.enableIris) {
public static void glDrawArrays(int mode, int first, int count) {
// Iris -- TODO: This doesn't seem to work and is related to matchPass()
if(AngelicaConfig.enableIris) {
Iris.getPipelineManager().getPipeline().ifPresent(WorldRenderingPipeline::syncProgram);
}
}
public static void glBegin(int mode) {
trySyncProgram();
GL11.glBegin(mode);
}
public static void glDrawElements(int mode, ByteBuffer indices) {
trySyncProgram();
GL11.glDrawElements(mode, indices);
}
public static void glDrawElements(int mode, IntBuffer indices) {
trySyncProgram();
GL11.glDrawElements(mode, indices);
}
public static void glDrawElements(int mode, ShortBuffer indices) {
trySyncProgram();
GL11.glDrawElements(mode, indices);
}
public static void glDrawElements(int mode, int indices_count, int type, long indices_buffer_offset) {
trySyncProgram();
GL11.glDrawElements(mode, indices_count, type, indices_buffer_offset);
}
public static void glDrawElements(int mode, int count, int type, ByteBuffer indices) {
trySyncProgram();
GL11.glDrawElements(mode, count, type, indices);
GL11.glDrawArrays(mode, first, count);
}
public static void glDrawBuffer(int mode) {
trySyncProgram();
GL11.glDrawBuffer(mode);
}
public static void glDrawArrays(int mode, int first, int count) {
trySyncProgram();
GL11.glDrawArrays(mode, first, count);
public static void glDrawElements(int mode, IntBuffer indices) {
if(AngelicaConfig.enableIris) {
Iris.getPipelineManager().getPipeline().ifPresent(WorldRenderingPipeline::syncProgram);
}
GL11.glDrawElements(mode, indices);
}
public static void glBegin(int mode) {
if (AngelicaConfig.enableIris) {
Iris.getPipelineManager().getPipeline().ifPresent(WorldRenderingPipeline::syncProgram);
}
GL11.glBegin(mode);
}
public static void glLogicOp(int opcode) {
@@ -1507,21 +1191,13 @@ public class GLStateManager {
glListNesting += 1;
return;
}
glListId = list;
glListMode = mode;
GL11.glNewList(list, mode);
List<IStateStack<?>> stacks = Feature.maskToFeatures(GL11.GL_ALL_ATTRIB_BITS);
int size = stacks.size();
for(int i = 0; i < size; i++) {
IStateStack<?> stack = stacks.get(i);
for(IStateStack<?> stack : Feature.maskToFeatures(GL11.GL_ALL_ATTRIB_BITS)) {
// Feature Stack, copy of current feature state
glListStates.put(stack, (ISettableState<?>) ((ISettableState<?>)stack).copy());
}
if(glListMode == GL11.GL_COMPILE) {
pushState(GL11.GL_ALL_ATTRIB_BITS);
}
@@ -1564,7 +1240,6 @@ public class GLStateManager {
if(list < 0) {
VBOManager.get(list).render();
} else {
trySyncProgram();
GL11.glCallList(list);
if(glListChanges.containsKey(list)) {
for(Map.Entry<IStateStack<?>, ISettableState<?>> entry : glListChanges.get(list)) {
@@ -1577,23 +1252,15 @@ public class GLStateManager {
public static void pushState(int mask) {
attribs.push(mask);
List<IStateStack<?>> stacks = Feature.maskToFeatures(mask);
int size = stacks.size();
for(int i = 0; i < size; i++) {
stacks.get(i).push();
for(IStateStack<?> stack : Feature.maskToFeatures(mask)) {
stack.push();
}
}
}
public static void popState() {
final int mask = attribs.popInt();
List<IStateStack<?>> stacks = Feature.maskToFeatures(mask);
int size = stacks.size();
for(int i = 0; i < size; i++) {
stacks.get(i).pop();
for(IStateStack<?> stack : Feature.maskToFeatures(mask)) {
stack.pop();
}
}
@@ -2137,20 +1804,6 @@ public class GLStateManager {
}
}
public static boolean vendorIsAMD() {
return VENDOR == AMD;
}
public static boolean vendorIsIntel() {
return VENDOR == INTEL;
}
public static boolean vendorIsMesa() {
return VENDOR == MESA;
}
public static boolean vendorIsNVIDIA() {
return VENDOR == NVIDIA;
}
}
@@ -30,6 +30,7 @@ import java.nio.IntBuffer;
public class RenderSystem {
private static final Logger LOGGER = LogManager.getLogger("RenderSystem");
private static DSAAccess dsaState;
private static boolean hasMultibind;
private static boolean supportsCompute;
private RenderSystem() {}
@@ -40,6 +41,7 @@ public class RenderSystem {
dsaState = new DSACore();
LOGGER.info("OpenGL 4.5 detected, enabling DSA.");
}
hasMultibind = GLStateManager.capabilities.OpenGL45;
} catch (NoSuchFieldError ignored) {}
try {
@@ -53,6 +55,10 @@ public class RenderSystem {
LOGGER.info("No DSA support detected, falling back to legacy OpenGL.");
}
try {
hasMultibind |= GLStateManager.capabilities.GL_ARB_multi_bind;
} catch (NoSuchFieldError ignored) {}
supportsCompute = supportsCompute();
}
@@ -97,8 +103,6 @@ public class RenderSystem {
GL20.glUniform3f(location, v0, v1, v2);
}
public static void uniform3i(int location, int v0, int v1, int v2) { GL20.glUniform3i(location, v0, v1, v2); }
public static void uniform4f(int location, float v0, float v1, float v2, float v3) {
GL20.glUniform4f(location, v0, v1, v2, v3);
}
@@ -1,27 +0,0 @@
package com.gtnewhorizons.angelica.glsm;
/// Matches
public enum Vendor {
AMD("amd"),
INTEL("intel"),
MESA("mesa"),
NVIDIA("nvidia"),
UNKNOWN("");
final String[] names;
Vendor(String... names) {
this.names = names;
}
public static Vendor getVendor(String vendorString) {
for (var v : values()) {
for (var name : v.names) {
if (vendorString.contains(name)) return v;
}
}
// Shouldn't be needed as "" matches everything, but sanity checks good.
return UNKNOWN;
}
}
@@ -12,20 +12,7 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
protected int pointer;
public BooleanStateStack(int glCap) {
// Most booleans default to false
this(glCap, false);
}
/**
* Create a BooleanStateStack with a custom initial state.
* Useful for GL states that default to true (e.g., GL_DITHER, GL_MULTISAMPLE).
*
* @param glCap GL capability constant
* @param initialState initial enabled state
*/
public BooleanStateStack(int glCap, boolean initialState) {
super(glCap);
this.enabled = initialState;
stack = new BooleanState[GLStateManager.MAX_ATTRIB_STACK_DEPTH];
for (int i = 0; i < GLStateManager.MAX_ATTRIB_STACK_DEPTH; i++) {
stack[i] = new BooleanState(glCap);
@@ -47,7 +34,7 @@ public class BooleanStateStack extends BooleanState implements IStateStack<Boole
}
set(stack[--pointer]);
if (GLStateManager.vendorIsAMD() && glCap == GL11.GL_BLEND) {
if (GLStateManager.isAMD() && glCap == GL11.GL_BLEND) {
setEnabled(enabled);
}
return this;
@@ -22,7 +22,7 @@ public class BooleanState implements ISettableState<BooleanState> {
}
public void setEnabled(boolean enabled) {
if (GLStateManager.shouldBypassCache() || enabled != this.enabled || (this.glCap == GL11.GL_BLEND && GLStateManager.vendorIsAMD() && GLStateManager.isPoppingAttributes())) {
if (GLStateManager.shouldBypassCache() || enabled != this.enabled || (this.glCap == GL11.GL_BLEND && GLStateManager.isAMD() && GLStateManager.isPoppingAttributes())) {
this.enabled = enabled;
if (enabled) {
GL11.glEnable(this.glCap);
@@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.glsm.states;
public interface ISettableState<T> extends Cloneable {
T set(T state);
@SuppressWarnings("unchecked")
default void set(ISettableState<?> value) {
set((T) value);
}
@@ -18,6 +18,7 @@ public class LightState implements ISettableState<LightState> {
private static final Vector4i vector4i = new Vector4i();
private static final Vector4f vector4f = new Vector4f();
private static final Vector3i vector3i = new Vector3i();
private static final Vector3f vector3f = new Vector3f();
private static final Matrix3f matrix3f = new Matrix3f();
public int light;
@@ -124,7 +124,7 @@ public class MaterialState implements ISettableState<MaterialState> {
// glMaterial specifically for GL_COLOR_INDEXES. Other drivers do not do this.
// This probably breaks things but who knows, GLSM is setup to track to what the
// driver is doing for it right now.
if (GLStateManager.vendorIsNVIDIA()) {
if (GLStateManager.isNVIDIA()) {
vector3f.set(newBuffer.get(0), newBuffer.get(2), newBuffer.get(1));
} else {
vector3f.set(newBuffer);
@@ -6,40 +6,21 @@ import com.gtnewhorizons.angelica.glsm.stacks.TextureBindingStack;
import lombok.Getter;
import org.joml.Matrix4fStack;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
public class TextureUnitArray {
private final TextureBindingStack[] bindings;
private final BooleanStateStack[] states;
private final BooleanStateStack[] texture1DStates;
private final BooleanStateStack[] texture3DStates;
private final BooleanStateStack[] texGenSStates;
private final BooleanStateStack[] texGenTStates;
private final BooleanStateStack[] texGenRStates;
private final BooleanStateStack[] texGenQStates;
@Getter
public final Matrix4fStack[] textureMatricies;
public TextureUnitArray() {
bindings = new TextureBindingStack[GLStateManager.MAX_TEXTURE_UNITS];
states = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texture1DStates = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texture3DStates = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texGenSStates = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texGenTStates = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texGenRStates = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
texGenQStates = new BooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS];
textureMatricies = new Matrix4fStack[GLStateManager.MAX_TEXTURE_UNITS];
for (int i = 0; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
bindings[i] = new TextureBindingStack();
states[i] = new BooleanStateStack(GL11.GL_TEXTURE_2D);
texture1DStates[i] = new BooleanStateStack(GL11.GL_TEXTURE_1D);
texture3DStates[i] = new BooleanStateStack(GL12.GL_TEXTURE_3D);
texGenSStates[i] = new BooleanStateStack(GL11.GL_TEXTURE_GEN_S);
texGenTStates[i] = new BooleanStateStack(GL11.GL_TEXTURE_GEN_T);
texGenRStates[i] = new BooleanStateStack(GL11.GL_TEXTURE_GEN_R);
texGenQStates[i] = new BooleanStateStack(GL11.GL_TEXTURE_GEN_Q);
textureMatricies[i] = new Matrix4fStack(GLStateManager.MAX_TEXTURE_STACK_DEPTH);
}
}
@@ -52,30 +33,6 @@ public class TextureUnitArray {
return states[index];
}
public BooleanStateStack getTexture1DStates(int index) {
return texture1DStates[index];
}
public BooleanStateStack getTexture3DStates(int index) {
return texture3DStates[index];
}
public BooleanStateStack getTexGenSStates(int index) {
return texGenSStates[index];
}
public BooleanStateStack getTexGenTStates(int index) {
return texGenTStates[index];
}
public BooleanStateStack getTexGenRStates(int index) {
return texGenRStates[index];
}
public BooleanStateStack getTexGenQStates(int index) {
return texGenQStates[index];
}
public Matrix4fStack getTextureUnitMatrix(int index) {
return textureMatricies[index];
}
@@ -87,12 +44,6 @@ public class TextureUnitArray {
for (int i = 0; i < GLStateManager.MAX_TEXTURE_UNITS; i++) {
if (!bindings[i].sameAs(textureUnitArray.bindings[i])) return false;
if (!states[i].sameAs(textureUnitArray.states[i])) return false;
if (!texture1DStates[i].sameAs(textureUnitArray.texture1DStates[i])) return false;
if (!texture3DStates[i].sameAs(textureUnitArray.texture3DStates[i])) return false;
if (!texGenSStates[i].sameAs(textureUnitArray.texGenSStates[i])) return false;
if (!texGenTStates[i].sameAs(textureUnitArray.texGenTStates[i])) return false;
if (!texGenRStates[i].sameAs(textureUnitArray.texGenRStates[i])) return false;
if (!texGenQStates[i].sameAs(textureUnitArray.texGenQStates[i])) return false;
}
return true;
}
@@ -12,7 +12,7 @@ public class TextureInfo {
protected int width = -1;
protected int height = -1;
@Getter @Setter protected int minFilter = GL11.GL_LINEAR_MIPMAP_LINEAR;
@Getter @Setter protected int minFilter = GL11.GL_NEAREST_MIPMAP_LINEAR;
@Getter @Setter protected int magFilter = GL11.GL_LINEAR;
@Getter @Setter protected int wrapS = GL11.GL_REPEAT;
@Getter @Setter protected int wrapT = GL11.GL_REPEAT;
@@ -6,7 +6,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL11;
import java.nio.Buffer;
import java.nio.IntBuffer;
public class TextureInfoCache {
/**
@@ -25,7 +25,7 @@ public class TextureInfoCache {
return cache.computeIfAbsent(id, TextureInfo::new);
}
public void onTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable Buffer pixels) {
public void onTexImage2D(int target, int level, int internalformat, int width, int height, int border, int format, int type, @Nullable IntBuffer pixels) {
if (target == GL11.GL_TEXTURE_2D && level == 0) {
final TextureInfo info = getInfo(GLStateManager.getBoundTexture());
if(info == null) return;
@@ -1,14 +1,17 @@
package com.gtnewhorizons.angelica.hudcaching;
import java.util.ArrayList;
import java.util.List;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.compat.ModStatus;
import com.gtnewhorizons.angelica.compat.holoinventory.HoloInventoryReflectionCompat;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.mixins.interfaces.GuiIngameAccessor;
import com.gtnewhorizons.angelica.mixins.interfaces.GuiIngameForgeAccessor;
import com.gtnewhorizons.angelica.mixins.interfaces.RenderGameOverlayEventAccessor;
import com.kentington.thaumichorizons.common.ThaumicHorizons;
import cpw.mods.fml.common.eventhandler.EventPriority;
import net.dries007.holoInventory.client.Renderer;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.event.world.WorldEvent;
import org.lwjgl.opengl.GL11;
@@ -31,7 +34,7 @@ import xaero.common.core.XaeroMinimapCore;
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
// See LICENSE-HUDCaching.md for license information.
// See LICENSE+HUDCaching.md for license information.
public class HUDCaching {
@@ -56,10 +59,11 @@ public class HUDCaching {
public static boolean renderCrosshairsCaptured;
private final static RenderGameOverlayEvent fakeTextEvent = new RenderGameOverlayEvent.Text(new RenderGameOverlayEvent(0, null, 0, 0), null, null);
private final static RenderGameOverlayEvent.Post fakePostEvent = new RenderGameOverlayEvent.Post(new RenderGameOverlayEvent(0, null, 0, 0), RenderGameOverlayEvent.ElementType.HELMET);
private final static RenderGameOverlayEvent fakePreEvent = new RenderGameOverlayEvent.Pre(new RenderGameOverlayEvent(0, null, 0, 0), RenderGameOverlayEvent.ElementType.HELMET);
public static final HUDCaching INSTANCE = new HUDCaching();
private HUDCaching() {}
// highest so it runs before the GLSM load event
@@ -98,7 +102,7 @@ public class HUDCaching {
renderingCacheOverride = false;
mc.getFramebuffer().bindFramebuffer(false);
} else {
renderer.setupOverlayRendering();
renderer.setupOverlayRendering();
}
ScaledResolution resolution = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
@@ -111,50 +115,48 @@ public class HUDCaching {
// render bits that were captured when rendering into cache
GuiIngameAccessor gui = (GuiIngameAccessor) ingame;
if (renderVignetteCaptured) {
if (renderVignetteCaptured)
{
gui.callRenderVignette(mc.thePlayer.getBrightness(partialTicks), width, height);
} else {
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0);
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0);
}
if (ingame instanceof GuiIngameForge) {
GuiIngameForgeAccessor guiForge = ((GuiIngameForgeAccessor) ingame);
if (renderHelmetCaptured) {
guiForge.callRenderHelmet(resolution, partialTicks, hasScreen, mouseX, mouseY);
if (ModStatus.isHoloInventoryLoaded) {
HoloInventoryReflectionCompat.setAngelicaOverride(false);
GuiIngameForgeAccessor guiForge = ((GuiIngameForgeAccessor) ingame);
if (renderHelmetCaptured) {
guiForge.callRenderHelmet(resolution, partialTicks, hasScreen, mouseX, mouseY);
if (ModStatus.isHoloInventoryLoaded){
Renderer.INSTANCE.angelicaOverride = false;
// only settings the partial ticks as mouseX and mouseY are not used in renderEvent
((RenderGameOverlayEventAccessor) fakePostEvent).setPartialTicks(partialTicks);
HoloInventoryReflectionCompat.renderEvent(fakePostEvent);
((RenderGameOverlayEventAccessor) fakePreEvent).setPartialTicks(partialTicks);
Renderer.INSTANCE.renderEvent(fakePreEvent);
}
}
if (renderPortalCapturedTicks > 0) {
guiForge.callRenderPortal(width, height, partialTicks);
}
if (renderCrosshairsCaptured) {
if (ModStatus.isXaerosMinimapLoaded) {
// this fixes the crosshair going invisible when no lines are being drawn under the minimap
GLStateManager.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
}
guiForge.callRenderCrosshairs(width, height);
}
if (ModStatus.isThaumcraftLoaded || ModStatus.isThaumicHorizonsLoaded) {
}
if (renderPortalCapturedTicks > 0) {
guiForge.callRenderPortal(width, height, partialTicks);
}
if (renderCrosshairsCaptured) {
guiForge.callRenderCrosshairs(width, height);
}
if (ModStatus.isThaumcraftLoaded || ModStatus.isThaumicHorizonsLoaded){
((RenderGameOverlayEventAccessor) fakeTextEvent).setPartialTicks(partialTicks);
((RenderGameOverlayEventAccessor) fakeTextEvent).setResolution(resolution);
((RenderGameOverlayEventAccessor) fakeTextEvent).setMouseX(mouseX);
((RenderGameOverlayEventAccessor) fakeTextEvent).setMouseY(mouseY);
if (ModStatus.isThaumcraftLoaded) {
if (ModStatus.isThaumcraftLoaded){
Thaumcraft.instance.renderEventHandler.renderOverlay(fakeTextEvent);
}
if (ModStatus.isThaumicHorizonsLoaded) {
if (ModStatus.isThaumicHorizonsLoaded){
ThaumicHorizons.instance.renderEventHandler.renderOverlay(fakeTextEvent);
}
}
} else {
if (renderHelmetCaptured) {
if (renderHelmetCaptured)
{
gui.callRenderPumpkinBlur(width, height);
}
if (renderPortalCapturedTicks > 0) {
if (renderPortalCapturedTicks > 0)
{
gui.callRenderPortal(renderPortalCapturedTicks, width, height);
}
}
@@ -177,19 +179,19 @@ public class HUDCaching {
* the state before we start rendering
*/
public static void fixGLStateBeforeRenderingCache() {
GLStateManager.glDepthMask(true);
GLStateManager.enableDepthTest();
GLStateManager.enableAlphaTest();
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GLStateManager.disableBlend();
GLStateManager.glDepthMask(true);
GLStateManager.enableDepthTest();
GLStateManager.enableAlphaTest();
GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GLStateManager.disableBlend();
}
private static void resetFramebuffer(int width, int height) {
if (framebuffer.framebufferWidth != width || framebuffer.framebufferHeight != height) {
framebuffer.createBindFramebuffer(width, height);
framebuffer.createBindFramebuffer(width, height);
framebuffer.setFramebufferFilter(GL11.GL_NEAREST);
} else {
framebuffer.framebufferClear();
framebuffer.framebufferClear();
}
// copy depth buffer from MC
OpenGlHelper.func_153171_g(GL30.GL_READ_FRAMEBUFFER, mc.getFramebuffer().framebufferObject);
@@ -211,9 +213,10 @@ public class HUDCaching {
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
}
// moved to here due to the method being called from a mixin
public static void disableHoloInventory() {
if (ModStatus.isHoloInventoryLoaded) {
HoloInventoryReflectionCompat.setAngelicaOverride(true);
Renderer.INSTANCE.angelicaOverride = true;
}
}
@@ -1,22 +1,21 @@
package com.gtnewhorizons.angelica.loading;
import com.google.common.collect.ImmutableMap;
import com.gtnewhorizon.gtnhlib.asm.ASMUtil;
import com.gtnewhorizon.gtnhlib.config.ConfigException;
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
import com.gtnewhorizon.gtnhmixins.builders.ITransformers;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.config.CompatConfig;
import com.gtnewhorizons.angelica.config.FontConfig;
import com.gtnewhorizons.angelica.mixins.Mixins;
import com.gtnewhorizons.angelica.loading.fml.compat.CompatHandlers;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import com.gtnewhorizons.angelica.mixins.TargetedMod;
import com.gtnewhorizons.angelica.transform.compat.GenericCompatTransformer;
import com.gtnewhorizons.angelica.transform.compat.handlers.CompatHandler;
import com.gtnewhorizons.angelica.transform.compat.handlers.CompatHandlers;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import jss.notfine.asm.AsmTransformers;
import jss.notfine.asm.mappings.Namer;
import jss.notfine.config.MCPatcherForgeConfig;
import jss.notfine.config.NotFineConfig;
import net.minecraft.launchwrapper.Launch;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -27,7 +26,7 @@ import org.spongepowered.asm.launch.GlobalProperties;
import org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -43,22 +42,21 @@ import java.util.Set;
//@IFMLLoadingPlugin.SortingIndex(Integer.MAX_VALUE - 5)
@IFMLLoadingPlugin.MCVersion("1.7.10")
@IFMLLoadingPlugin.TransformerExclusions({
"jss.notfine.asm",
"com.gtnewhorizons.angelica.loading",
"com.gtnewhorizons.angelica.glsm.GLStateManager"})
"com.gtnewhorizons.angelica.transform.RedirectorTransformer",
"com.gtnewhorizons.angelica.glsm.GLStateManager"})
public class AngelicaTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
private static Boolean OBF_ENV;
private static final boolean DUMP_CLASSES = Boolean.parseBoolean(System.getProperty("angelica.dumpClass", "false"));
private static boolean OBF_ENV;
public static final Logger LOGGER = LogManager.getLogger("Angelica");
private String[] transformerClasses;
public AngelicaTweaker() {
static {
try {
// Angelica Config
ConfigurationManager.registerConfig(AngelicaConfig.class);
ConfigurationManager.registerConfig(CompatConfig.class);
ConfigurationManager.registerConfig(FontConfig.class);
MCPatcherForgeConfig.registerConfig();
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final Configuration config = ctx.getConfiguration();
@@ -69,29 +67,39 @@ public class AngelicaTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
ctx.updateLoggers();
// Debug features
AngelicaConfig.enableTestBlocks = Boolean.getBoolean("angelica.enableTestBlocks");
AngelicaConfig.enableTestBlocks = Boolean.parseBoolean(System.getProperty("angelica.enableTestBlocks", "false"));
} catch (ConfigException e) {
throw new RuntimeException(e);
}
verifyDependencies();
}
private static void verifyDependencies() {
if (AngelicaTweaker.class.getResource("/it/unimi/dsi/fastutil/ints/Int2ObjectMap.class") == null) {
throw new RuntimeException("Missing dependency: Angelica requires GTNHLib 0.2.1 or newer! Download: https://modrinth.com/mod/gtnhlib");
}
}
@Override
public String[] getASMTransformerClass() {
// Directly add this to the MixinServiceLaunchWrapper tweaker's list of Tweak Classes
final List<String> mixinTweakClasses = GlobalProperties.get(MixinServiceLaunchWrapper.BLACKBOARD_KEY_TWEAKCLASSES);
if (mixinTweakClasses != null) {
mixinTweakClasses.add(MixinCompatHackTweaker.class.getName());
}
if (transformerClasses == null) {
final List<String> transformers = new ArrayList<>();
transformers.addAll(CompatHandlers.getTransformers());
// Regsiter compat handlers, and add extra specific transformers, then build and register the generic transformer
for (CompatHandler handler : CompatHandlers.getHandlers()) {
GenericCompatTransformer.register(handler);
if (handler.extraTransformers() != null) {
transformers.addAll(handler.extraTransformers());
}
}
GenericCompatTransformer.build();
transformers.add(GenericCompatTransformer.class.getName());
// Add NotFine transformers
final List<String> notFineTransformers = Arrays.asList(ITransformers.getTransformers(AsmTransformers.class));
final List<String> notFineTransformers = AsmTransformers.getTransformers();
if (!notFineTransformers.isEmpty()) Namer.initNames();
transformers.addAll(notFineTransformers);
transformerClasses = transformers.toArray(new String[0]);
}
return transformerClasses;
@@ -109,28 +117,7 @@ public class AngelicaTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
@Override
public void injectData(Map<String, Object> data) {
OBF_ENV = (Boolean) data.get("runtimeDeobfuscationEnabled");
// Directly add this to the MixinServiceLaunchWrapper tweaker's list of Tweak Classes
final List<String> tweaks = GlobalProperties.get(MixinServiceLaunchWrapper.BLACKBOARD_KEY_TWEAKCLASSES);
if (tweaks != null) {
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.IncompatibleModsDisablerTweaker");
if (AngelicaConfig.enableHudCaching) {
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.XaerosTransformerDisablerTweaker");
}
if (FMLLaunchHandler.side().isClient()) {
// We register ITweakers that will run last in order to register
// specific IClassTransformers that will run last in the transformer chain.
// If we were to register them normally in getASMTransformerClass(),
// they would be sorted at index 0 which we do not want.
boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
if (!rfbLoaded) {
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.AngelicaLateTweaker");
}
if (AngelicaConfig.enableSodium) {
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.SodiumLateTweaker");
}
}
}
OBF_ENV = (boolean) data.get("runtimeDeobfuscationEnabled");
}
@Override
@@ -145,34 +132,39 @@ public class AngelicaTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
@Override
public List<String> getMixins(Set<String> loadedCoreMods) {
NotFineConfig.loadSettings();
//This may be possible to handle differently or fix.
if (loadedCoreMods.contains("cofh.asm.LoadingPlugin")) {
MCPatcherForgeConfig.ExtendedHD.hdFont = false;
}
return IMixins.getEarlyMixins(Mixins.class, loadedCoreMods);
return Mixins.getEarlyMixins(loadedCoreMods);
}
public static boolean DUMP_CLASSES() {
return DUMP_CLASSES || !OBF_ENV;
}
/**
* Returns true if we are in an obfuscated environment, returns false in dev environment.
*/
public static boolean isObfEnv() {
if (OBF_ENV == null) {
throw new IllegalStateException("Obfuscation state has been accessed too early!");
}
return OBF_ENV;
}
/**
* Returns the appropriate name according to current environment's obfuscation
*/
public static String obf(String deobf, String obf) {
if (OBF_ENV == null) {
throw new IllegalStateException("Obfuscation state has been accessed too early!");
public static void dumpClass(String className, byte[] originalBytes, byte[] transformedBytes, Object transformer) {
if (AngelicaTweaker.DUMP_CLASSES()) {
ASMUtil.saveAsRawClassFile(originalBytes, className + "_PRE", transformer);
ASMUtil.saveAsRawClassFile(transformedBytes, className + "_POST", transformer);
}
if (OBF_ENV) {
return obf;
}
return deobf;
}
private static final ImmutableMap<String, TargetedMod> MODS_BY_CLASS = ImmutableMap.<String, TargetedMod>builder()
.put("optifine.OptiFineForgeTweaker", TargetedMod.OPTIFINE)
.put("fastcraft.Tweaker", TargetedMod.FASTCRAFT)
.put("cofh.asm.LoadingPlugin", TargetedMod.COFHCORE)
.build();
public static final Set<TargetedMod> coreMods = new HashSet<>();
private static void detectCoreMods(Set<String> loadedCoreMods) {
MODS_BY_CLASS.forEach((key, value) -> {
if (loadedCoreMods.contains(key))
coreMods.add(value);
});
}
}
@@ -1,7 +1,11 @@
package com.gtnewhorizons.angelica.loading.fml.tweakers;
package com.gtnewhorizons.angelica.loading;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.transform.BlockTransformer;
import com.gtnewhorizons.angelica.transform.RedirectorTransformer;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.relauncher.CoreModManager;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.Launch;
@@ -19,44 +23,61 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
public class IncompatibleModsDisablerTweaker implements ITweaker {
private static final boolean DISABLE_OPTIFINE_FASTCRAFT_BETTERFPS = true;
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
public class MixinCompatHackTweaker implements ITweaker {
public static final boolean DISABLE_OPTIFINE_FASTCRAFT_BETTERFPS = true;
@Override
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
// no-op
}
verifyDependencies();
@Override
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
if (DISABLE_OPTIFINE_FASTCRAFT_BETTERFPS) {
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Disabling Optifine, Fastcraft, BetterFPS, and other incompatible mods (if present)");
disableIncompatibleMods(classLoader);
if(DISABLE_OPTIFINE_FASTCRAFT_BETTERFPS) {
LOGGER.info("Disabling Optifine, Fastcraft, BetterFPS, and other incompatible mods (if present)");
disableIncompatibleMods();
}
if (AngelicaConfig.enableHudCaching){
disableXaerosMinimapWaypointTransformer();
}
}
@Override
public String getLaunchTarget() {
return null;
private void verifyDependencies() {
if(MixinCompatHackTweaker.class.getResource("/it/unimi/dsi/fastutil/ints/Int2ObjectMap.class") == null) {
throw new RuntimeException("Missing dependency: Angelica requires GTNHLib 0.2.1 or newer! Download: https://modrinth.com/mod/gtnhlib");
}
}
@Override
public String[] getLaunchArguments() {
return new String[0];
private void disableXaerosMinimapWaypointTransformer(){
try {
final LaunchClassLoader lcl = Launch.classLoader;
final Field xformersField = lcl.getClass().getDeclaredField("transformers");
xformersField.setAccessible(true);
@SuppressWarnings("unchecked")
List<IClassTransformer> xformers = (List<IClassTransformer>) xformersField.get(lcl);
for (int idx = xformers.size() - 1; idx >= 0; idx--) {
final String name = xformers.get(idx).getClass().getName();
if (name.startsWith("xaero.common.core.transformer.GuiIngameForgeTransformer")) {
LOGGER.info("Removing transformer " + name);
xformers.remove(idx);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
private void disableIncompatibleMods(LaunchClassLoader lcl) {
private void disableIncompatibleMods() {
// Remove transformers, Mod Containers, and mixins for Optifine, Fastcraft, BetterFPS and other incompatible mods
try {
final LaunchClassLoader lcl = Launch.classLoader;
final Field xformersField = lcl.getClass().getDeclaredField("transformers");
xformersField.setAccessible(true);
final List<IClassTransformer> xformers = (List<IClassTransformer>) xformersField.get(lcl);
for (int idx = xformers.size() - 1; idx >= 0; idx--) {
final String name = xformers.get(idx).getClass().getName();
if (name.startsWith("optifine") || name.startsWith("fastcraft") || name.startsWith("me.guichaguri.betterfps")) {
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Removing transformer " + name);
LOGGER.info("Removing transformer " + name);
xformers.remove(idx);
}
}
@@ -64,14 +85,13 @@ public class IncompatibleModsDisablerTweaker implements ITweaker {
throw new RuntimeException(e);
}
try {
final Class<?> loaderClass = Class.forName("cpw.mods.fml.common.Loader", true, lcl);
final Field injectedContainersField = loaderClass.getDeclaredField("injectedContainers");
final Field injectedContainersField = Loader.class.getDeclaredField("injectedContainers");
injectedContainersField.setAccessible(true);
final List<String> containers = (List<String>) injectedContainersField.get(loaderClass);
final List<String> containers = (List<String> ) injectedContainersField.get(Loader.class);
for (int idx = containers.size() - 1; idx >= 0; idx--) {
final String name = containers.get(idx);
final String name = containers.get(idx);
if (name.startsWith("optifine") || name.startsWith("fastcraft")) {
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Removing mod container " + name);
LOGGER.info("Removing mod container " + name);
containers.remove(idx);
}
}
@@ -85,7 +105,7 @@ public class IncompatibleModsDisablerTweaker implements ITweaker {
for (int idx = reparsedCoremods.size() - 1; idx >= 0; idx--) {
final String coreMod = reparsedCoremods.get(idx);
if (coreMod.startsWith("optimizationsandtweaks")) {
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Removing reparsed coremod " + coreMod);
LOGGER.info("Removing reparsed coremod " + coreMod);
// Fool the CoreModManager into not checking for a mod container again later
loadedCoremods.add(reparsedCoremods.remove(idx));
@@ -100,7 +120,7 @@ public class IncompatibleModsDisablerTweaker implements ITweaker {
for (int idx = mixinConfigsDefault.size() - 1; idx >= 0; idx--) {
final String name = mixinConfigsDefault.get(idx);
if (name != null && name.contains("optimizationsandtweaks")) {
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Removing mixin config " + name);
LOGGER.info("Removing mixin config " + name);
mixinConfigsDefault.remove(idx);
}
}
@@ -111,7 +131,7 @@ public class IncompatibleModsDisablerTweaker implements ITweaker {
for (Config config : mixinConfigs) {
final String name = config.getName();
if (name != null && name.contains("optimizationsandtweaks")) {
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Removing queued mixin config " + config.getName());
LOGGER.info("Removing queued mixin config " + config.getName());
toRemove.add(config);
}
}
@@ -124,8 +144,8 @@ public class IncompatibleModsDisablerTweaker implements ITweaker {
final Map<IContainerHandle, MixinContainer> containers = (Map<IContainerHandle, MixinContainer>) containersField.get(platformManager);
for (Map.Entry<IContainerHandle, MixinContainer> entry : containers.entrySet()) {
final String attribute = entry.getKey().getAttribute("MixinConfigs");
if (attribute != null && attribute.contains("optimizationsandtweaks")) {
FMLRelaunchLog.info("[Angelica/IncompatibleModsDisablerTweaker] Removing mixin container " + entry.getKey().toString());
if(attribute != null && attribute.contains("optimizationsandtweaks")) {
LOGGER.info("Removing mixin container " + entry.getKey().toString());
containers.remove(entry.getKey());
}
}
@@ -135,4 +155,31 @@ public class IncompatibleModsDisablerTweaker implements ITweaker {
throw new RuntimeException(e);
}
}
@Override
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
// no-op
}
@Override
public String getLaunchTarget() {
return null;
}
@Override
public String[] getLaunchArguments() {
if (FMLLaunchHandler.side().isClient()) {
final boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
if (!rfbLoaded) {
// Run after Mixins, but before LWJGl3ify
Launch.classLoader.registerTransformer(RedirectorTransformer.class.getName());
}
if(AngelicaConfig.enableSodium) {
Launch.classLoader.registerTransformer(BlockTransformer.class.getName());
}
}
return new String[0];
}
}
@@ -1,64 +0,0 @@
package com.gtnewhorizons.angelica.loading.fml.compat;
import com.gtnewhorizons.angelica.config.CompatConfig;
import com.gtnewhorizons.angelica.loading.fml.compat.handlers.ExtraUtilsCompatHandler;
import com.gtnewhorizons.angelica.loading.fml.compat.handlers.ImmersiveEngineeringCompatHandler;
import com.gtnewhorizons.angelica.loading.fml.compat.handlers.StacksOnStacksCompatHandler;
import com.gtnewhorizons.angelica.loading.fml.compat.handlers.ThaumcraftCompatHandler;
import com.gtnewhorizons.angelica.loading.fml.compat.handlers.ThaumicHorizonsCompatHandler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
public enum CompatHandlers {
STACKS_ON_STACKS(() -> CompatConfig.fixStacksOnStacks, new StacksOnStacksCompatHandler()),
EXTRA_UTILS(() -> CompatConfig.fixExtraUtils, new ExtraUtilsCompatHandler()),
IMMERSIVE_ENGINEERING(() -> CompatConfig.fixImmersiveEngineering, new ImmersiveEngineeringCompatHandler()),
THAUMCRAFT(() -> CompatConfig.fixThaumcraft, new ThaumcraftCompatHandler()),
THAUMIC_HORIZONS(() -> CompatConfig.fixThaumicHorizons, new ThaumicHorizonsCompatHandler());
private final Supplier<Boolean> applyIf;
private final ICompatHandler handler;
CompatHandlers(Supplier<Boolean> applyIf, ICompatHandler handler) {
this.applyIf = applyIf;
this.handler = handler;
}
private static List<ICompatHandler> compatHandlers = null;
public static List<ICompatHandler> getHandlers() {
if (compatHandlers != null) {
return compatHandlers;
}
compatHandlers = new ArrayList<>();
for (CompatHandlers value : values()) {
if (value.applyIf.get()) {
compatHandlers.add(value.handler);
}
}
return compatHandlers;
}
/**
* Returns extra transformers as well as the main transformer.
* Returns an empty list if no handlers registered.
*/
public static List<String> getTransformers() {
final List<ICompatHandler> handlers = getHandlers();
if (handlers.isEmpty()) {
return Collections.emptyList();
}
List<String> transformers = new ArrayList<>();
for (ICompatHandler handler : handlers) {
if (handler.extraTransformers() != null) {
transformers.addAll(handler.extraTransformers());
}
}
transformers.add("com.gtnewhorizons.angelica.loading.fml.transformers.GenericCompatTransformer");
return transformers;
}
}
@@ -1,49 +0,0 @@
package com.gtnewhorizons.angelica.loading.fml.transformers;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import com.gtnewhorizons.angelica.loading.shared.transformers.AngelicaRedirector;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
/** IClassTransformer wrapper for {@link AngelicaRedirector} */
public class AngelicaRedirectorTransformer implements IClassTransformer {
private final AngelicaRedirector inner;
private final String[] exclusions;
public AngelicaRedirectorTransformer() {
inner = new AngelicaRedirector(AngelicaTweaker.isObfEnv());
exclusions = inner.getTransformerExclusions();
}
@Override
public byte[] transform(final String className, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
// Ignore classes that are excluded from transformation - Doesn't fully work without the
// TransformerExclusions due to some nested classes
for (String exclusion : exclusions) {
if (transformedName.startsWith(exclusion)) return basicClass;
}
if (!inner.shouldTransform(basicClass)) {
return basicClass;
}
final ClassReader cr = new ClassReader(basicClass);
final ClassNode cn = new ClassNode();
cr.accept(cn, 0);
final boolean changed = inner.transformClassNode(transformedName, cn);
if (changed) {
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
return basicClass;
}
}
@@ -1,91 +0,0 @@
package com.gtnewhorizons.angelica.loading.fml.transformers;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import com.gtnewhorizons.angelica.loading.fml.compat.ICompatHandler;
import com.gtnewhorizons.angelica.loading.fml.compat.CompatHandlers;
import com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic.FieldLevelTessellatorTransformer;
import com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic.HUDCachingEarlyReturnTransformer;
import com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic.ThreadSafeISBRHAnnotationTransformer;
import com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic.TileEntityNullGuardTransformer;
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.launchwrapper.IClassTransformer;
import org.spongepowered.asm.lib.ClassReader;
import org.spongepowered.asm.lib.tree.ClassNode;
import org.spongepowered.asm.transformers.MixinClassWriter;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class GenericCompatTransformer implements IClassTransformer {
private final Map<String, List<String>> fieldLevelTessellator = new Object2ObjectOpenHashMap<>();
private final Map<String, List<String>> tileEntityNullGuard = new Object2ObjectOpenHashMap<>();
private final Map<String, Boolean> threadSafeIBSRH = new Object2BooleanOpenHashMap<>();
private final Map<String, List<String>> hudCachingEarlyReturn = new Object2ObjectOpenHashMap<>();
private final Set<String> targetedClasses = new ObjectOpenHashSet<>();
public GenericCompatTransformer() {
for (ICompatHandler handler : CompatHandlers.getHandlers()) {
registerHandler(handler);
}
buildTargetClassSet();
}
private void registerHandler(ICompatHandler handler) {
if (handler.getFieldLevelTessellator() != null) {
fieldLevelTessellator.putAll(handler.getFieldLevelTessellator());
}
if (handler.getTileEntityNullGuard() != null) {
tileEntityNullGuard.putAll(handler.getTileEntityNullGuard());
}
if (handler.getThreadSafeISBRHAnnotations() != null) {
threadSafeIBSRH.putAll(handler.getThreadSafeISBRHAnnotations());
}
if (handler.getHUDCachingEarlyReturn() != null) {
hudCachingEarlyReturn.putAll(handler.getHUDCachingEarlyReturn());
}
}
private void buildTargetClassSet() {
targetedClasses.addAll(fieldLevelTessellator.keySet());
targetedClasses.addAll(tileEntityNullGuard.keySet());
targetedClasses.addAll(threadSafeIBSRH.keySet());
targetedClasses.addAll(hudCachingEarlyReturn.keySet());
}
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
if (!targetedClasses.contains(transformedName)) return basicClass;
ClassReader cr = new ClassReader(basicClass);
ClassNode cn = new ClassNode();
cr.accept(cn, 0);
if (fieldLevelTessellator.containsKey(transformedName)) {
FieldLevelTessellatorTransformer.transform(cn, fieldLevelTessellator.get(transformedName));
}
if (tileEntityNullGuard.containsKey(transformedName)) {
TileEntityNullGuardTransformer.transform(cn, tileEntityNullGuard.get(transformedName));
}
if (threadSafeIBSRH.containsKey(transformedName)) {
ThreadSafeISBRHAnnotationTransformer.transform(cn, threadSafeIBSRH.get(transformedName));
}
if (hudCachingEarlyReturn.containsKey(transformedName)) {
HUDCachingEarlyReturnTransformer.transform(cn, hudCachingEarlyReturn.get(transformedName));
}
MixinClassWriter cw = new MixinClassWriter(MixinClassWriter.COMPUTE_MAXS | MixinClassWriter.COMPUTE_FRAMES);
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
}
@@ -1,36 +0,0 @@
package com.gtnewhorizons.angelica.loading.fml.tweakers;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import java.io.File;
import java.util.List;
public class AngelicaLateTweaker implements ITweaker {
@Override
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
// no-op
}
@Override
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
// no-op
}
@Override
public String getLaunchTarget() {
return null;
}
@Override
public String[] getLaunchArguments() {
// Run after Mixins, but before LWJGl3ify
String transformer = "com.gtnewhorizons.angelica.loading.fml.transformers.AngelicaRedirectorTransformer";
FMLRelaunchLog.finer("Registering transformer %s", transformer);
Launch.classLoader.registerTransformer(transformer);
return new String[0];
}
}
@@ -1,35 +0,0 @@
package com.gtnewhorizons.angelica.loading.fml.tweakers;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import java.io.File;
import java.util.List;
public class SodiumLateTweaker implements ITweaker {
@Override
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
// no-op
}
@Override
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
// no-op
}
@Override
public String getLaunchTarget() {
return null;
}
@Override
public String[] getLaunchArguments() {
String transformer = "com.gtnewhorizons.angelica.loading.fml.transformers.SodiumBlockTransformer";
FMLRelaunchLog.finer("Registering transformer %s", transformer);
Launch.classLoader.registerTransformer(transformer);
return new String[0];
}
}
@@ -1,47 +0,0 @@
package com.gtnewhorizons.angelica.loading.fml.tweakers;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.LaunchClassLoader;
import java.io.File;
import java.lang.reflect.Field;
import java.util.List;
public class XaerosTransformerDisablerTweaker implements ITweaker {
@Override
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
// no-op
}
@Override
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
try {
final Field xformersField = classLoader.getClass().getDeclaredField("transformers");
xformersField.setAccessible(true);
@SuppressWarnings("unchecked")
List<IClassTransformer> transformers = (List<IClassTransformer>) xformersField.get(classLoader);
for (int idx = transformers.size() - 1; idx >= 0; idx--) {
final String name = transformers.get(idx).getClass().getName();
if (name.startsWith("xaero.common.core.transformer.GuiIngameForgeTransformer")) {
transformers.remove(idx);
FMLRelaunchLog.info("[Angelica/XaerosTransformerDisablerTweaker] Removed transformer " + name);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public String getLaunchTarget() {
return null;
}
@Override
public String[] getLaunchArguments() {
return new String[0];
}
}
@@ -1,11 +0,0 @@
/**
* ITweaker needs to be in a standalone package
* because FML adds the whole package to the class
* loader exclusions and that causes really hard to
* find bugs. ITweakers also get class loaded
* by the parent class loader and not LaunchClassLoader,
* as a result the ITweakers should not reference any
* class otherwise it will load them (maybe a second time)
* on the parent class loader.
*/
package com.gtnewhorizons.angelica.loading.fml.tweakers;
@@ -1,6 +1,5 @@
package com.gtnewhorizons.angelica.loading.rfb;
import com.gtnewhorizons.angelica.loading.rfb.transformers.RFBAngelicaRedirector;
import com.gtnewhorizons.retrofuturabootstrap.api.PluginContext;
import com.gtnewhorizons.retrofuturabootstrap.api.RetroFuturaBootstrap;
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
@@ -22,10 +21,8 @@ public class AngelicaRfbPlugin implements RfbPlugin {
if (isServer) {
return null;
}
final boolean isObf = RetroFuturaBootstrap.API.launchClassLoader().findClassMetadata("net.minecraft.world.World") == null;
return new RfbClassTransformer[] {
new RFBAngelicaRedirector(isObf)
new RedirectorTransformerWrapper()
};
}
}
@@ -1,7 +1,6 @@
package com.gtnewhorizons.angelica.loading.rfb.transformers;
package com.gtnewhorizons.angelica.loading.rfb;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import com.gtnewhorizons.angelica.loading.shared.transformers.AngelicaRedirector;
import com.gtnewhorizons.angelica.transform.RedirectorTransformer;
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
import com.gtnewhorizons.retrofuturabootstrap.api.ExtensibleClassLoader;
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
@@ -11,14 +10,10 @@ import org.jetbrains.annotations.Nullable;
import java.util.jar.Manifest;
/** RfbClassTransformer wrapper for {@link AngelicaRedirector} */
public class RFBAngelicaRedirector implements RfbClassTransformer {
/** RFB wrapper for {@link com.gtnewhorizons.angelica.transform.RedirectorTransformer} */
public class RedirectorTransformerWrapper implements RfbClassTransformer {
private final AngelicaRedirector inner;
public RFBAngelicaRedirector(boolean isObf) {
inner = new AngelicaRedirector(isObf);
}
public final RedirectorTransformer inner = new RedirectorTransformer();
@Pattern("[a-z0-9-]+")
@Override
@@ -38,7 +33,7 @@ public class RFBAngelicaRedirector implements RfbClassTransformer {
@Override
public @NotNull String @Nullable [] additionalExclusions() {
return inner.getTransformerExclusions();
return RedirectorTransformer.getTransformerExclusions().toArray(new String[0]);
}
@Override
@@ -52,7 +47,7 @@ public class RFBAngelicaRedirector implements RfbClassTransformer {
// If a class is already a transformed ClassNode, conservatively continue processing.
return true;
}
return inner.shouldTransform(classNode.getOriginalBytes());
return inner.shouldRfbTransform(classNode.getOriginalBytes());
}
@Override
@@ -61,7 +56,6 @@ public class RFBAngelicaRedirector implements RfbClassTransformer {
final boolean changed = inner.transformClassNode(className, classNode.getNode());
if (changed) {
classNode.computeMaxs();
AngelicaClassDump.dumpRFBClass(className, classNode, this);
}
}
}
@@ -1,25 +0,0 @@
package com.gtnewhorizons.angelica.loading.shared;
import com.gtnewhorizon.gtnhlib.asm.ASMUtil;
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
public class AngelicaClassDump {
private static final boolean DUMP_CLASS = Boolean.getBoolean("angelica.dumpClass");
public static void dumpClass(String className, byte[] originalBytes, byte[] transformedBytes, Object transformer) {
if (DUMP_CLASS) {
ASMUtil.saveAsRawClassFile(originalBytes, className + "_PRE", transformer);
ASMUtil.saveAsRawClassFile(transformedBytes, className + "_POST", transformer);
}
}
public static void dumpRFBClass(String className, ClassNodeHandle classNode, Object transformer) {
if (DUMP_CLASS) {
final byte[] originalBytes = classNode.getOriginalBytes();
final byte[] transformedBytes = classNode.computeBytes();
ASMUtil.saveAsRawClassFile(originalBytes, className + "_PRE", transformer);
ASMUtil.saveAsRawClassFile(transformedBytes, className + "_POST", transformer);
}
}
}
@@ -1,6 +0,0 @@
/**
* Package that contains classes that may be loaded both by RFB and FML classloaders, the code contained in this package
* should not reference any code from outside of this package to avoid causing hard to debug classloading issues unless
* the code is known to be safe with different classloaders.
*/
package com.gtnewhorizons.angelica.loading.shared;
@@ -1,243 +1,213 @@
package com.gtnewhorizons.angelica.mixins;
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
import com.gtnewhorizon.gtnhmixins.builders.MixinBuilder;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.config.CompatConfig;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import jss.notfine.config.MCPatcherForgeConfig;
import jss.notfine.config.NotFineConfig;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@Getter
@RequiredArgsConstructor
public enum Mixins implements IMixins {
ANGELICA_STARTUP(new MixinBuilder()
.setPhase(Phase.EARLY)
.addClientMixins(
"angelica.startup.MixinInitGLStateManager"
public enum Mixins {
ANGELICA_STARTUP(new Builder("Angelica Startup").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).addMixinClasses(
"angelica.startup.MixinInitGLStateManager"
)
),
ANGELICA(new MixinBuilder()
.setPhase(Phase.EARLY)
.addClientMixins(
"angelica.MixinActiveRenderInfo"
, "angelica.MixinEntityRenderer"
, "angelica.MixinGameSettings"
, "angelica.MixinMinecraft"
, "angelica.MixinMinecraftServer"
, "angelica.optimizations.MixinRendererLivingEntity"
, "angelica.MixinFMLClientHandler"
, "angelica.bugfixes.MixinRenderGlobal_DestroyBlock"
ANGELICA(new Builder("Angelica").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).addMixinClasses(
"angelica.MixinActiveRenderInfo"
,"angelica.MixinEntityRenderer"
,"angelica.MixinGameSettings"
,"angelica.MixinMinecraft"
,"angelica.MixinMinecraftServer"
,"angelica.optimizations.MixinRendererLivingEntity"
,"angelica.MixinFMLClientHandler"
,"angelica.bugfixes.MixinRenderGlobal_DestroyBlock"
)
),
ANGELICA_VBO(
new MixinBuilder()
.setApplyIf(() -> AngelicaConfig.enableVBO)
.setPhase(Phase.EARLY)
.addClientMixins(
"angelica.vbo.MixinGLAllocation"
, "angelica.vbo.MixinModelRenderer"
, "angelica.vbo.MixinRenderGlobal"
, "angelica.vbo.MixinWavefrontObject"
)
new Builder("Angelica VBO").addTargetedMod(TargetedMod.VANILLA).setApplyIf(() -> AngelicaConfig.enableVBO).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).addMixinClasses(
"angelica.vbo.MixinGLAllocation"
,"angelica.vbo.MixinModelRenderer"
,"angelica.vbo.MixinRenderGlobal"
,"angelica.vbo.MixinWavefrontObject"
)
),
ANGELICA_FONT_RENDERER(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableFontRenderer)
.addClientMixins(
"angelica.fontrenderer.MixinGuiIngameForge"
, "angelica.fontrenderer.MixinFontRenderer"
, "angelica.fontrenderer.MixinMCResourceAccessor"
ANGELICA_FONT_RENDERER(new Builder("Angelica Font Renderer").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableFontRenderer).addMixinClasses(
"angelica.fontrenderer.MixinGuiIngameForge"
,"angelica.fontrenderer.MixinFontRenderer"
)
),
ANGELICA_ENABLE_DEBUG(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaMod.lwjglDebug)
.addClientMixins(
"angelica.debug.MixinProfiler"
, "angelica.debug.MixinSplashProgress"
, "angelica.debug.MixinTextureManager"
ANGELICA_ENABLE_DEBUG(new Builder("Angelica Debug").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaMod.lwjglDebug).addMixinClasses(
"angelica.debug.MixinProfiler"
,"angelica.debug.MixinSplashProgress"
,"angelica.debug.MixinTextureManager"
)
),
ANGELICA_DYNAMIC_LIGHTS(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableDynamicLights)
.addClientMixins(
ANGELICA_DYNAMIC_LIGHTS(new Builder("Angelica Dynamic Lights").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableDynamicLights).addMixinClasses(
"angelica.dynamiclights.MixinEntityRenderer"
, "angelica.dynamiclights.MixinEntity"
, "angelica.dynamiclights.MixinWorld"
, "angelica.dynamiclights.MixinItemRenderer"
,"angelica.dynamiclights.MixinEntity"
,"angelica.dynamiclights.MixinWorld"
,"angelica.dynamiclights.MixinItemRenderer"
)
),
ANGELICA_FIX_FLUID_RENDERER_CHECKING_BLOCK_AGAIN(
new MixinBuilder("Fix RenderBlockFluid reading the block type from the world access multiple times")
.setPhase(Phase.EARLY)
.addClientMixins("angelica.bugfixes.MixinRenderBlockFluid")
.setApplyIf(() -> AngelicaConfig.fixFluidRendererCheckingBlockAgain)),
new Builder("Fix RenderBlockFluid reading the block type from the world access multiple times")
.setPhase(Phase.EARLY).addMixinClasses("angelica.bugfixes.MixinRenderBlockFluid").setSide(Side.BOTH)
.setApplyIf(() -> AngelicaConfig.fixFluidRendererCheckingBlockAgain)
.addTargetedMod(TargetedMod.VANILLA)),
ANGELICA_LIMIT_DROPPED_ITEM_ENTITIES(new MixinBuilder()
.setPhase(Phase.EARLY)
.addClientMixins("angelica.optimizations.MixinRenderGlobal_ItemRenderDist")
.setApplyIf(() -> AngelicaConfig.dynamicItemRenderDistance)),
ANGELICA_LIMIT_DROPPED_ITEM_ENTITIES(new Builder("Dynamically modifies the render distance of dropped items entities to preserve performance")
.setPhase(Phase.EARLY).addMixinClasses("angelica.optimizations.MixinRenderGlobal_ItemRenderDist").setSide(Side.CLIENT)
.setApplyIf(() -> AngelicaConfig.dynamicItemRenderDistance)
.addTargetedMod(TargetedMod.VANILLA)),
ANGELICA_ITEM_RENDERER_OPTIMIZATION(new MixinBuilder("Optimizes in-world item rendering")
.setPhase(Phase.EARLY)
.addClientMixins("angelica.itemrenderer.MixinItemRenderer")
.setApplyIf(() -> AngelicaConfig.optimizeInWorldItemRendering)),
ANGELICA_ITEM_DISPLAY_LIST_OPTIMIZATION(new Builder("Optimized item rendering by wrapping them with display lists")
.setPhase(Phase.EARLY).addMixinClasses("angelica.itemrenderer.MixinItemRenderer").setSide(Side.CLIENT)
.setApplyIf(() -> AngelicaConfig.optimizeInWorldItemRendering)
.addTargetedMod(TargetedMod.VANILLA)),
// Not compatible with the lwjgl debug callbacks, so disable if that's enabled
ARCHAIC_SPLASH(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.showSplashMemoryBar && !AngelicaMod.lwjglDebug)
.addClientMixins(
"angelica.archaic.MixinSplashProgress$3",
"angelica.archaic.AccessorSplashProgress"
ARCHAIC_SPLASH(new Builder("ArchaicFix Splash").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.showSplashMemoryBar && !AngelicaMod.lwjglDebug).addMixinClasses(
"angelica.archaic.MixinSplashProgress"
,"angelica.archaic.AccessorSplashProgress"
)
),
ARCHAIC_CORE(new MixinBuilder()
.addExcludedMod(TargetedMod.ARCHAICFIX)
.setPhase(Phase.EARLY)
.addClientMixins(
"angelica.archaic.MixinBlockFence"
, "angelica.archaic.MixinFMLClientHandler"
, "angelica.archaic.MixinGuiIngameForge"
, "angelica.archaic.MixinNetHandlerPlayClient"
, "angelica.archaic.MixinThreadDownloadImageData"
ARCHAIC_CORE(new Builder("Archaic Core").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).addMixinClasses(
"angelica.archaic.MixinBlockFence"
,"angelica.archaic.MixinFMLClientHandler"
,"angelica.archaic.MixinGuiIngameForge"
,"angelica.archaic.MixinNetHandlerPlayClient"
,"angelica.archaic.MixinThreadDownloadImageData"
)
),
IRIS_STARTUP(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins(
"shaders.startup.MixinGameSettings"
, "shaders.startup.MixinMinecraft"
, "shaders.startup.MixinInitRenderer"
, "shaders.startup.MixinAbstractTexture"
, "shaders.startup.MixinTextureAtlasSprite"
, "shaders.startup.MixinTextureMap"
IRIS_STARTUP(new Builder("Start Iris").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableIris).addMixinClasses(
"shaders.startup.MixinGameSettings"
,"shaders.startup.MixinGuiMainMenu"
,"shaders.startup.MixinInitRenderer"
,"shaders.startup.MixinAbstractTexture"
,"shaders.startup.MixinTextureAtlasSprite"
,"shaders.startup.MixinTextureMap"
)
),
SODIUM(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableSodium)
.addClientMixins(
"sodium.MixinBlock"
, "sodium.MixinBlockFluidBase"
, "sodium.AccessorBiomeColorEvent"
, "sodium.MixinBiomeGenBase"
, "sodium.MixinChunk"
, "sodium.MixinChunkProviderServer"
, "sodium.MixinClientRegistry"
, "sodium.MixinEntity_RenderDist"
, "sodium.MixinEntityItem_RenderDist"
, "sodium.MixinRenderManager"
, "sodium.MixinEntityRenderer"
, "sodium.MixinFMLClientHandler"
, "sodium.MixinForgeHooksClient"
, "sodium.MixinGameSettings"
, "sodium.MixinFrustrum"
, "sodium.MixinMinecraft"
, "sodium.MixinRenderBlocks"
, "sodium.MixinRenderGlobal"
, "sodium.MixinWorldClient"
, "sodium.MixinTileEntity"
, "sodium.MixinTileEntityMobSpawner"
, "sodium.MixinEffectRenderer"
, "sodium.MixinTileEntityRendererDispatcher"
, "sodium.MixinLongHashMap"
, "sodium.MixinRenderingRegistry"
, "sodium.MixinPlayerManager"
SODIUM(new Builder("Sodium").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableSodium).addMixinClasses(
"sodium.MixinBlock"
,"sodium.MixinBlockFluidBase"
,"sodium.AccessorBiomeColorEvent"
,"sodium.MixinBiomeGenBase"
,"sodium.MixinChunk"
,"sodium.MixinChunkProviderServer"
,"sodium.MixinClientRegistry"
,"sodium.MixinEntity_RenderDist"
,"sodium.MixinEntityItem_RenderDist"
,"sodium.MixinRenderManager"
,"sodium.MixinExtendedBlockStorage"
,"sodium.MixinEntityRenderer"
,"sodium.MixinFMLClientHandler"
,"sodium.MixinForgeHooksClient"
,"sodium.MixinGameSettings"
,"sodium.MixinFrustrum"
,"sodium.MixinMaterial"
,"sodium.MixinMinecraft"
,"sodium.MixinNibbleArray"
,"sodium.MixinRenderBlocks"
,"sodium.MixinRenderGlobal"
,"sodium.MixinWorldClient"
,"sodium.MixinTileEntity"
,"sodium.MixinEffectRenderer"
,"sodium.MixinTileEntityRendererDispatcher"
,"sodium.MixinLongHashMap"
,"sodium.MixinRenderingRegistry"
,"sodium.MixinPlayerManager"
)
),
IRIS_SHADERS(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins(
"shaders.MixinEntityRenderer"
, "shaders.MixinGuiIngameForge"
, "shaders.MixinFramebuffer"
, "shaders.MixinItem"
, "shaders.MixinLocale"
, "shaders.MixinOpenGlHelper"
, "shaders.MixinRender"
, "shaders.MixinRendererLivingEntity"
, "shaders.MixinRenderGlobal"
, "shaders.MixinTileEntityBeaconRenderer"
IRIS_RENDERING(new Builder("Iris Shaders").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableIris).addMixinClasses(
"shaders.MixinEntityRenderer"
,"shaders.MixinGuiIngameForge"
,"shaders.MixinFramebuffer"
,"shaders.MixinItem"
,"shaders.MixinItemRenderer"
,"shaders.MixinLocale"
,"shaders.MixinOpenGlHelper"
,"shaders.MixinRender"
,"shaders.MixinRendererLivingEntity"
,"shaders.MixinRenderGlobal"
,"shaders.MixinTileEntityBeaconRenderer"
)
),
IRIS_RENDERING_NOBACKHAND(new MixinBuilder("Iris Hand Shaders")
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addExcludedMod(TargetedMod.BACKHAND)
.addClientMixins(
"shaders.MixinItemRenderer"
IRIS_ACCESSORS(new Builder("Iris Accessors").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableIris).addMixinClasses(
"shaders.accessors.MixinMinecraft"
,"shaders.accessors.MixinEntityRenderer"
,"shaders.accessors.MixinSimpleTexture"
,"shaders.accessors.MixinTextureAtlasSprite"
,"shaders.accessors.MixinTextureMap"
,"shaders.accessors.MixinAnimationMetadataSection"
)
),
IRIS_RENDERING_BACKHAND(new MixinBuilder("Iris Hand Shaders (Backhand)")
.addRequiredMod(TargetedMod.BACKHAND)
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableIris)
.addClientMixins(
"shaders.MixinItemRendererBackhand"
)
),
ANGELICA_TEXTURE(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableIris || AngelicaConfig.enableSodium)
.addClientMixins(
"angelica.textures.MixinTextureAtlasSprite"
, "angelica.textures.MixinTextureUtil"
ANGELICA_TEXTURE(new Builder("Textures").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableIris || AngelicaConfig.enableSodium).addMixinClasses(
"angelica.textures.MixinTextureAtlasSprite"
,"angelica.textures.MixinTextureUtil"
)),
ANGELICA_ZOOM(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableZoom)
.addClientMixins(
ANGELICA_ZOOM(new Builder("Zoom").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT)
.setPhase(Phase.EARLY).setApplyIf(() -> AngelicaConfig.enableZoom)
.addMixinClasses(
"angelica.zoom.MixinEntityRenderer_Zoom",
"angelica.zoom.MixinMinecraft_Zoom",
"angelica.zoom.MixinMouseFilter"
)),
HUD_CACHING(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableHudCaching)
.addClientMixins(
"angelica.hudcaching.MixinGuiIngame",
"angelica.hudcaching.MixinGuiIngameForge",
HUD_CACHING(new Builder("Renders the HUD elements 20 times per second maximum to improve performance")
.addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableHudCaching).addMixinClasses(
"angelica.hudcaching.MixinGuiIngame",
"angelica.hudcaching.MixinGuiIngameForge",
"angelica.hudcaching.MixinRenderGameOverlayEvent",
"angelica.hudcaching.MixinEntityRenderer_HUDCaching",
"angelica.hudcaching.MixinFramebuffer_HUDCaching",
"angelica.hudcaching.MixinGuiIngame_HUDCaching",
"angelica.hudcaching.MixinGuiIngameForge_HUDCaching",
"angelica.hudcaching.MixinRenderItem")
),
OPTIMIZE_WORLD_UPDATE_LIGHT(new MixinBuilder()
.setPhase(Phase.EARLY)
.addExcludedMod(TargetedMod.ARCHAICFIX)
.setApplyIf(() -> AngelicaConfig.optimizeWorldUpdateLight)
.addCommonMixins("angelica.lighting.MixinWorld_FixLightUpdateLag")),
OPTIMIZE_WORLD_UPDATE_LIGHT(new Builder("Optimize world updateLightByType method").setPhase(Phase.EARLY)
.setSide(Side.BOTH).addTargetedMod(TargetedMod.VANILLA).addExcludedMod(TargetedMod.ARCHAICFIX).setApplyIf(() -> AngelicaConfig.optimizeWorldUpdateLight)
.addMixinClasses("angelica.lighting.MixinWorld_FixLightUpdateLag")),
SPEEDUP_VANILLA_ANIMATIONS(new MixinBuilder()
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.speedupAnimations)
.addClientMixins(
SPEEDUP_VANILLA_ANIMATIONS(new Builder("Speedup Vanilla Animations").setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.speedupAnimations).setSide(Side.CLIENT).addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(
"angelica.animation.MixinTextureAtlasSprite",
"angelica.animation.MixinTextureMap",
"angelica.animation.MixinBlockFire",
@@ -248,57 +218,38 @@ public enum Mixins implements IMixins {
"angelica.animation.MixinWorldRenderer",
"angelica.animation.MixinRenderItem")),
SCALED_RESOUTION_UNICODE_FIX(new MixinBuilder("Removes unicode languages gui scaling being forced to even values")
.setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.removeUnicodeEvenScaling)
.addClientMixins("angelica.bugfixes.MixinScaledResolution_UnicodeFix")),
SECURITYCRAFT_COMPAT(new MixinBuilder("Fix reflection in SecurityCraft for compat with Angelica")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.SECURITYCRAFT)
.setApplyIf(() -> CompatConfig.fixSecurityCraft)
.addClientMixins(
"client.securitycraft.MixinBlockReinforcedFenceGate",
"client.securitycraft.MixinBlockReinforcedIronBars"
)),
EXTRA_UTILITIES_THREAD_SAFETY(new MixinBuilder("Enable thread safety fixes in Extra Utilities")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.EXTRAUTILS)
EXTRA_UTILITIES_THREAD_SAFETY(new Builder("Enable thread safety fixes in Extra Utilities").setPhase(Phase.LATE)
.addTargetedMod(TargetedMod.EXTRAUTILS).setSide(Side.CLIENT)
.setApplyIf(() -> CompatConfig.fixExtraUtils)
.addClientMixins(
.addMixinClasses(
"client.extrautils.MixinRenderBlockConnectedTextures",
"client.extrautils.MixinRenderBlockConnectedTexturesEthereal",
"client.extrautils.MixinIconConnectedTexture")),
MFR_THREAD_SAFETY(new MixinBuilder("Enable thread safety fixes for MineFactory Reloaded")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.MINEFACTORY_RELOADED)
.setApplyIf(() -> CompatConfig.fixMinefactoryReloaded)
.addClientMixins("client.minefactoryreloaded.MixinRedNetCableRenderer")),
MFR_THREAD_SAFETY(new Builder("Enable thread safety fixes for MineFactory Reloaded").setPhase(Phase.LATE)
.addTargetedMod(TargetedMod.MINEFACTORY_RELOADED).setSide(Side.CLIENT)
.setApplyIf(() -> CompatConfig.fixMinefactoryReloaded)
.addMixinClasses("client.minefactoryreloaded.MixinRedNetCableRenderer")),
SPEEDUP_CAMPFIRE_BACKPORT_ANIMATIONS(new MixinBuilder("Add animation speedup support to Campfire Backport")
.setPhase(Phase.LATE)
.addRequiredMod(TargetedMod.CAMPFIRE_BACKPORT)
.setApplyIf(() -> AngelicaConfig.speedupAnimations)
.addClientMixins("client.campfirebackport.MixinRenderBlockCampfire")),
SPEEDUP_CAMPFIRE_BACKPORT_ANIMATIONS(new Builder("Add animation speedup support to Campfire Backport").setPhase(Phase.LATE)
.addTargetedMod(TargetedMod.CAMPFIRE_BACKPORT).setSide(Side.CLIENT)
.setApplyIf(() -> AngelicaConfig.speedupAnimations)
.addMixinClasses("client.campfirebackport.MixinRenderBlockCampfire")),
IC2_FLUID_RENDER_FIX(new MixinBuilder()
.setPhase(Phase.EARLY)
.addRequiredMod(TargetedMod.IC2)
.setApplyIf(() -> AngelicaConfig.speedupAnimations)
.addClientMixins("angelica.textures.ic2.MixinRenderLiquidCell")),
IC2_FLUID_RENDER_FIX(new Builder("IC2 Fluid Render Fix").setPhase(Phase.EARLY).setSide(Side.CLIENT)
.addTargetedMod(TargetedMod.IC2).setApplyIf(() -> AngelicaConfig.speedupAnimations)
.addMixinClasses("angelica.textures.ic2.MixinRenderLiquidCell")),
OPTIMIZE_TEXTURE_LOADING(new MixinBuilder()
.setPhase(Phase.EARLY)
.addClientMixins("angelica.textures.MixinTextureUtil_OptimizeMipmap")
.setApplyIf(() -> AngelicaConfig.optimizeTextureLoading)),
OPTIMIZE_TEXTURE_LOADING(new Builder("Optimize Texture Loading").setPhase(Phase.EARLY)
.addMixinClasses("angelica.textures.MixinTextureUtil_OptimizeMipmap").addTargetedMod(TargetedMod.VANILLA)
.setApplyIf(() -> AngelicaConfig.optimizeTextureLoading).setSide(Side.CLIENT)),
//From NotFine
NOTFINE_BASE_MOD(new MixinBuilder()
.setPhase(Phase.EARLY)
NOTFINE_BASE_MOD(new Builder("NotFine")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableNotFineFeatures)
.addClientMixins(addPrefix("notfine.",
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("notfine.",
"clouds.MixinEntityRenderer",
"clouds.MixinGameSettings",
//"clouds.MixinRenderGlobal",
@@ -330,10 +281,11 @@ public enum Mixins implements IMixins {
"interpolatedtexturemap.MixinTextureMap"
))
),
BETTER_FACE_CULLING(new MixinBuilder()
.setPhase(Phase.EARLY)
BETTER_FACE_CULLING(new Builder("Better face culling")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> NotFineConfig.betterBlockFaceCulling)
.addClientMixins(addPrefix("notfine.faceculling.",
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("notfine.faceculling.",
"MixinBlock",
"MixinBlockCactus",
"MixinBlockCarpet",
@@ -345,94 +297,103 @@ public enum Mixins implements IMixins {
"MixinRenderBlocks"
))
),
NOTFINE_NO_DYNAMIC_SURROUNDINGS(new MixinBuilder()
.setPhase(Phase.EARLY)
NOTFINE_NO_DYNAMIC_SURROUNDINGS(new Builder("NotFine no Dynamic Surroundings")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> true)
.addTargetedMod(TargetedMod.VANILLA)
.addExcludedMod(TargetedMod.DYNAMIC_SURROUNDINGS_MIST)
.addExcludedMod(TargetedMod.DYNAMIC_SURROUNDINGS_ORIGINAL)
.addClientMixins("notfine.toggle.MixinEntityRenderer$RenderRainSnow")
.addMixinClasses("notfine.toggle.MixinEntityRenderer$RenderRainSnow")
),
NOTFINE_NO_CUSTOM_ITEM_TEXTURES(new MixinBuilder()
.setPhase(Phase.EARLY)
NOTFINE_NO_CUSTOM_ITEM_TEXTURES(new Builder("NotFine no Custom Item Textures")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> !AngelicaConfig.enableMCPatcherForgeFeatures || !MCPatcherForgeConfig.CustomItemTextures.enabled)
.addClientMixins(addPrefix("notfine.glint.",
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("notfine.glint.",
"MixinItemRenderer",
"MixinRenderItem"
))
),
NOTFINE_NATURA_COMPAT(new MixinBuilder()
.setPhase(Phase.LATE)
NOTFINE_NATURA(new Builder("NotFine Natura compat")
.setSide(Side.CLIENT).setPhase(Phase.LATE)
.setApplyIf(() -> AngelicaConfig.enableNotFineFeatures)
.addRequiredMod(TargetedMod.NATURA)
.addClientMixins(addPrefix("notfine.leaves.natura.",
.addTargetedMod(TargetedMod.NATURA)
.addMixinClasses(addPrefix("notfine.leaves.natura.",
"MixinBerryBush",
"MixinNetherBerryBush"
))
),
NOTFINE_THAUMCRAFT_COMPAT(new MixinBuilder()
.setPhase(Phase.LATE)
NOTFINE_THAUMCRAFT(new Builder("NotFine Thaumcraft compat")
.setSide(Side.CLIENT).setPhase(Phase.LATE)
.setApplyIf(() -> AngelicaConfig.enableNotFineFeatures)
.addRequiredMod(TargetedMod.THAUMCRAFT)
.addClientMixins("notfine.leaves.thaumcraft.MixinBlockMagicalLeaves")
.addTargetedMod(TargetedMod.THAUMCRAFT)
.addMixinClasses("notfine.leaves.thaumcraft.MixinBlockMagicalLeaves")
),
THAUMCRAFT_BETTER_FACE_CULLING(new MixinBuilder()
.setPhase(Phase.LATE)
THAUMCRAFT_BETTER_FACE_CULLING(new Builder("Better face culling Thaumcraft compat")
.setSide(Side.CLIENT).setPhase(Phase.LATE)
.setApplyIf(() -> NotFineConfig.betterBlockFaceCulling)
.addRequiredMod(TargetedMod.THAUMCRAFT)
.addClientMixins(addPrefix("notfine.faceculling.thaumcraft.",
.addTargetedMod(TargetedMod.THAUMCRAFT)
.addMixinClasses(addPrefix("notfine.faceculling.thaumcraft.",
"MixinBlockWoodenDevice",
"MixinBlockStoneDevice",
"MixinBlockTable"
))
),
NOTFINE_TINKERS_CONSTRUCT_COMPAT(new MixinBuilder()
.setPhase(Phase.LATE)
NOTFINE_TINKERS_CONSTRUCT(new Builder("NotFine Tinker's Construct compat")
.setSide(Side.CLIENT).setPhase(Phase.LATE)
.setApplyIf(() -> AngelicaConfig.enableNotFineFeatures)
.addRequiredMod(TargetedMod.TINKERS_CONSTRUCT)
.addClientMixins("notfine.leaves.tconstruct.MixinOreberryBush")
.addTargetedMod(TargetedMod.TINKERS_CONSTRUCT)
.addMixinClasses("notfine.leaves.tconstruct.MixinOreberryBush")
),
NOTFINE_WITCHERY_COMPAT(new MixinBuilder()
.setPhase(Phase.LATE)
NOTFINE_WITCHERY(new Builder("NotFine Witchery compat")
.setSide(Side.CLIENT).setPhase(Phase.LATE)
.setApplyIf(() -> AngelicaConfig.enableNotFineFeatures)
.addRequiredMod(TargetedMod.WITCHERY)
.addClientMixins("notfine.leaves.witchery.MixinBlockWitchLeaves")
.addTargetedMod(TargetedMod.WITCHERY)
.addMixinClasses("notfine.leaves.witchery.MixinBlockWitchLeaves")
),
NOTFINE_TWILIGHT_FOREST_COMPAT(new MixinBuilder()
.setPhase(Phase.LATE)
NOTFINE_TWILIGHT_FOREST(new Builder("NotFine Twilight Forest compat")
.setSide(Side.CLIENT).setPhase(Phase.LATE)
.setApplyIf(() -> AngelicaConfig.enableNotFineFeatures)
.addRequiredMod(TargetedMod.TWILIGHT_FOREST)
.addClientMixins(addPrefix("notfine.leaves.twilightforest.",
.addTargetedMod(TargetedMod.TWILIGHT_FOREST)
.addMixinClasses(addPrefix("notfine.leaves.twilightforest.",
"MixinBlockTFLeaves",
"MixinBlockTFLeaves3",
// TODO: Verify 2.3.8.18 or later to support non NH builds?
"MixinBlockTFMagicLeaves"
))
),
MCPATCHER_FORGE(new MixinBuilder()
.setPhase(Phase.EARLY)
MCPATCHER_FORGE(new Builder("MCPatcher Forge")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures)
.addClientMixins(addPrefix("mcpatcherforge.",
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.",
"base.MixinBlockGrass",
"base.MixinBlockMycelium",
"base.MixinAbstractTexture",
"base.MixinTextureAtlasSprite",
"base.MixinSimpleReloadableResourceManager",
"base.MixinMinecraft"
))
),
MCPATCHER_FORGE_RENDERPASS(new MixinBuilder()
MCPATCHER_FORGE_RENDERPASS(new Builder("MCPatcher Forge Renderpass")
.setPhase(Phase.EARLY)
.setApplyIf(() -> NotFineConfig.renderPass)
.addClientMixins(addPrefix("mcpatcherforge.",
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.",
"renderpass.MixinEntityRenderer",
"renderpass.MixinRenderBlocks",
"renderpass.MixinRenderGlobal",
"renderpass.MixinWorldRenderer"
))
),
MCPATCHER_FORGE_CUSTOM_COLORS(new MixinBuilder()
.setPhase(Phase.EARLY)
MCPATCHER_FORGE_CUSTOM_COLORS(new Builder("MCP:F Custom Colors")
.setSide(Mixins.Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.CustomColors.enabled)
.addClientMixins(addPrefix("mcpatcherforge.cc.",
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.cc.",
"block.material.MixinMapColor",
"block.MixinBlock",
@@ -484,10 +445,11 @@ public enum Mixins implements IMixins {
"world.MixinWorldProviderHell"
))
),
MCPATCHER_FORGE_CUSTOM_ITEM_TEXTURES(new MixinBuilder()
.setPhase(Phase.EARLY)
MCPATCHER_FORGE_CUSTOM_ITEM_TEXTURES(new Builder("MCP:F Custom Item Textures")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.CustomItemTextures.enabled)
.addClientMixins(addPrefix("mcpatcherforge.cit.",
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.cit.",
"client.renderer.entity.MixinRenderBiped",
"client.renderer.entity.MixinRenderEntityLiving",
"client.renderer.entity.MixinRenderItem",
@@ -502,30 +464,34 @@ public enum Mixins implements IMixins {
"world.MixinWorld"
))
),
MCPATCHER_FORGE_CONNECTED_TEXTURES(new MixinBuilder()
.setPhase(Phase.EARLY)
MCPATCHER_FORGE_CONNECTED_TEXTURES(new Builder("MCP:F Connected Textures")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.ConnectedTextures.enabled)
.addClientMixins("mcpatcherforge.ctm.MixinRenderBlocks")
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses("mcpatcherforge.ctm.MixinRenderBlocks")
),
MCPATCHER_FORGE_EXTENDED_HD(new MixinBuilder()
.setPhase(Phase.EARLY)
MCPATCHER_FORGE_EXTENDED_HD(new Builder("MCP:F Extended hd")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.ExtendedHD.enabled)
.addClientMixins(addPrefix("mcpatcherforge.hd.",
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.hd.",
"MixinTextureClock",
"MixinTextureCompass",
"MixinTextureManager"
))
),
MCPATCHER_FORGE_EXTENDED_HD_FONT(new MixinBuilder()
.setPhase(Phase.EARLY)
MCPATCHER_FORGE_EXTENDED_HD_FONT(new Builder("MCP:F Extended HD Font")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> (AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.ExtendedHD.enabled && MCPatcherForgeConfig.ExtendedHD.hdFont))
.addTargetedMod(TargetedMod.VANILLA)
.addExcludedMod(TargetedMod.COFHCORE)
.addClientMixins("mcpatcherforge.hd.MixinFontRenderer")
.addMixinClasses("mcpatcherforge.hd.MixinFontRenderer")
),
MCPATCHER_FORGE_RANDOM_MOBS(new MixinBuilder()
.setPhase(Phase.EARLY)
MCPATCHER_FORGE_RANDOM_MOBS(new Builder("MCP:F Random Mobs")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.RandomMobs.enabled)
.addClientMixins(addPrefix("mcpatcherforge.mob.",
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.mob.",
"MixinRender",
"MixinRenderEnderman",
"MixinRenderFish",
@@ -538,59 +504,220 @@ public enum Mixins implements IMixins {
"MixinEntityLivingBase"
))
),
MCPATCHER_FORGE_SKY(new MixinBuilder()
.setPhase(Phase.EARLY)
MCPATCHER_FORGE_SKY(new Builder("MCP:F Sky")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures && MCPatcherForgeConfig.BetterSkies.enabled)
.addClientMixins(addPrefix("mcpatcherforge.sky.",
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(addPrefix("mcpatcherforge.sky.",
"MixinEffectRenderer",
"MixinRenderGlobal"
))
),
MCPATCHER_FORGE_CC_NO_CTM(new MixinBuilder("MCP:F Custom Colors, no Connected Textures")
.setPhase(Phase.EARLY)
MCPATCHER_FORGE_CC_NO_CTM(new Builder("MCP:F Custom Colors, no Connected Textures")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures
&& !MCPatcherForgeConfig.ConnectedTextures.enabled
&& MCPatcherForgeConfig.CustomColors.enabled)
.addClientMixins("mcpatcherforge.ctm_cc.MixinRenderBlocksNoCTM")
&& !MCPatcherForgeConfig.ConnectedTextures.enabled
&& MCPatcherForgeConfig.CustomColors.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses("mcpatcherforge.cc_ctm.MixinRenderBlocksNoCTM")
),
MCPATCHER_FORGE_CTM_NO_CC(new MixinBuilder("MCP:F Connected Textures, no Custom Colours")
.setPhase(Phase.EARLY)
MCPATCHER_FORGE_CTM_NO_CC(new Builder("MCP:F Connected Textures, no Custom Colours")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures
&& MCPatcherForgeConfig.ConnectedTextures.enabled
&& !MCPatcherForgeConfig.CustomColors.enabled)
.addClientMixins("mcpatcherforge.ctm_cc.MixinRenderBlocksNoCC")
&& MCPatcherForgeConfig.ConnectedTextures.enabled
&& !MCPatcherForgeConfig.CustomColors.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses("mcpatcherforge.ctm_cc.MixinRenderBlocksNoCC")
),
MCPATCHER_FORGE_CTM_AND_CC(new MixinBuilder("MCP:F Connected Textures and Custom Colors")
.setPhase(Phase.EARLY)
MCPATCHER_FORGE_CTM_AND_CC(new Builder("MCP:F Connected Textures and Custom Colors")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures
&& MCPatcherForgeConfig.ConnectedTextures.enabled
&& MCPatcherForgeConfig.CustomColors.enabled)
.addClientMixins("mcpatcherforge.ctm_cc.MixinRenderBlocks")
&& MCPatcherForgeConfig.ConnectedTextures.enabled
&& MCPatcherForgeConfig.CustomColors.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses("mcpatcherforge.ctm_cc.MixinRenderBlocks")
),
MCPATCHER_FORGE_CTM_OR_CC(new MixinBuilder("MCP:F Connected Textures or Custom Colors")
.setPhase(Phase.EARLY)
MCPATCHER_FORGE_CTM_OR_CC(new Builder("MCP:F Connected Textures or Custom Colors")
.setSide(Side.CLIENT).setPhase(Phase.EARLY)
.setApplyIf(() -> AngelicaConfig.enableMCPatcherForgeFeatures
&& MCPatcherForgeConfig.ConnectedTextures.enabled
|| MCPatcherForgeConfig.CustomColors.enabled)
.addClientMixins("mcpatcherforge.ctm_cc.MixinTextureMap")
&& MCPatcherForgeConfig.ConnectedTextures.enabled
|| MCPatcherForgeConfig.CustomColors.enabled)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses("mcpatcherforge.ctm_cc.MixinTextureMap")
),
//End from NotFine
QPR(new MixinBuilder("Adds a QuadProvider field to blocks without populating it")
QPR(new Builder("Adds a QuadProvider field to blocks without populating it")
.setSide(Side.CLIENT)
.setPhase(Phase.EARLY)
.addClientMixins(
.setApplyIf(() -> true)
.addTargetedMod(TargetedMod.VANILLA)
.addMixinClasses(
"angelica.models.MixinBlock",
"angelica.models.MixinBlockOldLeaf")),
;
private final MixinBuilder builder;
private final List<String> mixinClasses;
private final Supplier<Boolean> applyIf;
private final Phase phase;
private final Side side;
private final List<TargetedMod> targetedMods;
private final List<TargetedMod> excludedMods;
private static String[] addPrefix(String prefix, String... values) {
List<String> list = new ArrayList<>(values.length);
for (String s : values) {
list.add(prefix + s);
Mixins(Builder builder) {
this.mixinClasses = builder.mixinClasses;
this.applyIf = builder.applyIf;
this.side = builder.side;
this.targetedMods = builder.targetedMods;
this.excludedMods = builder.excludedMods;
this.phase = builder.phase;
if (this.targetedMods.isEmpty()) {
throw new RuntimeException("No targeted mods specified for " + this.name());
}
return list.toArray(new String[values.length]);
if (this.applyIf == null) {
throw new RuntimeException("No ApplyIf function specified for " + this.name());
}
}
public static List<String> getEarlyMixins(Set<String> loadedCoreMods) {
NotFineConfig.loadSettings();
//This may be possible to handle differently or fix.
if(loadedCoreMods.contains("cofh.asm.LoadingPlugin")) {
MCPatcherForgeConfig.ExtendedHD.hdFont = false;
}
final List<String> mixins = new ArrayList<>();
final List<String> notLoading = new ArrayList<>();
for (Mixins mixin : Mixins.values()) {
if (mixin.phase == Mixins.Phase.EARLY) {
if (mixin.shouldLoad(loadedCoreMods, Collections.emptySet())) {
mixins.addAll(mixin.mixinClasses);
} else {
notLoading.addAll(mixin.mixinClasses);
}
}
}
AngelicaTweaker.LOGGER.info("Not loading the following EARLY mixins: {}", notLoading);
return mixins;
}
public static List<String> getLateMixins(Set<String> loadedMods) {
final List<String> mixins = new ArrayList<>();
final List<String> notLoading = new ArrayList<>();
for (Mixins mixin : Mixins.values()) {
if (mixin.phase == Mixins.Phase.LATE) {
if (mixin.shouldLoad(Collections.emptySet(), loadedMods)) {
mixins.addAll(mixin.mixinClasses);
} else {
notLoading.addAll(mixin.mixinClasses);
}
}
}
AngelicaTweaker.LOGGER.info("Not loading the following LATE mixins: {}", notLoading.toString());
return mixins;
}
private boolean shouldLoadSide() {
return side == Side.BOTH || (side == Side.SERVER && FMLLaunchHandler.side().isServer())
|| (side == Side.CLIENT && FMLLaunchHandler.side().isClient());
}
private boolean allModsLoaded(List<TargetedMod> targetedMods, Set<String> loadedCoreMods, Set<String> loadedMods) {
if (targetedMods.isEmpty()) return false;
for (TargetedMod target : targetedMods) {
if (target == TargetedMod.VANILLA) continue;
// Check coremod first
if (!loadedCoreMods.isEmpty() && target.coreModClass != null
&& !loadedCoreMods.contains(target.coreModClass))
return false;
else if (!loadedMods.isEmpty() && target.modId != null && !loadedMods.contains(target.modId)) return false;
}
return true;
}
private boolean noModsLoaded(List<TargetedMod> targetedMods, Set<String> loadedCoreMods, Set<String> loadedMods) {
if (targetedMods.isEmpty()) return true;
for (TargetedMod target : targetedMods) {
if (target == TargetedMod.VANILLA) continue;
// Check coremod first
if (!loadedCoreMods.isEmpty() && target.coreModClass != null
&& loadedCoreMods.contains(target.coreModClass))
return false;
else if (!loadedMods.isEmpty() && target.modId != null && loadedMods.contains(target.modId)) return false;
}
return true;
}
private boolean shouldLoad(Set<String> loadedCoreMods, Set<String> loadedMods) {
return (shouldLoadSide() && applyIf.get()
&& allModsLoaded(targetedMods, loadedCoreMods, loadedMods)
&& noModsLoaded(excludedMods, loadedCoreMods, loadedMods));
}
private static class Builder {
private final List<String> mixinClasses = new ArrayList<>();
private Supplier<Boolean> applyIf = () -> true;
private Side side = Side.BOTH;
private Phase phase = Phase.LATE;
private final List<TargetedMod> targetedMods = new ArrayList<>();
private final List<TargetedMod> excludedMods = new ArrayList<>();
public Builder(@SuppressWarnings("unused") String description) {}
public Builder addMixinClasses(String... mixinClasses) {
this.mixinClasses.addAll(Arrays.asList(mixinClasses));
return this;
}
public Builder setPhase(Phase phase) {
this.phase = phase;
return this;
}
public Builder setSide(Side side) {
this.side = side;
return this;
}
public Builder setApplyIf(Supplier<Boolean> applyIf) {
this.applyIf = applyIf;
return this;
}
public Builder addTargetedMod(TargetedMod mod) {
this.targetedMods.add(mod);
return this;
}
public Builder addExcludedMod(TargetedMod mod) {
this.excludedMods.add(mod);
return this;
}
}
@SuppressWarnings("SimplifyStreamApiCallChains")
private static String[] addPrefix(String prefix, String... values) {
return Arrays.stream(values)
.map(s -> prefix + s)
.collect(Collectors.toList())
.toArray(new String[values.length]);
}
private enum Side {
BOTH,
CLIENT,
SERVER
}
private enum Phase {
EARLY,
LATE,
}
}
@@ -1,31 +1,59 @@
package com.gtnewhorizons.angelica.mixins;
import com.gtnewhorizon.gtnhmixins.builders.ITargetMod;
import com.gtnewhorizon.gtnhmixins.builders.TargetModBuilder;
import lombok.Getter;
@Getter
public enum TargetedMod implements ITargetMod {
public enum TargetedMod {
ARCHAICFIX("org.embeddedt.archaicfix.ArchaicCore", "archaicfix"),
BACKHAND("xonin.backhand.coremod.BackhandLoadingPlugin", "backhand"),
CAMPFIRE_BACKPORT(null, "campfirebackport"),
COFHCORE( "cofh.asm.LoadingPlugin", "CoFHCore"),
DYNAMIC_SURROUNDINGS_MIST("org.blockartistry.mod.DynSurround.mixinplugin.DynamicSurroundingsEarlyMixins", "dsurround"),
DYNAMIC_SURROUNDINGS_ORIGINAL("org.blockartistry.mod.DynSurround.asm.TransformLoader", "dsurround"),
EXTRAUTILS(null, "ExtraUtilities"),
MINEFACTORY_RELOADED(null, "MineFactoryReloaded"),
IC2("ic2.core.coremod.IC2core", "IC2"),
NATURA(null, "Natura"),
SECURITYCRAFT(null, "securitycraft"),
THAUMCRAFT(null, "Thaumcraft"),
TINKERS_CONSTRUCT(null, "TConstruct"),
TWILIGHT_FOREST(null, "TwilightForest"),
WITCHERY(null, "witchery");
VANILLA("Minecraft", null)
, FASTCRAFT("FastCraft", "fastcraft.Tweaker")
, OPTIFINE("Optifine", "optifine.OptiFineForgeTweaker", "Optifine")
, ARCHAICFIX("ArchaicFix", "org.embeddedt.archaicfix.ArchaicCore", "archaicfix")
, BOTANIA("Botania", null, "Botania")
, CAMPFIRE_BACKPORT("CampfireBackport", null, "campfirebackport")
, CHICKENCHUNKS("ChickenChunks", null, "ChickenChunks")
, COFHCORE("CoFHCore", "cofh.asm.LoadingPlugin", "CoFHCore")
, DYNAMIC_SURROUNDINGS_MIST("Dynamic Surroundings", "org.blockartistry.mod.DynSurround.mixinplugin.DynamicSurroundingsEarlyMixins", "dsurround")
, DYNAMIC_SURROUNDINGS_ORIGINAL("Dynamic Surroundings", "org.blockartistry.mod.DynSurround.asm.TransformLoader", "dsurround")
, EXTRAUTILS("ExtraUtilities", null, "ExtraUtilities")
, MINEFACTORY_RELOADED("MineFactory Reloaded", null, "MineFactoryReloaded")
, GTNHLIB("GTNHLib", "com.gtnewhorizon.gtnhlib.core.GTNHLibCore", "gtnhlib")
, IC2("IC2", "ic2.core.coremod.IC2core", "IC2")
, JABBA("JABBA", null, "JABBA")
, JOURNEYMAP("JourneyMap", null, "journeymap")
, LWJGL3IFY("lwjgl3ify", "me.eigenraven.lwjgl3ify.core.Lwjgl3ifyCoremod", "lwjgl3ify")
, MRTJPCORE("MrTJPCore", null, "MrTJPCoreMod")
, NATURA("Natura", null, "Natura")
, PROJECTRED_ILLUMINATION("ProjectRed Illumination", null, "ProjRed|Illumination")
, SMART_RENDER("Smart Render", null, "SmartRender")
, THAUMCRAFT("Thaumcraft", null, "Thaumcraft")
, TINKERS_CONSTRUCT("Tinker's Construct", null, "TConstruct")
, TWILIGHT_FOREST("TwilightForest", null, "TwilightForest")
, WITCHERY("Witchery", null, "witchery")
;
private final TargetModBuilder builder;
TargetedMod(String coreModClass, String modId) {
this.builder = new TargetModBuilder().setCoreModClass(coreModClass).setModId(modId);
/** The "name" in the @Mod annotation */
@Getter
public final String modName;
/** Class that implements the IFMLLoadingPlugin interface */
@Getter
public final String coreModClass;
/** The "modid" in the @Mod annotation */
@Getter
public final String modId;
TargetedMod(String modName, String coreModClass) {
this(modName, coreModClass, null);
}
TargetedMod(String modName, String coreModClass, String modId) {
this.modName = modName;
this.coreModClass = coreModClass;
this.modId = modId;
}
@Override
public String toString() {
return "TargetedMod{modName='" + modName + "', coreModClass='" + coreModClass + "', modId='" + modId + "'}";
}
}
@@ -0,0 +1,14 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
public interface AnimationMetadataSectionAccessor {
int getFrameWidth();
void setFrameWidth(int frameWidth);
int getFrameHeight();
void setFrameHeight(int frameHeight);
}
@@ -0,0 +1,9 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
import org.spongepowered.asm.mixin.gen.Accessor;
public interface ExtendedBlockStorageAccessor {
int getYBase();
int getBlockRefCount();
void setBlockRefCount(int blockRefCount);
}
@@ -0,0 +1,7 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
public interface ExtendedNibbleArray {
public byte[] getData();
public int getDepthBits();
public int getDepthBitsPlusFour();
}
@@ -8,5 +8,4 @@ public interface ITexturesCache {
Set<IIcon> getRenderedTextures();
void enableTextureTracking();
void track(IPatchedTextureAtlasSprite sprite);
}
@@ -0,0 +1,7 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
import net.minecraft.util.Timer;
public interface MinecraftAccessor {
Timer getTimer();
}
@@ -1,7 +0,0 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
import java.util.List;
public interface ResourceAccessor {
List angelica$getDefaultResourcePacks();
}
@@ -0,0 +1,7 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
import net.minecraft.util.ResourceLocation;
public interface SimpleTextureAccessor {
ResourceLocation getLocation();
}
@@ -0,0 +1,21 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
import net.minecraft.client.resources.data.AnimationMetadataSection;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.List;
public interface TextureAtlasSpriteAccessor {
AnimationMetadataSection getMetadata();
List<int[][]> getFramesTextureData();
int getFrame();
void setFrame(int frame);
int getSubFrame();
void setSubFrame(int subFrame);
}
@@ -0,0 +1,16 @@
package com.gtnewhorizons.angelica.mixins.interfaces;
import net.minecraft.util.ResourceLocation;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Map;
public interface TextureMapAccessor {
Map getMapUploadedSprites();
int getAnisotropicFiltering();
int getMipmapLevels();
ResourceLocation getLocationBlocksTexture();
}
@@ -1,5 +1,6 @@
package com.gtnewhorizons.angelica.models;
import com.gtnewhorizon.gtnhlib.blockpos.BlockPos;
import com.gtnewhorizon.gtnhlib.blockpos.IBlockPos;
import com.gtnewhorizon.gtnhlib.client.model.ModelLoader;
import com.gtnewhorizon.gtnhlib.client.model.Variant;
@@ -47,7 +47,6 @@ import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.Direction;
@@ -153,7 +152,7 @@ public class ClientProxy extends CommonProxy {
@SubscribeEvent
public void onKeypress(TickEvent.ClientTickEvent event) {
final boolean isPressed = glsmKeyBinding.getKeyCode() != 0 && GameSettings.isKeyDown(glsmKeyBinding);
final boolean isPressed = glsmKeyBinding.getKeyCode() != 0 && Keyboard.isKeyDown(glsmKeyBinding.getKeyCode());
if (isPressed && !wasGLSMKeyPressed) {
OpenGLDebugging.checkGLSM();
}
@@ -308,14 +307,9 @@ public class ClientProxy extends CommonProxy {
@SubscribeEvent(priority = EventPriority.LOWEST)
public void onGuiOpen(GuiOpenEvent event) {
if (!event.isCanceled()) {
if (event.gui instanceof GuiMainMenu && gameStartTime == -1 ) {
gameStartTime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
LOGGER.info("The game loaded in {} seconds.", gameStartTime);
}
// force reset zoom when a GUI is opened
if (AngelicaConfig.enableZoom && event.gui != null) Zoom.resetZoom();
if (!event.isCanceled() && event.gui instanceof GuiMainMenu && gameStartTime == -1) {
gameStartTime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
LOGGER.info("The game loaded in {} seconds.", gameStartTime);
}
}
@@ -242,8 +242,7 @@ public class CloudRenderer implements IResourceManagerReloadListener {
GraphicsQualityOff cloudGraphicsQuality = (GraphicsQualityOff)Settings.MODE_CLOUDS.option.getStore();
final int cloudQualitySetting = cloudGraphicsQuality == GraphicsQualityOff.FANCY
|| cloudGraphicsQuality == GraphicsQualityOff.DEFAULT && mc.gameSettings.fancyGraphics ? 2 : 1;
final boolean newEnabled = cloudGraphicsQuality != GraphicsQualityOff.OFF
&& mc.gameSettings.shouldRenderClouds()
final boolean newEnabled = mc.gameSettings.shouldRenderClouds()
&& mc.theWorld != null
&& mc.theWorld.provider.isSurfaceWorld();
final int targetDistance = Math.max(mc.gameSettings.renderDistanceChunks, (int)Settings.RENDER_DISTANCE_CLOUDS.option.getStore());
@@ -25,161 +25,87 @@
package com.gtnewhorizons.angelica.rendering;
import com.gtnewhorizon.gtnhlib.client.renderer.CapturingTessellator;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizon.gtnhlib.client.renderer.quad.QuadView;
import com.gtnewhorizon.gtnhlib.client.renderer.vbo.VertexBuffer;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import it.unimi.dsi.fastutil.HashCommon;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.minecraft.client.Minecraft;
import org.lwjgl.BufferUtils;
import lombok.val;
import org.lwjgl.opengl.GL11;
import java.nio.ByteBuffer;
import net.minecraft.client.renderer.GLAllocation;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.IResourceManagerReloadListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ItemRenderListManager {
// Least used element is at position 0. This is in theory slightly faster.
private static final Object2ObjectLinkedOpenHashMap<ItemProp, CachedVBO> vboCache = new Object2ObjectLinkedOpenHashMap<>(64);
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ItemRenderListManager implements IResourceManagerReloadListener {
public static final ItemRenderListManager INSTANCE = new ItemRenderListManager();
// Formula: (widthSubdivisions * 2 + heightSubdivisions * 2 + 2) * 4 * vertexSize
// Using 256 as both variables due to enchants, to prevent later re-allocations.
private static ByteBuffer quadBuffer = BufferUtils.createByteBuffer(
(1026 * DefaultVertexFormat.POSITION_TEXTURE_NORMAL.getVertexSize()) << 2
);
private final Map<ItemProp, Integer> theMap = new HashMap<>();
private final List<ItemProp> propList = new ArrayList<>();
private final ItemProp prop = new ItemProp();
private int list = 0;
// 1 minute
private static final int EXPIRY_TICKS = 1_200;
private static int smallestExpiry;
private static final ItemProp prop = new ItemProp();
public static VertexBuffer pre(float minU, float minV, float maxU, float maxV, int widthSubdivisions, int heightSubdivisions, float thickness) {
prop.set(minU, minV, maxU, maxV, widthSubdivisions, heightSubdivisions, thickness);
if (!vboCache.isEmpty()) {
final CachedVBO vbo = vboCache.getAndMoveToLast(prop);
if (vbo != null) {
final int time = getElapsedTicks();
vbo.render(time);
// Prevent constant map lookups by only storing the expiry of the least recently used entry
if (time > smallestExpiry && time > (smallestExpiry = (vboCache.get(vboCache.firstKey()).expiry + 20))) {
vboCache.removeFirst().delete();
if (!vboCache.isEmpty()) {
smallestExpiry = vboCache.get(vboCache.firstKey()).expiry + 20;
}
}
return null;
}
}
final CachedVBO vbo;
if (vboCache.size() >= AngelicaConfig.itemRendererCacheSize) {
final ItemProp oldestProp = vboCache.firstKey();
vbo = vboCache.removeFirst();
oldestProp.set(prop);
vboCache.put(oldestProp, vbo);
public boolean pre(float a, float b, float c, float d, int e, int f, float g) {
prop.set(a, b, c, d, e, f, g);
if (theMap.containsKey(prop)) {
val list = theMap.get(prop);
propList.add(propList.remove(propList.indexOf(prop)));
GL11.glCallList(list);
return true;
} else {
vbo = new CachedVBO();
vboCache.put(new ItemProp(prop), vbo);
if (propList.size() >= AngelicaConfig.itemRendererDisplayListCacheSize) {
val oldProp = propList.remove(0);
GLAllocation.deleteDisplayLists(theMap.remove(oldProp));
}
list = GLAllocation.generateDisplayLists(1);
val newProp = new ItemProp(prop);
theMap.put(newProp, list);
propList.add(newProp);
GL11.glNewList(list, GL11.GL_COMPILE);
return false;
}
vbo.expiry = getElapsedTicks() + EXPIRY_TICKS;
TessellatorManager.startCapturing();
return vbo.vertexBuffer;
}
public static void post(CapturingTessellator tessellator, VertexBuffer vbo) {
final List<QuadView> quads = TessellatorManager.stopCapturingToPooledQuads();
final int size = quads.size();
final int needed = (DefaultVertexFormat.POSITION_TEXTURE_NORMAL.getVertexSize() * size) << 2;
if (quadBuffer.capacity() < needed) {
quadBuffer = BufferUtils.createByteBuffer(HashCommon.nextPowerOfTwo(needed));
}
for (int i = 0; i < size; i++) {
DefaultVertexFormat.POSITION_TEXTURE_NORMAL.writeQuad(quads.get(i), quadBuffer);
}
quadBuffer.flip();
vbo.upload(quadBuffer);
quadBuffer.clear();
tessellator.clearQuads();
vbo.render();
public void post() {
GL11.glEndList();
GL11.glCallList(list);
}
private static int getElapsedTicks() {
return Minecraft.getMinecraft().thePlayer.ticksExisted;
}
private static final class CachedVBO {
private VertexBuffer vertexBuffer;
private int expiry;
public CachedVBO() {
this.vertexBuffer = new VertexBuffer(DefaultVertexFormat.POSITION_TEXTURE_NORMAL, GL11.GL_QUADS);
}
private void render(int elapsedTicks) {
vertexBuffer.render();
expiry = elapsedTicks + EXPIRY_TICKS;
}
private void delete() {
vertexBuffer.close();
vertexBuffer = null;
}
@Override
public void onResourceManagerReload(IResourceManager p_110549_1_) {
propList.clear();
theMap.forEach((key, value) -> GLAllocation.deleteDisplayLists(value));
theMap.clear();
}
@NoArgsConstructor
@Data
private static final class ItemProp {
private float minU;
private float minV;
private float maxU;
private float maxV;
private int widthSubdivisions;
private int heightSubdivisions;
private float thickness;
public class ItemProp {
private float a;
private float b;
private float c;
private float d;
private int e;
private int f;
private float g;
public ItemProp(ItemProp old) {
set(
old.minU, old.minV,
old.maxU, old.maxV,
old.widthSubdivisions, old.heightSubdivisions,
old.thickness
);
set(old.a, old.b, old.c, old.d, old.e, old.f, old.g);
}
public void set(ItemProp other) {
set(
other.minU, other.minV,
other.maxU, other.maxV,
other.widthSubdivisions, other.heightSubdivisions,
other.thickness
);
}
public void set(float minU, float minV, float maxU, float maxV, int widthSubdivisions, int heightSubdivisions, float thickness) {
this.minU = minU;
this.minV = minV;
this.maxU = maxU;
this.maxV = maxV;
this.widthSubdivisions = widthSubdivisions;
this.heightSubdivisions = heightSubdivisions;
this.thickness = thickness;
public void set(float a, float b, float c, float d, int e, int f, float g) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.e = e;
this.f = f;
this.g = g;
}
}
}
@@ -1,10 +1,12 @@
package com.gtnewhorizons.angelica.rendering;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import lombok.Getter;
import lombok.Setter;
import org.joml.Matrix4f;
import org.joml.Vector3d;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import java.nio.FloatBuffer;
@@ -0,0 +1,13 @@
package com.gtnewhorizons.angelica.transform;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
public class AsmUtil {
public static String obf(String deobf, String obf) {
if (AngelicaTweaker.isObfEnv()) {
return obf;
}
return deobf;
}
}
@@ -1,8 +1,7 @@
package com.gtnewhorizons.angelica.loading.fml.transformers;
package com.gtnewhorizons.angelica.transform;
import com.google.common.collect.ImmutableList;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import com.gtnewhorizons.angelica.loading.shared.transformers.AngelicaRedirector;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import net.minecraft.launchwrapper.IClassTransformer;
import org.apache.commons.lang3.tuple.Pair;
import org.objectweb.asm.ClassReader;
@@ -11,9 +10,10 @@ import org.objectweb.asm.tree.ClassNode;
import java.util.List;
public class SodiumBlockTransformer implements IClassTransformer {
public class BlockTransformer implements IClassTransformer {
private static final List<Pair<String, String>> BlockBoundsFields = ImmutableList.of(
private static final String BlockClassFriendly = "net.minecraft.block.Block";
public static final List<Pair<String, String>> BlockBoundsFields = ImmutableList.of(
Pair.of("minX", "field_149759_B"),
Pair.of("minY", "field_149760_C"),
Pair.of("minZ", "field_149754_D"),
@@ -23,13 +23,12 @@ public class SodiumBlockTransformer implements IClassTransformer {
);
/**
* Delete the global vanilla bounding box fields off the Block object. {@link AngelicaRedirector}
* Delete the global vanilla bounding box fields off the Block object. {@link RedirectorTransformer}
* replaces these with a thread-safe alternative.
*/
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
if ("net.minecraft.block.Block".equals(transformedName)) {
if (basicClass != null && BlockClassFriendly.equals(transformedName)) {
final ClassReader cr = new ClassReader(basicClass);
final ClassNode cn = new ClassNode();
cr.accept(cn, 0);
@@ -37,7 +36,7 @@ public class SodiumBlockTransformer implements IClassTransformer {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
AngelicaTweaker.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
return basicClass;
@@ -1,12 +1,14 @@
package com.gtnewhorizons.angelica.loading.shared.transformers;
package com.gtnewhorizons.angelica.transform;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.gtnewhorizon.gtnhlib.asm.ClassConstantPoolParser;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import net.coderbot.iris.IrisLogging;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
@@ -20,6 +22,7 @@ import org.objectweb.asm.tree.MethodNode;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -30,25 +33,10 @@ import java.util.stream.Collectors;
/**
* This transformer redirects many GL calls to our custom GLStateManager
* <p>
* THIS CLASS MIGHT BE LOADED ON A DIFFERENT CLASS LOADER,
* IT SHOULD NOT CALL ANY CODE FROM THE MAIN MOD
*/
public final class AngelicaRedirector {
// TODO(Alexdoru) properly split this class in two because currently this class is doing
// two very different things :
// - redirect the GL calls to our GL manager class
// - replace the block fields with a thread safe replacement (only if Sodium is enabled)
public class RedirectorTransformer implements IClassTransformer {
public AngelicaRedirector(boolean isObf) {
IS_OBF = isObf;
}
private final boolean IS_OBF;
private static final boolean ASSERT_MAIN_THREAD = Boolean.getBoolean("angelica.assertMainThread");
private static final boolean LOG_SPAM = Boolean.getBoolean("angelica.redirectorLogspam");
private static final Logger LOGGER = LogManager.getLogger("AngelicaRedirector");
private static final boolean ASSERT_MAIN_THREAD = Boolean.parseBoolean(System.getProperty("angelica.assertMainThread", "false"));
private static final String Drawable = "org/lwjgl/opengl/Drawable";
private static final String GLStateManager = "com/gtnewhorizons/angelica/glsm/GLStateManager";
private static final String GL11 = "org/lwjgl/opengl/GL11";
@@ -70,26 +58,25 @@ public final class AngelicaRedirector {
"startGame", "func_71384_a",
"initializeTextures", "func_77474_a"
);
private static final List<Pair<String, String>> BlockBoundsFields = ImmutableList.of(
Pair.of("minX", "field_149759_B"),
Pair.of("minY", "field_149760_C"),
Pair.of("minZ", "field_149754_D"),
Pair.of("maxX", "field_149755_E"),
Pair.of("maxY", "field_149756_F"),
Pair.of("maxZ", "field_149757_G")
);
/** All classes in <tt>net.minecraft.block.*</tt> are the block subclasses save for these. */
private static final String[] VanillaBlockExclusions = {
private static final List<String> VanillaBlockExclusions = Arrays.asList(
"net/minecraft/block/IGrowable",
"net/minecraft/block/ITileEntityProvider",
"net/minecraft/block/BlockEventData",
"net/minecraft/block/BlockSourceImpl",
"net/minecraft/block/material/"
};
);
private static final ClassConstantPoolParser cstPoolParser = new ClassConstantPoolParser(GL11, GL13, GL14, GL20, OpenGlHelper, EXTBlendFunc, ARBMultiTexture, BlockPackage, Project);
private static final ClassConstantPoolParser cstPoolParser = new ClassConstantPoolParser(GL11, GL13, GL14, OpenGlHelper, EXTBlendFunc, ARBMultiTexture, BlockPackage, Project);
private static final Map<String, Map<String, String>> methodRedirects = new HashMap<>();
private static final Map<Integer, String> glCapRedirects = new HashMap<>();
private static final List<String> TransformerExclusions = Arrays.asList(
"org.lwjgl",
"com.gtnewhorizons.angelica.glsm.",
"com.gtnewhorizons.angelica.transform",
"me.eigenraven.lwjgl3ify"
);
private static int remaps = 0;
private static final Set<String> moddedBlockSubclasses = Collections.newSetFromMap(new ConcurrentHashMap<>());
// Block owners we *shouldn't* redirect because they shadow one of our fields
@@ -223,7 +210,8 @@ public final class AngelicaRedirector {
try {
final Class<?> angelicaConfig = Class.forName("com.gtnewhorizons.angelica.config.AngelicaConfig", true, Launch.classLoader);
angelicaConfigSodiumEnabledGetter = MethodHandles.lookup().findStaticGetter(angelicaConfig, "enableSodium", boolean.class);
final MethodHandle sodiumGetter = MethodHandles.lookup().findStaticGetter(angelicaConfig, "enableSodium", boolean.class);
angelicaConfigSodiumEnabledGetter = sodiumGetter;
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
@@ -253,23 +241,45 @@ public final class AngelicaRedirector {
return isVanillaBlockSubclass(className) || moddedBlockSubclasses.contains(className);
}
private String getFieldName(Pair<String, String> fieldPair) {
return IS_OBF ? fieldPair.getRight() : fieldPair.getLeft();
public static List<String> getTransformerExclusions() {
return Collections.unmodifiableList(TransformerExclusions);
}
public String[] getTransformerExclusions() {
return new String[]{
"org.lwjgl",
"com.gtnewhorizons.angelica.glsm.",
"com.gtnewhorizons.angelica.transform",
"me.eigenraven.lwjgl3ify"
};
}
public boolean shouldTransform(byte[] basicClass) {
public boolean shouldRfbTransform(byte[] basicClass) {
return cstPoolParser.find(basicClass, true);
}
@Override
public byte[] transform(final String className, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
// Ignore classes that are excluded from transformation - Doesn't fully work without the
// TransformerExclusions due to some nested classes
for (String exclusion : TransformerExclusions) {
if (className.startsWith(exclusion)) {
return basicClass;
}
}
if (!cstPoolParser.find(basicClass, true)) {
return basicClass;
}
// Keep in sync with com.gtnewhorizons.angelica.loading.rfb.RedirectorTransformerWrapper
final ClassReader cr = new ClassReader(basicClass);
final ClassNode cn = new ClassNode();
cr.accept(cn, 0);
final boolean changed = transformClassNode(transformedName, cn);
if (changed) {
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
AngelicaTweaker.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
return basicClass;
}
/** @return Was the class changed? */
public boolean transformClassNode(String transformedName, ClassNode cn) {
if (cn == null) {
@@ -283,19 +293,19 @@ public final class AngelicaRedirector {
}
// Check if this class shadows any fields of the parent class
if (moddedBlockSubclasses.contains(cn.name)) {
if(moddedBlockSubclasses.contains(cn.name)) {
// If a superclass shadows, then so do we, because JVM will resolve a reference on our class to that
// superclass
boolean doWeShadow;
if (blockOwnerExclusions.contains(cn.superName)) {
if(blockOwnerExclusions.contains(cn.superName)) {
doWeShadow = true;
} else {
// Check if we declare any known field names
doWeShadow = BlockBoundsFields.stream().anyMatch(pair ->
cn.fields.stream().anyMatch(field -> field.name.equals(getFieldName(pair))));
Set<String> fieldsDeclaredByClass = cn.fields.stream().map(f -> f.name).collect(Collectors.toSet());
doWeShadow = BlockTransformer.BlockBoundsFields.stream().anyMatch(pair -> fieldsDeclaredByClass.contains(pair.getLeft()) || fieldsDeclaredByClass.contains(pair.getRight()));
}
if (doWeShadow) {
LOGGER.info("Class '{}' shadows one or more block bounds fields, these accesses won't be redirected!", cn.name);
AngelicaTweaker.LOGGER.info("Class '{}' shadows one or more block bounds fields, these accesses won't be redirected!", cn.name);
blockOwnerExclusions.add(cn.name);
}
}
@@ -323,11 +333,11 @@ public final class AngelicaRedirector {
name = "disable" + name;
}
}
if (LOG_SPAM) {
if (IrisLogging.ENABLE_SPAM) {
if (name == null) {
LOGGER.info("Redirecting call in {} from GL11.{}(I)V to GLStateManager.{}(I)V", transformedName, mNode.name, mNode.name);
AngelicaTweaker.LOGGER.info("Redirecting call in {} from GL11.{}(I)V to GLStateManager.{}(I)V", transformedName, mNode.name, mNode.name);
} else {
LOGGER.info("Redirecting call in {} from GL11.{}(I)V to GLStateManager.{}()V", transformedName, mNode.name, name);
AngelicaTweaker.LOGGER.info("Redirecting call in {} from GL11.{}(I)V to GLStateManager.{}()V", transformedName, mNode.name, name);
}
}
mNode.owner = GLStateManager;
@@ -338,37 +348,43 @@ public final class AngelicaRedirector {
}
changed = true;
redirectInMethod = true;
remaps++;
} else if (mNode.owner.startsWith(Drawable) && mNode.name.equals("makeCurrent")) {
mNode.setOpcode(Opcodes.INVOKESTATIC);
mNode.owner = GLStateManager;
mNode.desc = "(L" + Drawable + ";)V";
mNode.itf = false;
changed = true;
if (LOG_SPAM) {
LOGGER.info("Redirecting call in {} to GLStateManager.makeCurrent()", transformedName);
if (IrisLogging.ENABLE_SPAM) {
AngelicaTweaker.LOGGER.info("Redirecting call in {} to GLStateManager.makeCurrent()", transformedName);
}
} else {
final Map<String, String> redirects = methodRedirects.get(mNode.owner);
if (redirects != null && redirects.containsKey(mNode.name)) {
if (LOG_SPAM) {
if (IrisLogging.ENABLE_SPAM) {
final String shortOwner = mNode.owner.substring(mNode.owner.lastIndexOf("/") + 1);
LOGGER.info("Redirecting call in {} from {}.{}{} to GLStateManager.{}{}", transformedName, shortOwner, mNode.name, mNode.desc, redirects.get(mNode.name), mNode.desc);
AngelicaTweaker.LOGGER.info("Redirecting call in {} from {}.{}{} to GLStateManager.{}{}", transformedName, shortOwner, mNode.name, mNode.desc, redirects.get(mNode.name), mNode.desc);
}
mNode.owner = GLStateManager;
mNode.name = redirects.get(mNode.name);
changed = true;
redirectInMethod = true;
remaps++;
}
}
} else if ((node.getOpcode() == Opcodes.GETFIELD || node.getOpcode() == Opcodes.PUTFIELD) && node instanceof FieldInsnNode fNode) {
if (!blockOwnerExclusions.contains(fNode.owner) && isBlockSubclass(fNode.owner) && isSodiumEnabled()) {
Pair<String, String> fieldToRedirect = BlockBoundsFields.stream()
.filter(pair -> fNode.name.equals(getFieldName(pair)))
.findFirst()
.orElse(null);
}
else if ((node.getOpcode() == Opcodes.GETFIELD || node.getOpcode() == Opcodes.PUTFIELD) && node instanceof FieldInsnNode fNode) {
if(!blockOwnerExclusions.contains(fNode.owner) && isBlockSubclass(fNode.owner) && isSodiumEnabled()) {
Pair<String, String> fieldToRedirect = null;
for(Pair<String, String> blockPairs : BlockTransformer.BlockBoundsFields) {
if(fNode.name.equals(blockPairs.getLeft()) || fNode.name.equals(blockPairs.getRight())) {
fieldToRedirect = blockPairs;
break;
}
}
if (fieldToRedirect != null) {
if (LOG_SPAM) {
LOGGER.info("Redirecting Block.{} in {} to thread-safe wrapper", fNode.name, transformedName);
if (IrisLogging.ENABLE_SPAM) {
AngelicaTweaker.LOGGER.info("Redirecting Block.{} in {} to thread-safe wrapper", fNode.name, transformedName);
}
// Perform the redirect
fNode.name = fieldToRedirect.getLeft(); // use unobfuscated name
@@ -407,7 +423,6 @@ public final class AngelicaRedirector {
}
private static class RedirectMap<K> extends HashMap<K, K> {
public static RedirectMap<String> newMap() {
return new RedirectMap<>();
}
@@ -0,0 +1,80 @@
package com.gtnewhorizons.angelica.transform.compat;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.transform.compat.handlers.CompatHandler;
import com.gtnewhorizons.angelica.transform.compat.transformers.generic.FieldLevelTessellatorTransformer;
import com.gtnewhorizons.angelica.transform.compat.transformers.generic.HUDCachingEarlyReturnTransformer;
import com.gtnewhorizons.angelica.transform.compat.transformers.generic.ThreadSafeISBRHAnnotationTransformer;
import com.gtnewhorizons.angelica.transform.compat.transformers.generic.TileEntityNullGuardTransformer;
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.launchwrapper.IClassTransformer;
import org.spongepowered.asm.lib.ClassReader;
import org.spongepowered.asm.lib.tree.ClassNode;
import org.spongepowered.asm.transformers.MixinClassWriter;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class GenericCompatTransformer implements IClassTransformer {
private static final Map<String, List<String>> fieldLevelTessellator = new Object2ObjectOpenHashMap<>();
private static final Map<String, List<String>> tileEntityNullGuard = new Object2ObjectOpenHashMap<>();
private static final Map<String, Boolean> threadSafeIBSRH = new Object2BooleanOpenHashMap<>();
private static final Map<String, List<String>> hudCachingEarlyReturn = new Object2ObjectOpenHashMap<>();
private static final Set<String> transformedClasses = new ObjectOpenHashSet<>();
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
if (!transformedClasses.contains(transformedName)) return basicClass;
ClassReader cr = new ClassReader(basicClass);
ClassNode cn = new ClassNode();
cr.accept(cn, 0);
if (fieldLevelTessellator.containsKey(transformedName)) {
FieldLevelTessellatorTransformer.transform(cn, fieldLevelTessellator.get(transformedName));
}
if (tileEntityNullGuard.containsKey(transformedName)) {
TileEntityNullGuardTransformer.transform(cn, tileEntityNullGuard.get(transformedName));
}
if (threadSafeIBSRH.containsKey(transformedName)) {
ThreadSafeISBRHAnnotationTransformer.transform(cn, threadSafeIBSRH.get(transformedName));
}
if (hudCachingEarlyReturn.containsKey(transformedName)) {
HUDCachingEarlyReturnTransformer.transform(cn, hudCachingEarlyReturn.get(transformedName));
}
MixinClassWriter cw = new MixinClassWriter(MixinClassWriter.COMPUTE_MAXS | MixinClassWriter.COMPUTE_FRAMES);
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
AngelicaTweaker.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
public static void register(CompatHandler handler) {
if (handler.getFieldLevelTessellator() != null)
fieldLevelTessellator.putAll(handler.getFieldLevelTessellator());
if (handler.getTileEntityNullGuard() != null)
tileEntityNullGuard.putAll(handler.getTileEntityNullGuard());
if (handler.getThreadSafeISBRHAnnotations() != null)
threadSafeIBSRH.putAll(handler.getThreadSafeISBRHAnnotations());
if (handler.getHUDCachingEarlyReturn() != null)
hudCachingEarlyReturn.putAll(handler.getHUDCachingEarlyReturn());
}
public static void build() {
transformedClasses.addAll(fieldLevelTessellator.keySet());
transformedClasses.addAll(tileEntityNullGuard.keySet());
transformedClasses.addAll(threadSafeIBSRH.keySet());
transformedClasses.addAll(hudCachingEarlyReturn.keySet());
}
}
@@ -1,9 +1,9 @@
package com.gtnewhorizons.angelica.loading.fml.compat;
package com.gtnewhorizons.angelica.transform.compat.handlers;
import java.util.List;
import java.util.Map;
public interface ICompatHandler {
public interface CompatHandler {
default Map<String, List<String>> getFieldLevelTessellator() {
return null;
@@ -0,0 +1,40 @@
package com.gtnewhorizons.angelica.transform.compat.handlers;
import com.gtnewhorizons.angelica.config.CompatConfig;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
public enum CompatHandlers {
STACKS_ON_STACKS(() -> CompatConfig.fixStacksOnStacks, new StacksOnStacksCompatHandler()),
EXTRA_UTILS(() -> CompatConfig.fixExtraUtils, new ExtraUtilsCompatHandler()),
IMMERSIVE_ENGINEERING(() -> CompatConfig.fixImmersiveEngineering, new ImmersiveEngineeringCompatHandler()),
THAUMCRAFT(() -> CompatConfig.fixThaumcraft, new ThaumcraftCompatHandler()),
THAUMIC_HORIZONS(() -> CompatConfig.fixThaumicHorizons, new ThaumicHorizonsCompatHandler());
private final Supplier<Boolean> applyIf;
private final CompatHandler handler;
CompatHandlers(Supplier<Boolean> applyIf, CompatHandler handler) {
this.applyIf = applyIf;
this.handler = handler;
}
private static List<CompatHandler> compatHandlers = null;
public static List<CompatHandler> getHandlers() {
if (compatHandlers != null) {
return compatHandlers;
}
compatHandlers = new ArrayList<>();
for (CompatHandlers value : values()) {
if (value.applyIf.get()) {
compatHandlers.add(value.handler);
}
}
return compatHandlers;
}
}
@@ -1,11 +1,10 @@
package com.gtnewhorizons.angelica.loading.fml.compat.handlers;
package com.gtnewhorizons.angelica.transform.compat.handlers;
import com.google.common.collect.ImmutableMap;
import com.gtnewhorizons.angelica.loading.fml.compat.ICompatHandler;
import java.util.Map;
public class ExtraUtilsCompatHandler implements ICompatHandler {
public class ExtraUtilsCompatHandler implements CompatHandler {
@Override
public Map<String, Boolean> getThreadSafeISBRHAnnotations() {
@@ -1,13 +1,13 @@
package com.gtnewhorizons.angelica.loading.fml.compat.handlers;
package com.gtnewhorizons.angelica.transform.compat.handlers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.gtnewhorizons.angelica.loading.fml.compat.ICompatHandler;
import com.gtnewhorizons.angelica.transform.compat.transformers.specific.ImmersiveEngineeringTransformer;
import java.util.List;
import java.util.Map;
public class ImmersiveEngineeringCompatHandler implements ICompatHandler {
public class ImmersiveEngineeringCompatHandler implements CompatHandler {
@Override
public Map<String, List<String>> getTileEntityNullGuard() {
@@ -38,7 +38,7 @@ public class ImmersiveEngineeringCompatHandler implements ICompatHandler {
@Override
public List<String> extraTransformers() {
return ImmutableList.of("com.gtnewhorizons.angelica.loading.fml.compat.transformers.specific.ImmersiveEngineeringTransformer");
return ImmutableList.of(ImmersiveEngineeringTransformer.class.getName());
}
}
@@ -1,13 +1,13 @@
package com.gtnewhorizons.angelica.loading.fml.compat.handlers;
package com.gtnewhorizons.angelica.transform.compat.handlers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.gtnewhorizons.angelica.loading.fml.compat.ICompatHandler;
import com.gtnewhorizons.angelica.transform.compat.transformers.specific.StacksOnStacksTransformer;
import java.util.List;
import java.util.Map;
public class StacksOnStacksCompatHandler implements ICompatHandler {
public class StacksOnStacksCompatHandler implements CompatHandler {
@Override
public Map<String, List<String>> getFieldLevelTessellator() {
@@ -28,6 +28,6 @@ public class StacksOnStacksCompatHandler implements ICompatHandler {
@Override
public List<String> extraTransformers() {
return ImmutableList.of("com.gtnewhorizons.angelica.loading.fml.compat.transformers.specific.StacksOnStacksTransformer");
return ImmutableList.of(StacksOnStacksTransformer.class.getName());
}
}
@@ -1,13 +1,12 @@
package com.gtnewhorizons.angelica.loading.fml.compat.handlers;
package com.gtnewhorizons.angelica.transform.compat.handlers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.gtnewhorizons.angelica.loading.fml.compat.ICompatHandler;
import java.util.List;
import java.util.Map;
public class ThaumcraftCompatHandler implements ICompatHandler {
public class ThaumcraftCompatHandler implements CompatHandler {
@Override
public Map<String, List<String>> getHUDCachingEarlyReturn() {
@@ -1,13 +1,12 @@
package com.gtnewhorizons.angelica.loading.fml.compat.handlers;
package com.gtnewhorizons.angelica.transform.compat.handlers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.gtnewhorizons.angelica.loading.fml.compat.ICompatHandler;
import java.util.List;
import java.util.Map;
public class ThaumicHorizonsCompatHandler implements ICompatHandler {
public class ThaumicHorizonsCompatHandler implements CompatHandler {
@Override
public Map<String, List<String>> getHUDCachingEarlyReturn() {
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic;
package com.gtnewhorizons.angelica.transform.compat.transformers.generic;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.transform.AsmUtil;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.lib.tree.AbstractInsnNode;
import org.spongepowered.asm.lib.tree.ClassNode;
@@ -62,7 +62,7 @@ public class FieldLevelTessellatorTransformer {
new FieldInsnNode(
Opcodes.GETSTATIC,
"net/minecraft/client/renderer/Tessellator",
AngelicaTweaker.obf("instance", "field_78398_a"),
AsmUtil.obf("instance", "field_78398_a"),
"Lnet/minecraft/client/renderer/Tessellator;"));
list.add(new VarInsnNode(Opcodes.ASTORE, localIndex));
mn.instructions.insert(list);
Loaded 100 of 404 files, more files were not shown because too many files have changed in this diff. Show more