Compare commits

..
16 Commits
Author SHA1 Message Date
Jason Mitchell 0ad69f3433 FINE, we'll work with java8/forge. 2026-03-28 20:09:42 -07:00
Fabian Maurer 0dabecc784 Handle GL_POLYGON in legacy conversion 2026-03-28 14:41:23 -07:00
Jason Mitchell 9b3fad4c6d Ensure sufficient coverage + optimize the check 2026-03-28 13:16:54 -07:00
Kaiz e1e8256861 fix: crash when client-side vertex attrib pointers are used in core profile 2026-03-28 13:16:54 -07:00
Jason Mitchell e65c01c2a3 Handle method handles in invokedynamic 2026-03-28 12:46:19 -07:00
Jason Mitchell b9de4df18e Update alpha test to hopefully work with RTM and AE2 (#1597) 2026-03-28 09:40:10 -07:00
Kotmatross a5d31cd1b6 Return phaseChangeNotifier (#1596) 2026-03-28 09:15:46 -07:00
DarkShadow44 29f8d718cc Pass texturemap to composite shader and honor texture replacements (#1589) 2026-03-27 19:07:45 -07:00
12411234 52b0db2566 Fix missing glGetTexImage method crash when using XaerosWorldMap (#1592) 2026-03-27 19:02:51 -07:00
Eclipse-Sol e9eb45f4f2 Centers the Wither Armor (#1590) 2026-03-27 18:37:17 -07:00
Jason Mitchell 92e28a4cd5 Blur Compat (#1593) 2026-03-27 18:32:24 -07:00
Jason Mitchell f46a86889c Fix crash with fancy block particles. (#1591) 2026-03-27 17:52:14 -07:00
Jason MitchellandAlexdoru c827d446d5 External Redirector API (#1580)
Co-authored-by: Alexdoru <[email protected]>
2026-03-27 11:20:29 -07:00
Eclipse-Sol 00ef355ce8 Fix sky lighting on drawCrossSquare rendered blocks. (#1576) 2026-03-25 18:58:40 -07:00
Kotmatross 1282056fff Fix blend leak in BatchingFontRenderer (#1575) 2026-03-25 15:34:43 -07:00
Alexdoru 6c422d47a2 Make the mod no-op on server (#1574) 2026-03-25 12:59:23 -07:00
65 changed files with 1442 additions and 379 deletions

No files matched your search

+1 -1
View File
@@ -119,7 +119,7 @@ dependencies {
compileOnly("com.github.GTNewHorizons:NotEnoughIds:2.1.10:dev")
compileOnly(rfg.deobf("maven.modrinth:ntmspace:X5593_H261"))
compileOnly(rfg.deobf("maven.modrinth:ntmspace:X5634_H261"))
compileOnly(rfg.deobf("curse.maven:campfirebackport-387444:4611675"))
compileOnly(rfg.deobf("curse.maven:xaeros-minimap-263420:5060684"))
+2 -1
View File
@@ -76,7 +76,7 @@ dependencies {
compileOnly("org.lwjgl.lwjgl:lwjgl:${property("lwjglVersion")}")
compileOnly("org.lwjgl.lwjgl:lwjgl_util:${property("lwjglVersion")}")
compileOnly("org.ow2.asm:asm-tree:9.7")
compileOnly("org.ow2.asm:asm-tree:5.0.3")
// Our Deps
api("com.github.GTNewHorizons:GTNHLib:${property("gtnhlibVersion")}:dev")
@@ -100,6 +100,7 @@ dependencies {
testImplementation(sourceSets["stubs"].output)
testImplementation(platform("org.junit:junit-bom:${property("junitBomVersion")}"))
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.ow2.asm:asm-tree:9.7")
testImplementation("org.lwjgl.lwjgl:lwjgl:${property("lwjglVersion")}")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testRuntimeOnly("org.apache.logging.log4j:log4j-core:${property("log4jVersion")}")
@@ -345,6 +345,16 @@ public class FeedbackManager {
}
break;
case GL11.GL_POLYGON:
if (count >= 3) {
feedbackToken(GL11.GL_POLYGON_TOKEN);
feedbackToken(count);
for (int i = 0; i < count; i++) {
emitVertex(reader.read(i), stride);
}
}
break;
default:
throw new UnsupportedOperationException("Feedback mode: unsupported primitive mode 0x" + Integer.toHexString(mode));
}
@@ -419,6 +419,9 @@ public class GLStateManager {
@Getter protected static int boundVBO;
@Getter protected static int boundEBO;
@Getter protected static int boundVAO;
private static int clientArraysVBO = 0;
private static int clientArraysVBOCapacity = 0;
private static final int[] clientArraysVBOOffsets = new int[VertexAttribState.MAX_ATTRIBS];
private static int boundPixelUnpackBuffer;
private static int boundPixelPackBuffer;
private static final Int2IntOpenHashMap vaoEboMap = new Int2IntOpenHashMap();
@@ -2145,6 +2148,7 @@ public class GLStateManager {
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
RENDER_BACKEND.drawElements(mode, indices);
}
@@ -2158,6 +2162,7 @@ public class GLStateManager {
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(indices);
} else {
@@ -2175,6 +2180,7 @@ public class GLStateManager {
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(indices);
} else {
@@ -2192,6 +2198,7 @@ public class GLStateManager {
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(count, type, indices);
} else {
@@ -2223,6 +2230,7 @@ public class GLStateManager {
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadElementsAsTriangles(indices_count, type, indices_buffer_offset);
} else {
@@ -2245,20 +2253,72 @@ public class GLStateManager {
public static void glMultiDrawElementsIndirect(int mode, int type, long indirect, int drawcount, int stride) {
ShaderManager.getInstance().preDraw();
prepareClientArrays();
RENDER_BACKEND.multiDrawElementsIndirect(mode, type, indirect, drawcount, stride);
}
public static void glMultiDrawElementsBaseVertex(int mode, long pCount, int type, long pIndices, int drawcount, long pBaseVertex) {
ShaderManager.getInstance().preDraw();
prepareClientArrays();
RENDER_BACKEND.multiDrawElementsBaseVertex(mode, pCount, type, pIndices, drawcount, pBaseVertex);
}
public static void glDrawElementsBaseVertex(int mode, int count, int type, long indices, int basevertex) {
ShaderManager.getInstance().preDraw();
prepareClientArrays();
RENDER_BACKEND.drawElementsBaseVertex(mode, count, type, indices, basevertex);
}
public static void glDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) { RENDER_BACKEND.drawElementsInstanced(mode, count, type, indices, primcount); }
public static void glDrawElementsInstanced(int mode, int count, int type, long indices, int primcount) {
ShaderManager.getInstance().preDraw();
prepareClientArrays();
RENDER_BACKEND.drawElementsInstanced(mode, count, type, indices, primcount);
}
private static void prepareClientArrays() {
if (ShaderManager.getInstance().isEnabled() && VertexAttribState.hasAnyClientSideEnabledAttrib()) {
uploadClientArraysToVBO();
}
}
/**
* If any enabled vertex attribute uses a client-side pointer (no VBO), upload all such
* attribs into a shared stream VBO so the draw succeeds under core profile.
*/
private static void uploadClientArraysToVBO() {
int totalBytes = 0;
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
clientArraysVBOOffsets[i] = -1;
final VertexAttribState.Attrib a = VertexAttribState.get(i);
if (!a.enabled || a.clientPointer == null) continue;
clientArraysVBOOffsets[i] = totalBytes;
totalBytes += a.clientPointer.remaining();
}
if (totalBytes == 0) return;
if (clientArraysVBO == 0) {
clientArraysVBO = RENDER_BACKEND.genBuffers();
}
final int savedVBO = boundVBO;
glBindBuffer(GL15.GL_ARRAY_BUFFER, clientArraysVBO);
if (totalBytes > clientArraysVBOCapacity) {
int newCap = Math.max(4096, clientArraysVBOCapacity);
while (newCap < totalBytes) newCap *= 2;
clientArraysVBOCapacity = newCap;
}
RENDER_BACKEND.bufferData(GL15.GL_ARRAY_BUFFER, clientArraysVBOCapacity, GL15.GL_STREAM_DRAW);
for (int i = 0; i < VertexAttribState.MAX_ATTRIBS; i++) {
if (clientArraysVBOOffsets[i] < 0) continue;
final VertexAttribState.Attrib a = VertexAttribState.get(i);
RENDER_BACKEND.bufferSubData(GL15.GL_ARRAY_BUFFER, clientArraysVBOOffsets[i], a.clientPointer.duplicate());
RENDER_BACKEND.vertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (long) clientArraysVBOOffsets[i]);
}
glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO);
}
public static void glDrawArrays(int mode, int first, int count) {
CommandRecorder savedRecorder = null;
@@ -2278,8 +2338,11 @@ public class GLStateManager {
}
prepareWideLineEmulation(mode);
ShaderManager.getInstance().preDraw();
prepareClientArrays();
if (mode == GL11.GL_QUADS) {
QuadConverter.drawQuadsAsTriangles(first, count);
} else if (mode == GL11.GL_POLYGON) {
RENDER_BACKEND.drawArrays(GL11.GL_TRIANGLE_FAN, first, count);
} else {
RENDER_BACKEND.drawArrays(mode, first, count);
}
@@ -5814,5 +5877,7 @@ public class GLStateManager {
public static void glGetUniform(int program, int location, IntBuffer params) {
RENDER_BACKEND.getUniformiv(program, location, params);
}
public static void glGetTexImage(int target, int level, int format, int type, long pixels) {
GL11.glGetTexImage(target, level, format, type, pixels);
}
}
@@ -36,8 +36,11 @@ public class GlslTransformUtils {
/** Reserved words added in later GLSL versions that may appear as identifiers in older shaders. */
private record ReservedWordRename(Pattern pattern, String replacement) {}
private static final Map<Integer, List<ReservedWordRename>> VERSIONED_RESERVED_WORDS = Map.of(
// Always rename sample, glsl-transformation-lib doesn't like it
0, List.of(
new ReservedWordRename(Pattern.compile("\\bsample\\b"), RENAMED_PREFIX + "sample")
),
400, List.of(
new ReservedWordRename(Pattern.compile("\\bsample\\b"), RENAMED_PREFIX + "sample"),
new ReservedWordRename(Pattern.compile("\\bsampler\\b(?!\\d)"), RENAMED_PREFIX + "sampler")
)
);
@@ -273,21 +273,27 @@ public final class FragmentShaderGenerator {
sb.append(" // Alpha test\n");
final int func = FragmentKey.decodeAlphaFunc(key.alphaTestFunc());
sb.append(" float qa = round(color.a * 255.0);\n");
switch (func) {
case GL11.GL_NEVER ->
sb.append(" discard;\n");
case GL11.GL_LESS ->
sb.append(" if (color.a >= u_AlphaRef) discard;\n");
case GL11.GL_EQUAL ->
sb.append(" if (color.a != u_AlphaRef) discard;\n");
sb.append(" if (qa >= u_AlphaRef * 255.0) discard;\n");
case GL11.GL_EQUAL -> {
sb.append(" float qr = round(u_AlphaRef * 255.0);\n");
sb.append(" if (qa != qr) discard;\n");
}
case GL11.GL_LEQUAL ->
sb.append(" if (color.a > u_AlphaRef) discard;\n");
sb.append(" if (qa > u_AlphaRef * 255.0) discard;\n");
case GL11.GL_GREATER ->
sb.append(" if (color.a <= u_AlphaRef) discard;\n");
case GL11.GL_NOTEQUAL ->
sb.append(" if (color.a == u_AlphaRef) discard;\n");
sb.append(" if (qa <= u_AlphaRef * 255.0) discard;\n");
case GL11.GL_NOTEQUAL -> {
sb.append(" float qr = round(u_AlphaRef * 255.0);\n");
sb.append(" if (qa == qr) discard;\n");
}
case GL11.GL_GEQUAL ->
sb.append(" if (color.a < u_AlphaRef) discard;\n");
sb.append(" if (qa < u_AlphaRef * 255.0) discard;\n");
case GL11.GL_ALWAYS -> {}
}
}
@@ -75,6 +75,9 @@ public final class DisplayListVBOBuilder {
if (data.drawMode == GL11.GL_QUADS) {
final IVertexArrayObject indexedVAO = new IndexedVAO(vbo, IndexBuffer.convertQuadsToTrigs(start, start + vertexCount));
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(indexedVAO, GL11.GL_TRIANGLES, 0, vertexCount / 4 * 6);
} else if (data.drawMode == GL11.GL_POLYGON) {
// GL_POLYGON is removed in core profile; convert to GL_TRIANGLE_FAN which is equivalent for convex polygons
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, GL11.GL_TRIANGLE_FAN, start, vertexCount);
} else {
vbos[data.drawIndex] = new DisplayListVBO.SubVBO(vao, data.drawMode, start, vertexCount);
}
@@ -4,16 +4,19 @@ import com.google.common.collect.ImmutableSet;
import com.gtnewhorizon.gtnhlib.asm.ClassConstantPoolParser;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -79,6 +82,9 @@ public class GLSMRedirector {
glCapRedirects.put(org.lwjgl.opengl.GL11.GL_SCISSOR_TEST, "ScissorTest");
final var gl11 = RedirectMap.newMap()
// glEnable/Disable - Special cased in GLSMRedirector, but included here for the external API
.add("glEnable")
.add("glDisable")
.add("glAlphaFunc")
.add("glBegin")
.add("glBindTexture")
@@ -495,6 +501,22 @@ public class GLSMRedirector {
return CORE_EXCLUSIONS.clone();
}
/** The internal-name prefix used to match GL version classes (e.g., {@code "org/lwjgl/opengl/GL"}). */
public static String getGLPrefix() { return GL_PREFIX; }
/** The internal name of the redirect target class. */
public static String getTargetClassName() { return GLStateManager; }
/** Method redirects for GL-prefix classes. Key: original method name, Value: GLStateManager method name. */
public static Map<String, String> getGLPrefixMethodRedirects() {
return Collections.unmodifiableMap(glMethodRedirects);
}
/** Method redirects for named (non-GL-prefix) classes. Key: internal class name, Value: per-method redirect map. */
public static Map<String, Map<String, String>> getNamedClassMethodRedirects() {
return Collections.unmodifiableMap(methodRedirects);
}
public boolean shouldTransform(byte[] basicClass) {
return cstPoolParser.find(basicClass, true);
}
@@ -504,7 +526,7 @@ public class GLSMRedirector {
boolean changed = false;
final boolean isOpenGlHelper = transformedName.equals("net.minecraft.client.renderer.OpenGlHelper");
for (MethodNode mn : cn.methods) {
for (MethodNode mn : (List<MethodNode>) cn.methods) {
if (isOpenGlHelper && (mn.name.equals("glBlendFunc") || mn.name.equals("func_148821_a"))) {
continue;
}
@@ -590,6 +612,22 @@ public class GLSMRedirector {
changed = true;
}
}
} else if (node instanceof InvokeDynamicInsnNode dynNode) {
// Redirect method handles in invokedynamic bootstrap arguments
for (int i = 0; i < dynNode.bsmArgs.length; i++) {
if (!(dynNode.bsmArgs[i] instanceof Handle handle)) continue;
final Map<String, String> redirects = handle.getOwner().startsWith(GL_PREFIX) ? glMethodRedirects : methodRedirects.get(handle.getOwner());
if (redirects == null) continue;
final String glsmName = redirects.get(handle.getName());
if (glsmName == null) continue;
if (LOG_SPAM) {
final String shortOwner = handle.getOwner().substring(handle.getOwner().lastIndexOf("/") + 1);
LOGGER.info("Redirecting invokedynamic handle in {} from {}.{}{} to GLStateManager.{}{}", transformedName, shortOwner, handle.getName(), handle.getDesc(), glsmName, handle.getDesc());
}
dynNode.bsmArgs[i] = new Handle(handle.getTag(), GLStateManager, glsmName, handle.getDesc());
changed = true;
redirectInMethod = true;
}
}
}
if (ASSERT_MAIN_THREAD && redirectInMethod && !transformedName.startsWith(SplashProgress) && !(transformedName.startsWith(MinecraftClient) && ExcludedMinecraftMainThreadChecks.contains(mn.name))) {
@@ -16,16 +16,19 @@ public class VertexAttribState {
private static final Int2ObjectOpenHashMap<Attrib[]> vaoAttribs = new Int2ObjectOpenHashMap<>();
private static Attrib[] current;
private static final ArrayDeque<Attrib[]> pool = new ArrayDeque<>();
private static int clientSideEnabledCount;
public static void init(int defaultVAO) {
vaoAttribs.clear();
pool.clear();
current = allocAttribArray();
vaoAttribs.put(defaultVAO, current);
clientSideEnabledCount = 0;
}
public static void onBindVertexArray(int newVAO) {
current = vaoAttribs.computeIfAbsent(newVAO, k -> allocAttribArray());
recomputeClientSideCount();
}
public static void onDeleteVertexArray(int vaoId) {
@@ -39,6 +42,7 @@ public class VertexAttribState {
vaoAttribs.clear();
pool.clear();
current = null;
clientSideEnabledCount = 0;
}
private static Attrib[] allocAttribArray() {
@@ -55,6 +59,7 @@ public class VertexAttribState {
public static void set(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = current[index];
final boolean was = a.isClientSide();
a.size = size;
a.type = type;
a.normalized = normalized;
@@ -62,11 +67,13 @@ public class VertexAttribState {
a.offset = offset;
a.vboId = vboId;
a.clientPointer = null;
if (was) clientSideEnabledCount--;
}
public static void set(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer, int vboId) {
if (index < 0 || index >= MAX_ATTRIBS) return;
final Attrib a = current[index];
final boolean was = a.isClientSide();
a.size = size;
a.type = type;
a.normalized = normalized;
@@ -74,17 +81,33 @@ public class VertexAttribState {
a.offset = 0;
a.vboId = vboId;
a.clientPointer = (vboId == 0) ? pointer : null;
final boolean now = a.isClientSide();
if (was != now) clientSideEnabledCount += now ? 1 : -1;
}
public static void setEnabled(int index, boolean enabled) {
if (index < 0 || index >= MAX_ATTRIBS) return;
current[index].enabled = enabled;
final Attrib a = current[index];
if (a.clientPointer != null && a.enabled != enabled) {
clientSideEnabledCount += enabled ? 1 : -1;
}
a.enabled = enabled;
}
public static Attrib get(int index) {
return current[index];
}
private static void recomputeClientSideCount() {
int count = 0;
for (int i = 0; i < MAX_ATTRIBS; i++) {
if (current[i].isClientSide()) count++;
}
clientSideEnabledCount = count;
}
public static boolean hasVBOBoundAttrib() {
for (int i = 0; i < MAX_ATTRIBS; i++) {
if (current[i].enabled && current[i].vboId != 0) return true;
@@ -92,6 +115,14 @@ public class VertexAttribState {
return false;
}
/**
* Returns true if any currently enabled vertex attribute was registered without a VBO
* (i.e. as a client-side pointer). In core profile, such attribs are treated as null
* offsets into a non-existent buffer, causing a native crash at draw time.
*/
public static boolean hasAnyClientSideEnabledAttrib() {
return clientSideEnabledCount > 0;
}
public static class Attrib {
public boolean enabled;
public int size;
@@ -102,6 +133,10 @@ public class VertexAttribState {
public int vboId;
public ByteBuffer clientPointer;
public boolean isClientSide() {
return enabled && clientPointer != null;
}
public void reset() {
enabled = false;
size = 0;
@@ -156,6 +156,57 @@ class FeedbackManagerTest {
GLStateManager.glDeleteVertexArrays(vao);
}
@Test
void testPolygonFeedback_GL2D() {
final FloatBuffer buf = BufferUtils.createFloatBuffer(128);
FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf);
FeedbackManager.glRenderMode(GL11.GL_FEEDBACK);
GLStateManager.getModelViewMatrix().identity();
GLStateManager.getProjectionMatrix().identity();
GLStateManager.getViewportState().setViewPort(0, 0, 800, 600);
GLStateManager.getViewportState().setDepthRange(0.0, 1.0);
final int vao = GL30.glGenVertexArrays();
GLStateManager.glBindVertexArray(vao);
final float[] verts = {
0f, 0f, 0f,
1f, 0f, 0f,
1f, 1f, 0f,
0f, 1f, 0f
};
final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(verts.length);
vertBuf.put(verts).flip();
final int vbo = GL15.glGenBuffers();
GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertBuf, GL15.GL_STATIC_DRAW);
GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0);
GLStateManager.glEnableVertexAttribArray(0);
FeedbackManager.processDrawArrays(GL11.GL_POLYGON, 0, 4);
final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER);
assertEquals(10, count, "Polygon in GL_2D should produce 10 floats");
assertEquals(GL11.GL_POLYGON_TOKEN, buf.get(0), 0.001f);
assertEquals(4.0f, buf.get(1), 0.001f);
assertEquals(400.0f, buf.get(2), 0.01f);
assertEquals(300.0f, buf.get(3), 0.01f);
assertEquals(800.0f, buf.get(4), 0.01f);
assertEquals(300.0f, buf.get(5), 0.01f);
assertEquals(800.0f, buf.get(6), 0.01f);
assertEquals(600.0f, buf.get(7), 0.01f);
assertEquals(400.0f, buf.get(8), 0.01f);
assertEquals(600.0f, buf.get(9), 0.01f);
GLStateManager.glDisableVertexAttribArray(0);
GLStateManager.glBindVertexArray(0);
GL15.glDeleteBuffers(vbo);
GLStateManager.glDeleteVertexArrays(vao);
}
@Test
void testIdentityTransform_viewportMapping() {
final FloatBuffer buf = BufferUtils.createFloatBuffer(64);
@@ -0,0 +1,142 @@
package com.gtnewhorizons.angelica.glsm.redirect;
import org.junit.jupiter.api.Test;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.MethodNode;
import java.util.ArrayList;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class GLSMRedirectorTest {
private static final String GLSM = GLSMRedirector.getTargetClassName();
private static final Handle LAMBDA_METAFACTORY = new Handle(
Opcodes.H_INVOKESTATIC,
"java/lang/invoke/LambdaMetafactory",
"metafactory",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;"
+ "Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)"
+ "Ljava/lang/invoke/CallSite;",
false);
private static ClassNode makeClassWithInvokeDynamic(Handle implHandle) {
final ClassNode cn = new ClassNode();
cn.version = Opcodes.V1_8;
cn.access = Opcodes.ACC_PUBLIC;
cn.name = "com/example/TestClass";
cn.superName = "java/lang/Object";
cn.methods = new ArrayList<>();
final MethodNode mn = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "test", "()V", null, null);
mn.instructions.add(new InvokeDynamicInsnNode(
"run",
"()Ljava/lang/Runnable;",
LAMBDA_METAFACTORY,
Type.getType("()V"),
implHandle,
Type.getType("()V")));
mn.instructions.add(new InsnNode(Opcodes.RETURN));
cn.methods.add(mn);
return cn;
}
private static Handle getResultHandle(ClassNode cn) {
final InvokeDynamicInsnNode dyn = (InvokeDynamicInsnNode) cn.methods.get(0).instructions.get(0);
return (Handle) dyn.bsmArgs[1];
}
@Test
void invokeDynamicGL11HandleIsRedirected() {
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11", "glPopMatrix", "()V", false);
final ClassNode cn = makeClassWithInvokeDynamic(impl);
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
assertTrue(changed);
final Handle result = getResultHandle(cn);
assertEquals(GLSM, result.getOwner());
assertEquals("glPopMatrix", result.getName());
assertEquals("()V", result.getDesc());
assertEquals(Opcodes.H_INVOKESTATIC, result.getTag());
}
@Test
void invokeDynamicNamedClassHandleIsRedirected() {
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/EXTBlendFuncSeparate", "glBlendFuncSeparateEXT", "(IIII)V", false);
final ClassNode cn = makeClassWithInvokeDynamic(impl);
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
assertTrue(changed);
final Handle result = getResultHandle(cn);
assertEquals(GLSM, result.getOwner());
assertEquals("tryBlendFuncSeparate", result.getName());
assertEquals("(IIII)V", result.getDesc());
}
@Test
void invokeDynamicNonGLHandleIsNotTouched() {
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
final ClassNode cn = makeClassWithInvokeDynamic(impl);
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
assertFalse(changed);
final Handle result = getResultHandle(cn);
assertEquals("java/lang/System", result.getOwner());
assertEquals("currentTimeMillis", result.getName());
}
@Test
void invokeDynamicUnknownGLMethodIsNotTouched() {
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11", "glNonexistent", "()V", false);
final ClassNode cn = makeClassWithInvokeDynamic(impl);
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
assertFalse(changed);
final Handle result = getResultHandle(cn);
assertEquals("org/lwjgl/opengl/GL11", result.getOwner());
}
@Test
void invokeDynamicGLCVariantIsRedirected() {
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11C", "glDrawArrays", "(III)V", false);
final ClassNode cn = makeClassWithInvokeDynamic(impl);
final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
assertTrue(changed);
final Handle result = getResultHandle(cn);
assertEquals(GLSM, result.getOwner());
assertEquals("glDrawArrays", result.getName());
assertEquals("(III)V", result.getDesc());
}
@Test
void transformedClassProducesValidBytecode() {
final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11", "glPopMatrix", "()V", false);
final ClassNode cn = makeClassWithInvokeDynamic(impl);
new GLSMRedirector().transformClassNode("com.example.TestClass", cn);
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
assertDoesNotThrow(() -> {
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
assertTrue(bytes.length > 0);
});
}
}
@@ -1,17 +1,11 @@
package com.gtnewhorizons.angelica;
import com.gtnewhorizons.angelica.common.BlockError;
import com.gtnewhorizons.angelica.compat.ModStatus;
import com.gtnewhorizons.angelica.config.ConfigMigrator;
import com.gtnewhorizons.angelica.proxy.CommonProxy;
import com.gtnewhorizons.angelica.utils.AnimationMode;
import com.gtnewhorizons.angelica.utils.ManagedEnum;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -32,32 +26,19 @@ import static com.gtnewhorizons.angelica.AngelicaMod.MOD_ID;
acceptableRemoteVersions = "*",
guiFactory = "com.gtnewhorizons.angelica.config.AngelicaGuiConfigFactory")
public class AngelicaMod {
public static final String MOD_ID = "angelica";
public static final Logger LOGGER = LogManager.getLogger("Angelica");
public static final boolean lwjglDebug = Boolean.getBoolean("org.lwjgl.util.Debug");
private static SodiumGameOptions CONFIG;
public static SodiumGameOptions options() {
if (CONFIG == null) {
CONFIG = SodiumGameOptions.load(ConfigMigrator.handleConfigMigration("angelica-options.json"));
}
return CONFIG;
}
@SidedProxy(clientSide = "com.gtnewhorizons.angelica.proxy.ClientProxy", serverSide = "com.gtnewhorizons.angelica.proxy.CommonProxy")
@SidedProxy(
clientSide = "com.gtnewhorizons.angelica.proxy.ClientProxy",
serverSide = "com.gtnewhorizons.angelica.proxy.CommonProxy")
public static CommonProxy proxy;
public static final boolean lwjglDebug = Boolean.parseBoolean(System.getProperty("org.lwjgl.util.Debug", "false"));
public static final ManagedEnum<AnimationMode> animationsMode = new ManagedEnum<>(AnimationMode.VISIBLE_ONLY);
public static BlockError blockError;
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
ModStatus.preInit();
proxy.preInit(event);
blockError = new BlockError();
}
@Mod.EventHandler
@@ -0,0 +1,118 @@
package com.gtnewhorizons.angelica.api;
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;
import java.util.regex.Pattern;
/**
* Public API for querying Angelica's GL call redirections.
* <p>
* Angelica's bytecode transformer redirects GL calls (e.g., {@code GL11.glPushMatrix()}) to
* {@link com.gtnewhorizons.angelica.glsm.GLStateManager GLStateManager} for state tracking and
* core profile compatibility. This works for normal Java bytecode loaded through the Forge classloader,
* but callers that bypass class transformation (e.g., Nashorn/script engines, reflection, MethodHandles)
* need to perform their own redirection.
* <p>
* This class provides the redirect mapping data and a precompiled regex pattern for transforming
* script source text before evaluation.
*
* <h3>Example: transforming a Nashorn script before eval</h3>
* Given a script that uses {@code importPackage(Packages.org.lwjgl.opengl)} and calls
* {@code GL11.glPushMatrix()}, the text transformation rewrites those calls before evaluation:
* <pre>{@code
* // Make GLStateManager available to the script -- either via importPackage:
* script = "importPackage(Packages.com.gtnewhorizons.angelica.glsm);\n" + script;
* // ... or via Java.type for an explicit binding:
* // script = "var GLStateManager = Java.type('" + GLRedirects.TARGET_CLASS + "');\n" + script;
*
* // Rewrite: GL11.glPushMatrix() -> GLStateManager.glPushMatrix()
* script = GLRedirects.getMethodRedirectPattern().matcher(script).replaceAll(mr -> {
* String target = GLRedirects.getTargetMethodName(mr.group(0));
* return target != null ? "GLStateManager." + target : mr.group(0);
* });
*
* engine.eval(script);
* }</pre>
*/
public final class GLRedirects {
/** Fully qualified name of the GLStateManager class that all methods redirect to. */
@SuppressWarnings("unused")
public static final String TARGET_CLASS = "com.gtnewhorizons.angelica.glsm.GLStateManager";
private static final String SHORT_GL_PREFIX;
private static final Map<String, Map<String, String>> NAMED_CLASS_SHORT_NAME_MAP;
private static final Map<String, String> GL_PREFIX_METHOD_REDIRECTS;
private static final Pattern METHOD_REDIRECT_PATTERN;
static {
SHORT_GL_PREFIX = buildShortGLPrefix();
NAMED_CLASS_SHORT_NAME_MAP = buildNamedClassShortNameMap();
GL_PREFIX_METHOD_REDIRECTS = GLSMRedirector.getGLPrefixMethodRedirects();
METHOD_REDIRECT_PATTERN = buildMethodRedirectPattern();
}
private GLRedirects() {}
/**
* Returns a precompiled pattern that matches redirected {@code ClassName.methodName} references
* using short (simple) class names, as they might typically appear in script text.
*
* @return compiled regex pattern
*/
public static Pattern getMethodRedirectPattern() {
return METHOD_REDIRECT_PATTERN;
}
/**
* Looks up the GLStateManager target method name for a short-name reference.
*
* @param classAndMethod short class name and method, e.g., {@code "GL11.glPushMatrix"}
* @return the target method name on GLStateManager (e.g., {@code "glPushMatrix"}), or {@code null} if this reference is not redirected
*/
public static String getTargetMethodName(String classAndMethod) {
final int dot = classAndMethod.indexOf('.');
if (dot < 0) return null;
final String className = classAndMethod.substring(0, dot);
final String methodName = classAndMethod.substring(dot + 1);
final Map<String, String> classRedirects = NAMED_CLASS_SHORT_NAME_MAP.get(className);
if (classRedirects != null) {
return classRedirects.get(methodName);
}
if (className.startsWith(SHORT_GL_PREFIX)) {
return GL_PREFIX_METHOD_REDIRECTS.get(methodName);
}
return null;
}
private static String buildShortGLPrefix() {
final String glPrefix = GLSMRedirector.getGLPrefix();
return glPrefix.substring(glPrefix.lastIndexOf('/') + 1);
}
private static Map<String, Map<String, String>> buildNamedClassShortNameMap() {
final Map<String, Map<String, String>> named = new HashMap<>();
for (final var entry : GLSMRedirector.getNamedClassMethodRedirects().entrySet()) {
final String internalName = entry.getKey();
final String shortName = internalName.substring(internalName.lastIndexOf('/') + 1);
named.put(shortName, entry.getValue());
}
return named;
}
private static Pattern buildMethodRedirectPattern() {
final TreeSet<String> alternatives = new TreeSet<>();
alternatives.add(Pattern.quote(SHORT_GL_PREFIX) + "\\w+");
for (final String shortName : NAMED_CLASS_SHORT_NAME_MAP.keySet()) {
alternatives.add(Pattern.quote(shortName));
}
final String classAlt = String.join("|", alternatives);
return Pattern.compile("\\b(" + classAlt + ")\\.\\w+\\b");
}
}
@@ -334,6 +334,7 @@ public class BatchingFontRenderer {
batchCommands.sort(FontDrawCmd.DRAW_ORDER_COMPARATOR);
final boolean isTextureEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D);
final boolean isBlendEnabledBefore = GLStateManager.glIsEnabled(GL11.GL_BLEND);
final int boundTextureBefore = GLStateManager.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
boolean textureChanged = false;
@@ -417,6 +418,9 @@ public class BatchingFontRenderer {
if (isTextureEnabledBefore) {
GLStateManager.glEnable(GL11.GL_TEXTURE_2D);
}
if (!isBlendEnabledBefore) {
GLStateManager.disableBlend();
}
if (textureChanged) {
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, boundTextureBefore);
}
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.client.rendering;
import com.github.bsideup.jabel.Desugar;
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizon.gtnhlib.client.renderer.ITessellatorInstance;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
@@ -33,10 +34,30 @@ class DeferredBatchTessellator extends DirectTessellator {
private final Vector3f scratch = new Vector3f();
private Matrix4fc currentTransform;
private Tessellator parentTessellator;
private boolean inIntercept;
DeferredBatchTessellator(ByteBuffer buffer) {
super(buffer, false); // deleteAfter=false: keep buffer across resets for reuse
}
void setParentTessellator(Tessellator parent) {
this.parentTessellator = parent;
}
@Override
public int draw() {
if (!inIntercept) {
final Tessellator parent = parentTessellator;
if (parent != null && parent.isDrawing) {
interceptDraw(parent);
((ITessellatorInstance) parent).discard();
}
}
isDrawing = false;
return 0;
}
@Override
protected long writeVertexData(VertexFormat format, int[] rawBuffer, int rawBufferIndex) {
if (currentTransform != null) {
@@ -53,27 +74,32 @@ class DeferredBatchTessellator extends DirectTessellator {
@Override
protected int interceptDraw(Tessellator tess) {
// Compute delta: if current MV differs from default, pre-transform vertices
final Matrix4f currentMV = GLStateManager.getModelViewMatrix();
if (!currentMV.equals(defaultModelview)) {
defaultModelviewInverse.mul(currentMV, deltaMatrix);
this.currentTransform = deltaMatrix;
} else {
this.currentTransform = null;
inIntercept = true;
try {
// Compute delta: if current MV differs from default, pre-transform vertices
final Matrix4f currentMV = GLStateManager.getModelViewMatrix();
if (!currentMV.equals(defaultModelview)) {
defaultModelviewInverse.mul(currentMV, deltaMatrix);
this.currentTransform = deltaMatrix;
} else {
this.currentTransform = null;
}
final int byteOffsetBefore = bufferLimit();
final int vertCount = tess.vertexCount;
final int drawMode = tess.drawMode;
final int flags = VertexFlags.convertToFlags(tess.hasTexture, tess.hasColor, tess.hasNormals, tess.hasBrightness);
final long stateKey = DeferredDrawBatcher.captureStateKey();
final int result = super.interceptDraw(tess); // repacks int[] -> ByteBuffer + transforms if vertexTransform set
final int byteOffsetAfter = bufferLimit();
ranges.add(new DrawRange(stateKey, byteOffsetBefore, byteOffsetAfter - byteOffsetBefore, vertCount, drawMode, flags));
this.currentTransform = null; // clear for next draw
return result;
} finally {
inIntercept = false;
}
final int byteOffsetBefore = bufferLimit();
final int vertCount = tess.vertexCount;
final int drawMode = tess.drawMode;
final int flags = VertexFlags.convertToFlags(tess.hasTexture, tess.hasColor, tess.hasNormals, tess.hasBrightness);
final long stateKey = DeferredDrawBatcher.captureStateKey();
final int result = super.interceptDraw(tess); // repacks int[] → ByteBuffer + transforms if vertexTransform set
final int byteOffsetAfter = bufferLimit();
ranges.add(new DrawRange(stateKey, byteOffsetBefore, byteOffsetAfter - byteOffsetBefore, vertCount, drawMode, flags));
this.currentTransform = null; // clear for next draw
return result;
}
List<DrawRange> getRanges() { return ranges; }
@@ -86,6 +112,7 @@ class DeferredBatchTessellator extends DirectTessellator {
@Override
protected void onRemovedFromStack() {
parentTessellator = null;
clearRanges();
super.onRemovedFromStack(); // calls reset(), keeps baseBuffer (deleteAfter=false)
}
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.client.rendering;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import lombok.Getter;
import net.minecraft.client.renderer.Tessellator;
import org.lwjgl.opengl.GL11;
import java.util.Comparator;
@@ -37,6 +38,7 @@ public class DeferredDrawBatcher {
batchTessellator = new DeferredBatchTessellator(memAlloc(INITIAL_BUFFER_SIZE));
}
batchTessellator.clearRanges();
batchTessellator.setParentTessellator(Tessellator.instance);
batchTessellator.snapshotDefaultModelview();
TessellatorManager.startCapturingDirect(batchTessellator);
}
@@ -1,34 +1,33 @@
package com.gtnewhorizons.angelica.compat.bettercrashes;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.mixins.interfaces.IRenderGlobalExt;
import cpw.mods.fml.common.Optional;
import net.minecraft.client.Minecraft;
import vfyjxf.bettercrashes.utils.StateManager;
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
@Optional.Interface(modid = "angelica", iface = "vfyjxf.bettercrashes.utils.StateManager.IResettable")
public class BetterCrashesCompat implements StateManager.IResettable {
private static BetterCrashesCompat INSTANCE;
public BetterCrashesCompat() {
AngelicaMod.LOGGER.info("BetterCrashesCompat initialized");
this.register();
}
public static void init() {
if (INSTANCE == null) {
INSTANCE = new BetterCrashesCompat();
}
}
public BetterCrashesCompat() {
LOGGER.info("BetterCrashesCompat initialized");
this.register();
}
@Override
public void resetState() {
LOGGER.info("Reloading SodiumRenderer");
AngelicaMod.LOGGER.info("Reloading SodiumRenderer");
((IRenderGlobalExt) Minecraft.getMinecraft().renderGlobal).angelica$reload();
LOGGER.info("Resetting GLStateManager");
AngelicaMod.LOGGER.info("Resetting GLStateManager");
GLStateManager.reset();
}
}
@@ -0,0 +1,226 @@
package com.gtnewhorizons.angelica.loading;
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.loading.fml.compat.CompatHandlers;
import com.gtnewhorizons.angelica.mixins.Mixins;
import com.gtnewhorizons.retrofuturabootstrap.SharedConfig;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
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;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.spongepowered.asm.launch.GlobalProperties;
import org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
// this is not the real IFMLLoadingPlugin, if you put any annotations
// here it won't do anything, that's why we register transformer
// exclusions manually in the constructor
public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
public static final Logger LOGGER = LogManager.getLogger("Angelica");
private static Boolean OBF_ENV;
private String[] transformerClasses;
public AngelicaClientTweaker() {
Launch.classLoader.addTransformerExclusion("jss.notfine.asm");
Launch.classLoader.addTransformerExclusion("com.gtnewhorizons.angelica.loading");
Launch.classLoader.addTransformerExclusion("com.gtnewhorizons.angelica.glsm.GLStateManager");
if (Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE) {
addLwjgl3ifyExclusions();
}
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();
final LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME);
if (AngelicaConfig.enableDebugLogging) {
loggerConfig.setLevel(Level.DEBUG);
}
ctx.updateLoggers();
// Debug features
AngelicaConfig.enableTestBlocks = Boolean.getBoolean("angelica.enableTestBlocks");
} catch (ConfigException e) {
throw new RuntimeException(e);
}
verifyDependencies();
// Register a scoped redirector early so classes prematurely loaded by other coremods during discovery/injectData
// (e.g. Mycelium using HookLoader.class.getName()) still get GL calls redirected to GLSM. Only targets specific mod
// packages to avoid interfering with mixin delegation on game classes. AngelicaLateTweaker removes this registration once
// mixins are setup.
if (FMLLaunchHandler.side().isClient()) {
final boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
if (!rfbLoaded) {
Launch.classLoader.registerTransformer("com.gtnewhorizons.angelica.loading.fml.transformers.EarlyRedirectorTransformer");
}
}
}
private static void addLwjgl3ifyExclusions() {
final var handle = SharedConfig.getRfbTransformers().stream()
.filter(transformer -> transformer.id().equals("lwjgl3ify:redirect"))
.findFirst()
.orElse(null);
if (handle != null) {
handle.exclusions().add("org.embeddedt.embeddium");
handle.exclusions().add("org.taumc.celeritas");
handle.exclusions().add("com.mitchej123.lwjgl.lwjgl3");
handle.exclusions().add("com.mitchej123.glsm");
handle.exclusions().add("com.gtnewhorizons.angelica.lwjgl3");
handle.exclusions().add("com.gtnewhorizons.angelica.vulkan");
}
}
private static void verifyDependencies() {
// Check for fastutil (bundled with GTNHLib since 0.2.1)
if (AngelicaTweaker.class.getResource("/it/unimi/dsi/fastutil/ints/Int2ObjectMap.class") == null) {
throw new RuntimeException("Missing dependency: Angelica requires GTNHLib! Download: https://modrinth.com/mod/gtnhlib");
}
// Check for PrimitiveExtractor/cel.model classes (added in GTNHLib 0.8.21)
if (AngelicaTweaker.class.getResource("/com/gtnewhorizon/gtnhlib/client/renderer/PrimitiveExtractor.class") == null) {
throw new RuntimeException("GTNHLib is outdated: Angelica requires GTNHLib 0.8.21 or newer! Download: https://modrinth.com/mod/gtnhlib");
}
// Check for jvmdowngrader stubs (bundled with GTNHLib since 0.9.0)
if (AngelicaTweaker.class.getResource("/xyz/wagyourtail/jvmdg/exc/MissingStubError.class") == null) {
throw new RuntimeException("GTNHLib is outdated: Angelica requires GTNHLib 0.9.0 or newer! Download: https://modrinth.com/mod/gtnhlib");
}
}
@Override
public String[] getASMTransformerClass() {
if (transformerClasses == null) {
final List<String> transformers = new ArrayList<>();
transformers.addAll(CompatHandlers.getTransformers());
// Add NotFine transformers
final List<String> notFineTransformers = Arrays.asList(ITransformers.getTransformers(AsmTransformers.class));
if (!notFineTransformers.isEmpty()) Namer.initNames();
transformers.addAll(notFineTransformers);
transformerClasses = transformers.toArray(new String[0]);
}
return transformerClasses;
}
@Override
public String getModContainerClass() {
return null;
}
@Override
public String getSetupClass() {
return null;
}
@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) {
// Bridge transformer compat config to blackboard for AngelicaLateTweaker
narrowTransformerConfig("DragonAPI", AngelicaConfig.transformerCompat.narrowDragonAPI);
narrowTransformerConfig("Xaeros", AngelicaConfig.transformerCompat.narrowXaeros);
narrowTransformerConfig("AdvancedLightsabers", AngelicaConfig.transformerCompat.narrowAdvancedLightsabers);
narrowTransformerConfig("Alfheim", AngelicaConfig.transformerCompat.narrowAlfheim);
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.
final boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
if (!rfbLoaded) {
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.AngelicaLateTweaker");
if (AngelicaConfig.enableCeleritas) {
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.CeleritasLateTweaker");
}
}
}
}
}
/** Publish a transformer-narrowing toggle to the blackboard; read by AngelicaLateTweaker.narrowEnabled(). */
private static void narrowTransformerConfig(String mod, boolean enabled) {
Launch.blackboard.put("angelica.narrow." + mod, enabled);
}
@Override
public String getAccessTransformerClass() {
return null;
}
@Override
public String getMixinConfig() {
final int v = Runtime.version().feature();
if (v >= 21) return "mixins.angelica.early.j21.json";
if (v >= 17) return "mixins.angelica.early.j17.json";
return "mixins.angelica.early.json";
}
@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);
}
/**
* 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!");
}
if (OBF_ENV) {
return obf;
}
return deobf;
}
}
@@ -1,34 +1,12 @@
package com.gtnewhorizons.angelica.loading;
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 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 com.gtnewhorizons.retrofuturabootstrap.SharedConfig;
import net.minecraft.launchwrapper.Launch;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.spongepowered.asm.launch.GlobalProperties;
import org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -41,195 +19,76 @@ import java.util.Set;
// They will instead use default index 0 which means they will see
// obfuscated mappings and not SRG mappings when running outside of dev env.
// ===============================================
//@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"
})
public class AngelicaTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
private static Boolean OBF_ENV;
public static final Logger LOGGER = LogManager.getLogger("Angelica");
private String[] transformerClasses;
private final IFMLLoadingPlugin loadingPlugin;
private final IEarlyMixinLoader mixinLoader;
public AngelicaTweaker() {
if (Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE) {
addLwjgl3ifyExclusions();
}
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();
final LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME);
if (AngelicaConfig.enableDebugLogging) {
loggerConfig.setLevel(Level.DEBUG);
}
ctx.updateLoggers();
// Debug features
AngelicaConfig.enableTestBlocks = Boolean.getBoolean("angelica.enableTestBlocks");
} catch (ConfigException e) {
throw new RuntimeException(e);
}
verifyDependencies();
// Register a scoped redirector early so classes prematurely loaded by other coremods during discovery/injectData
// (e.g. Mycelium using HookLoader.class.getName()) still get GL calls redirected to GLSM. Only targets specific mod
// packages to avoid interfering with mixin delegation on game classes. AngelicaLateTweaker removes this registration once
// mixins are setup.
if (FMLLaunchHandler.side().isClient()) {
final boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
if (!rfbLoaded) {
Launch.classLoader.registerTransformer("com.gtnewhorizons.angelica.loading.fml.transformers.EarlyRedirectorTransformer");
}
}
}
private static void verifyDependencies() {
// Check for fastutil (bundled with GTNHLib since 0.2.1)
if (AngelicaTweaker.class.getResource("/it/unimi/dsi/fastutil/ints/Int2ObjectMap.class") == null) {
throw new RuntimeException("Missing dependency: Angelica requires GTNHLib! Download: https://modrinth.com/mod/gtnhlib");
}
// Check for PrimitiveExtractor/cel.model classes (added in GTNHLib 0.8.21)
if (AngelicaTweaker.class.getResource("/com/gtnewhorizon/gtnhlib/client/renderer/PrimitiveExtractor.class") == null) {
throw new RuntimeException("GTNHLib is outdated: Angelica requires GTNHLib 0.8.21 or newer! Download: https://modrinth.com/mod/gtnhlib");
}
// Check for jvmdowngrader stubs (bundled with GTNHLib since 0.9.0)
if (AngelicaTweaker.class.getResource("/xyz/wagyourtail/jvmdg/exc/MissingStubError.class") == null) {
throw new RuntimeException("GTNHLib is outdated: Angelica requires GTNHLib 0.9.0 or newer! Download: https://modrinth.com/mod/gtnhlib");
final AngelicaClientTweaker clientTweaker = new AngelicaClientTweaker();
loadingPlugin = clientTweaker;
mixinLoader = clientTweaker;
} else {
loadingPlugin = null;
mixinLoader = null;
LogManager.getLogger("Angelica").info("Trying to get more FPS on your server ? :kekw:");
}
}
@Override
public String[] getASMTransformerClass() {
if (transformerClasses == null) {
final List<String> transformers = new ArrayList<>();
transformers.addAll(CompatHandlers.getTransformers());
// Add NotFine transformers
final List<String> notFineTransformers = Arrays.asList(ITransformers.getTransformers(AsmTransformers.class));
if (!notFineTransformers.isEmpty()) Namer.initNames();
transformers.addAll(notFineTransformers);
transformerClasses = transformers.toArray(new String[0]);
if (this.loadingPlugin != null) {
return this.loadingPlugin.getASMTransformerClass();
}
return transformerClasses;
return null;
}
@Override
public String getModContainerClass() {
if (this.loadingPlugin != null) {
return this.loadingPlugin.getModContainerClass();
}
return null;
}
@Override
public String getSetupClass() {
if (this.loadingPlugin != null) {
return this.loadingPlugin.getSetupClass();
}
return null;
}
/** Publish a transformer-narrowing toggle to the blackboard; read by AngelicaLateTweaker.narrowEnabled(). */
private static void narrowTransformerConfig(String mod, boolean enabled) {
Launch.blackboard.put("angelica.narrow." + mod, enabled);
}
@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) {
// Bridge transformer compat config to blackboard for AngelicaLateTweaker
narrowTransformerConfig("DragonAPI", AngelicaConfig.transformerCompat.narrowDragonAPI);
narrowTransformerConfig("Xaeros", AngelicaConfig.transformerCompat.narrowXaeros);
narrowTransformerConfig("AdvancedLightsabers", AngelicaConfig.transformerCompat.narrowAdvancedLightsabers);
narrowTransformerConfig("Alfheim", AngelicaConfig.transformerCompat.narrowAlfheim);
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.
final boolean rfbLoaded = Launch.blackboard.getOrDefault("angelica.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE;
if (!rfbLoaded) {
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.AngelicaLateTweaker");
if (AngelicaConfig.enableCeleritas) {
tweaks.add("com.gtnewhorizons.angelica.loading.fml.tweakers.CeleritasLateTweaker");
}
}
}
if (this.loadingPlugin != null) {
this.loadingPlugin.injectData(data);
}
}
@Override
public String getAccessTransformerClass() {
if (this.loadingPlugin != null) {
return this.loadingPlugin.getAccessTransformerClass();
}
return null;
}
@Override
public String getMixinConfig() {
final int v = Runtime.version().feature();
if (v >= 21) return "mixins.angelica.early.j21.json";
if (v >= 17) return "mixins.angelica.early.j17.json";
return "mixins.angelica.early.json";
if (this.mixinLoader != null) {
return this.mixinLoader.getMixinConfig();
}
return "mixins.angelica.server.json";
}
@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;
if (this.mixinLoader != null) {
return this.mixinLoader.getMixins(loadedCoreMods);
}
return IMixins.getEarlyMixins(Mixins.class, loadedCoreMods);
}
private static void addLwjgl3ifyExclusions() {
final var handle = SharedConfig.getRfbTransformers().stream()
.filter(transformer -> transformer.id().equals("lwjgl3ify:redirect"))
.findFirst()
.orElse(null);
if (handle != null) {
handle.exclusions().add("org.embeddedt.embeddium");
handle.exclusions().add("org.taumc.celeritas");
handle.exclusions().add("com.mitchej123.lwjgl.lwjgl3");
handle.exclusions().add("com.mitchej123.glsm");
handle.exclusions().add("com.gtnewhorizons.angelica.lwjgl3");
handle.exclusions().add("com.gtnewhorizons.angelica.vulkan");
}
}
/**
* 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!");
}
if (OBF_ENV) {
return obf;
}
return deobf;
return Collections.emptyList();
}
}
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
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"),
AngelicaClientTweaker.obf("instance", "field_78398_a"),
"Lnet/minecraft/client/renderer/Tessellator;"));
list.add(new VarInsnNode(Opcodes.ASTORE, localIndex));
mn.instructions.insert(list);
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.lib.tree.ClassNode;
import org.spongepowered.asm.lib.tree.InsnList;
@@ -29,7 +29,7 @@ public class HUDCachingEarlyReturnTransformer {
} else if (method.desc.endsWith("V")) {
list.add(new InsnNode(Opcodes.RETURN));
} else {
AngelicaTweaker.LOGGER.warn(
AngelicaClientTweaker.LOGGER.warn(
"HUDCaching Conditional Return - Unknown return type: {}#{}:{}", cn.name, method.name, method.desc
);
return;
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.generic;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.lib.tree.AbstractInsnNode;
import org.spongepowered.asm.lib.tree.ClassNode;
@@ -59,7 +59,7 @@ public class TileEntityNullGuardTransformer {
AbstractInsnNode in = mn.instructions.get(i);
if (in instanceof MethodInsnNode min) {
if (min.getOpcode() == Opcodes.INVOKEINTERFACE
&& min.name.equals(AngelicaTweaker.obf("getTileEntity", "func_147438_o"))
&& min.name.equals(AngelicaClientTweaker.obf("getTileEntity", "func_147438_o"))
&& min.owner.equals("net/minecraft/world/IBlockAccess")) {
AbstractInsnNode castNodeAbstract = min.getNext();
if (castNodeAbstract instanceof TypeInsnNode castNodeType) {
@@ -77,7 +77,7 @@ public class TileEntityNullGuardTransformer {
} else if (mn.desc.endsWith("V")) {
list.add(new InsnNode(Opcodes.RETURN));
} else {
AngelicaTweaker.LOGGER.warn("TileEntityNullGuard - Unknown Return Type: {}:{}", mn.name, mn.desc);
AngelicaClientTweaker.LOGGER.warn("TileEntityNullGuard - Unknown Return Type: {}:{}", mn.name, mn.desc);
return;
}
list.add(exit);
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.specific;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import net.minecraft.launchwrapper.IClassTransformer;
import org.spongepowered.asm.lib.ClassReader;
@@ -60,7 +60,7 @@ public class ImmersiveEngineeringTransformer implements IClassTransformer {
MixinClassWriter cw = new MixinClassWriter(MixinClassWriter.COMPUTE_FRAMES);
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
AngelicaTweaker.LOGGER.info("[AngelicaCompat]Extra Transformers: Applied ImmersiveEngineeringTransformer");
AngelicaClientTweaker.LOGGER.info("[AngelicaCompat]Extra Transformers: Applied ImmersiveEngineeringTransformer");
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.loading.fml.compat.transformers.specific;
import com.google.common.collect.ImmutableList;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import net.minecraft.launchwrapper.IClassTransformer;
import org.spongepowered.asm.lib.ClassReader;
@@ -47,7 +47,7 @@ public class StacksOnStacksTransformer implements IClassTransformer {
ClassWriter cw = new ClassWriter(0);
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
AngelicaTweaker.LOGGER.info("[AngelicaCompat]Extra Transformers: Applied StacksOnStacksTransformer");
AngelicaClientTweaker.LOGGER.info("[AngelicaCompat]Extra Transformers: Applied StacksOnStacksTransformer");
AngelicaClassDump.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.loading.fml.transformers;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import com.gtnewhorizons.angelica.loading.shared.AngelicaClassDump;
import com.gtnewhorizons.angelica.loading.shared.transformers.CeleritasBlockTransform;
import net.minecraft.launchwrapper.IClassTransformer;
@@ -14,7 +14,7 @@ public class CeleritasBlockTransformer implements IClassTransformer {
private final String[] exclusions;
public CeleritasBlockTransformer() {
this.inner = new CeleritasBlockTransform(AngelicaTweaker.isObfEnv());
this.inner = new CeleritasBlockTransform(AngelicaClientTweaker.isObfEnv());
this.exclusions = inner.getTransformerExclusions();
this.inner.setCeleritasSetting();
}
@@ -15,6 +15,7 @@ public class AngelicaRfbPlugin implements RfbPlugin {
@Override
public void onConstruction(@NotNull PluginContext ctx) {
Launch.blackboard.put("angelica.rfbPluginLoaded", Boolean.TRUE);
Launch.classLoader.addClassLoaderExclusion("com.gtnewhorizons.angelica.glsm.redirect.");
}
@Override
@@ -37,7 +37,10 @@ public enum Mixins implements IMixins {
, "angelica.MixinMinecraft_FrameHook"
, "angelica.MixinMinecraftServer"
, "angelica.bugfixes.MixinItemRenderer_EdgeDepth"
, "angelica.bugfixes.MixinModelWither_ArmorCentering"
, "angelica.bugfixes.MixinRenderBlocks_CrossedSquaresNormal"
, "angelica.bugfixes.MixinRenderGlobal_DestroyBlock"
, "angelica.bugfixes.MixinRenderWither_ArmorCentering"
, "angelica.bugfixes.MixinRendererLivingEntity_EyeDepth"
, "angelica.debug.MixinMinecraft_FPSCap"
, "angelica.ffp.MixinTessellator_CoreProfile"
@@ -4,10 +4,12 @@ import com.google.common.base.Objects;
import com.gtnewhorizon.gtnhlib.client.model.loading.ModelRegistry;
import com.gtnewhorizon.gtnhlib.client.renderer.vao.VAOManager;
import com.gtnewhorizons.angelica.commands.AngelicaCommand;
import com.gtnewhorizons.angelica.common.BlockError;
import com.gtnewhorizons.angelica.compat.ModStatus;
import com.gtnewhorizons.angelica.compat.bettercrashes.BetterCrashesCompat;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.config.CompatConfig;
import com.gtnewhorizons.angelica.config.ConfigMigrator;
import com.gtnewhorizons.angelica.debug.F3Direction;
import com.gtnewhorizons.angelica.debug.FrametimeGraph;
import com.gtnewhorizons.angelica.debug.TPSGraph;
@@ -16,7 +18,7 @@ import com.gtnewhorizons.angelica.dynamiclights.config.EntityLightConfig;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.hudcaching.HUDCaching;
import com.gtnewhorizons.angelica.iris.IrisGLSMBridge;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import com.gtnewhorizons.angelica.mixins.interfaces.IGameSettingsExt;
import com.gtnewhorizons.angelica.render.CloudRenderer;
import com.gtnewhorizons.angelica.rendering.AngelicaBlockSafetyRegistry;
@@ -25,6 +27,8 @@ import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasSetup;
import com.gtnewhorizons.angelica.rendering.celeritas.threading.ChunkTaskRegistry;
import com.gtnewhorizons.angelica.rendering.celeritas.threading.DefaultChunkTaskProvider;
import com.gtnewhorizons.angelica.rendering.celeritas.threading.ThreadedChunkTaskProvider;
import com.gtnewhorizons.angelica.utils.AnimationMode;
import com.gtnewhorizons.angelica.utils.ManagedEnum;
import com.gtnewhorizons.angelica.zoom.Zoom;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
@@ -39,17 +43,15 @@ import jss.notfine.core.Settings;
import jss.notfine.gui.GuiCustomMenu;
import jss.notfine.gui.NotFineGameOptionPages;
import me.flashyreese.mods.reeses_sodium_options.client.gui.ReeseSodiumVideoOptionsScreen;
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
import me.jellysquid.mods.sodium.client.gui.SodiumOptionsGUI;
import net.coderbot.iris.Iris;
import net.coderbot.iris.client.IrisDebugScreenHandler;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiVideoSettings;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.Direction;
@@ -65,29 +67,40 @@ import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
import org.lwjgl.input.Keyboard;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.management.ManagementFactory;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import static com.gtnewhorizons.angelica.AngelicaMod.MOD_ID;
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
public class ClientProxy extends CommonProxy {
public final class ClientProxy extends CommonProxy {
final Minecraft mc = Minecraft.getMinecraft();
final FrametimeGraph frametimeGraph = new FrametimeGraph();
final TPSGraph tpsGraph = new TPSGraph();
public static BlockError blockError;
public static final ManagedEnum<AnimationMode> animationsMode = new ManagedEnum<>(AnimationMode.VISIBLE_ONLY);
private static final Logger LOGGER = LogManager.getLogger("Angelica");
private static SodiumGameOptions CONFIG;
private final Minecraft mc = Minecraft.getMinecraft();
private final FrametimeGraph frametimeGraph = new FrametimeGraph();
private final TPSGraph tpsGraph = new TPSGraph();
public static SodiumGameOptions options() {
if (CONFIG == null) {
CONFIG = SodiumGameOptions.load(ConfigMigrator.handleConfigMigration("angelica-options.json"));
}
return CONFIG;
}
@Override
public void preInit(FMLPreInitializationEvent event) {
ModStatus.preInit();
super.preInit(event);
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
ModelRegistry.registerModid(MOD_ID);
blockError = new BlockError();
}
@SubscribeEvent
@@ -138,7 +151,6 @@ public class ClientProxy extends CommonProxy {
FMLCommonHandler.instance().bus().register(this);
MinecraftForge.EVENT_BUS.register(this);
if (ModStatus.isBetterCrashesLoaded) {
BetterCrashesCompat.init();
}
@@ -150,7 +162,7 @@ public class ClientProxy extends CommonProxy {
}
// Register debug commands in dev environment only
if (!AngelicaTweaker.isObfEnv()) {
if (!AngelicaClientTweaker.isObfEnv()) {
ClientCommandHandler.instance.registerCommand(new AngelicaCommand());
}
}
@@ -10,8 +10,7 @@ import cpw.mods.fml.common.registry.GameRegistry;
public class CommonProxy {
public void preInit(FMLPreInitializationEvent event) {
if (AngelicaConfig.enableTestBlocks){
if (AngelicaConfig.enableTestBlocks) {
GameRegistry.registerBlock(new BlockTest(), "test_block");
}
}
@@ -0,0 +1,5 @@
package com.gtnewhorizons.angelica.rendering;
public class WitherArmorState {
public static boolean pendingInflate = false;
}
@@ -1,11 +1,11 @@
package com.gtnewhorizons.angelica.rendering.celeritas;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.api.ExtQuadLightData;
import com.gtnewhorizons.angelica.api.TintComputer;
import com.gtnewhorizons.angelica.api.TintRegistry;
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightSource;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import com.gtnewhorizons.angelica.rendering.StateAwareTessellator;
import com.gtnewhorizons.angelica.rendering.celeritas.iris.BlockRenderContext;
import com.gtnewhorizons.angelica.rendering.celeritas.iris.IrisExtendedChunkVertexEncoder;
@@ -99,7 +99,7 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
private Material selectMaterial(Material material, TextureAtlasSprite sprite, boolean isShaderPackOverride) {
// Don't apply transparency-based optimization when shader pack explicitly overrides the material
if (!AngelicaMod.options().performance.useRenderPassOptimization || isShaderPackOverride) {
if (!ClientProxy.options().performance.useRenderPassOptimization || isShaderPackOverride) {
return material;
}
if (sprite != null && sprite.getClass() == TextureAtlasSprite.class && !sprite.hasAnimationMetadata()) {
@@ -129,7 +129,7 @@ public class AngelicaChunkBuildContext extends ChunkBuildContext {
final boolean hasDynamicLights = chunkLightSources != null && !chunkLightSources.isEmpty();
final boolean separateAo = BlockRenderingSettings.INSTANCE.shouldUseSeparateAo();
final boolean celeritasSmoothLighting = AngelicaMod.options().quality.useCeleritasSmoothLighting;
final boolean celeritasSmoothLighting = ClientProxy.options().quality.useCeleritasSmoothLighting;
final boolean shaderActive = IrisApi.getInstance().isShaderPackInUse();
final boolean useAoCalculation = lightPipelineReady && (separateAo || celeritasSmoothLighting || shaderActive);
final boolean shouldApplyDiffuse = !BlockRenderingSettings.INSTANCE.shouldDisableDirectionalShading();
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.rendering.celeritas;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProvider;
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
import me.jellysquid.mods.sodium.client.gui.options.named.MultiDrawMode;
@@ -30,7 +31,7 @@ class AngelicaChunkRenderer extends DefaultChunkRenderer {
}
private static MultiDrawEmitter createEmitter() {
MultiDrawMode mode = AngelicaMod.options().advanced.multiDrawMode;
MultiDrawMode mode = ClientProxy.options().advanced.multiDrawMode;
if (mode == MultiDrawMode.INDIRECT) {
boolean supported = GLStateManager.capabilities != null && (GLStateManager.capabilities.OpenGL43 || GLStateManager.capabilities.GL_ARB_multi_draw_indirect);
@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.rendering.celeritas;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.mixins.interfaces.RenderSectionManagerAccessor;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import com.gtnewhorizons.angelica.rendering.AngelicaRenderQueue;
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
import com.gtnewhorizons.angelica.rendering.celeritas.threading.ChunkTaskProvider;
@@ -87,7 +87,7 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
@Override
protected AsyncOcclusionMode getAsyncOcclusionMode() {
return AngelicaMod.options().performance.asyncOcclusionMode;
return ClientProxy.options().performance.asyncOcclusionMode;
}
@Override
@@ -97,7 +97,7 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
@Override
protected boolean useFogOcclusion() {
return AngelicaMod.options().performance.useFogOcclusion && !IrisShaderProviderHolder.isActive();
return ClientProxy.options().performance.useFogOcclusion && !IrisShaderProviderHolder.isActive();
}
@Override
@@ -113,7 +113,7 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
return false;
}
}
return AngelicaMod.options().performance.useOcclusionCulling;
return ClientProxy.options().performance.useOcclusionCulling;
}
@Override
@@ -152,7 +152,7 @@ public class AngelicaRenderSectionManager extends RenderSectionManager {
@Override
protected boolean allowImportantRebuilds() {
return !AngelicaMod.options().performance.alwaysDeferChunkUpdates;
return !ClientProxy.options().performance.alwaysDeferChunkUpdates;
}
@Override
@@ -2,9 +2,9 @@ package com.gtnewhorizons.angelica.rendering.celeritas;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import org.embeddedt.embeddium.impl.gl.device.GLRenderDevice;
import org.lwjgl.opengl.GL15;
import static com.gtnewhorizons.angelica.loading.AngelicaTweaker.LOGGER;
public class CeleritasSetup {
private static boolean initialized = false;
@@ -12,7 +12,7 @@ public class CeleritasSetup {
public static void ensureInitialized() {
if (!initialized && AngelicaConfig.enableCeleritas) {
GLRenderDevice.VANILLA_STATE_RESETTER = () -> GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
LOGGER.debug("Celeritas init");
AngelicaClientTweaker.LOGGER.debug("Celeritas init");
initialized = true;
}
}
@@ -1,10 +1,10 @@
package com.gtnewhorizons.angelica.rendering.celeritas;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
import com.gtnewhorizons.angelica.dynamiclights.IDynamicLightWorldRenderer;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.mixins.interfaces.ITileEntityBoundingBoxCache;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import com.gtnewhorizons.angelica.rendering.RenderingState;
import com.gtnewhorizons.angelica.rendering.TileEntityRenderBoundsRegistry;
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProvider;
@@ -127,7 +127,7 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
}
}
if (AngelicaMod.options().performance.useCompactVertexFormat) {
if (ClientProxy.options().performance.useCompactVertexFormat) {
return ChunkMeshFormats.COMPACT;
}
return ChunkMeshFormats.VANILLA_LIKE;
@@ -155,7 +155,7 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
renderSectionManager.setCameraPosition(transform.x, transform.y, transform.z);
this.useEntityCulling = AngelicaMod.options().performance.useEntityCulling;
this.useEntityCulling = ClientProxy.options().performance.useEntityCulling;
super.setupTerrain(viewport, cameraState, frame, spectator, updateChunksImmediately);
@@ -313,7 +313,7 @@ public class CeleritasWorldRenderer extends SimpleWorldRenderer<WorldClient, Ang
public int renderBlockEntities(float partialTicks) {
final int pass = MinecraftForgeClient.getRenderPass();
teRenderContext.set(partialTicks, pass);
if (pass == 0 || !AngelicaMod.options().performance.translucencySorting) {
if (pass == 0 || !ClientProxy.options().performance.translucencySorting) {
return super.renderBlockEntities(teRenderContext);
}
int count = 0;
+2 -2
View File
@@ -2,7 +2,7 @@ package jss.notfine.asm;
import java.util.Objects;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FrameNode;
@@ -133,7 +133,7 @@ public class ASMUtils {
yield false;
}
default -> {
AngelicaTweaker.LOGGER.warn("Unchecked node found: " + node1.getClass());
AngelicaClientTweaker.LOGGER.warn("Unchecked node found: " + node1.getClass());
yield node1.toString()
.equals(node2.toString());
}
@@ -1,10 +1,10 @@
package jss.notfine.asm.mappings;
import java.util.ArrayList;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import net.minecraft.launchwrapper.Launch;
import java.util.ArrayList;
/**
* This class und inherited classes are adapted from
* <a href=
@@ -48,7 +48,7 @@ public class Namer {
public static void initNames() {
final boolean obfuscated = !(Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment");
AngelicaTweaker.LOGGER.info("Environment obfuscated: {}", obfuscated);
AngelicaClientTweaker.LOGGER.info("Environment obfuscated: {}", obfuscated);
if (obfuscated) {
new NamerObf().setNames();
} else {
@@ -1,16 +1,16 @@
package jss.notfine.asm.mappings;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import jss.notfine.asm.mappings.Names.Clas;
import jss.notfine.asm.mappings.Names.Fiel;
import jss.notfine.asm.mappings.Names.Meth;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import jss.notfine.asm.mappings.Names.Clas;
import jss.notfine.asm.mappings.Names.Fiel;
import jss.notfine.asm.mappings.Names.Meth;
public class NamerMcp extends NamerSrg {
@Override
@@ -71,15 +71,15 @@ public class NamerMcp extends NamerSrg {
nameMap = loadReobfMap(confPath + "packaged.srg");
for (Clas c : ac) {
String s = nameMap.get(c.name);
AngelicaTweaker.LOGGER.trace("C {} {}", c.name, s);
AngelicaClientTweaker.LOGGER.trace("C {} {}", c.name, s);
}
for (Fiel f : af) {
String s = nameMap.get(f.clas + "/" + f.name);
AngelicaTweaker.LOGGER.trace("F {} {}", f.name, s);
AngelicaClientTweaker.LOGGER.trace("F {} {}", f.name, s);
}
for (Meth m : am) {
String s = nameMap.get(m.clas + "/" + m.name + m.desc);
AngelicaTweaker.LOGGER.trace("M {} {}", m.name, s);
AngelicaClientTweaker.LOGGER.trace("M {} {}", m.name, s);
}
}
@@ -1,6 +1,7 @@
package me.jellysquid.mods.sodium.client.gui.options.storage;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptions;
import java.io.IOException;
@@ -9,7 +10,7 @@ public class SodiumOptionsStorage implements OptionStorage<SodiumGameOptions> {
private final SodiumGameOptions options;
public SodiumOptionsStorage() {
this.options = AngelicaMod.options();
this.options = ClientProxy.options();
}
@Override
+9 -14
View File
@@ -3,32 +3,30 @@ package net.coderbot.iris;
import com.google.common.base.Throwables;
import com.gtnewhorizon.gtnhlib.client.renderer.CapturingTessellator;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.Tags;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.InputEvent;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import lombok.Getter;
import net.coderbot.iris.block_context.BlockContextHolder;
import net.coderbot.iris.block_rendering.BlockRenderingSettings;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import net.coderbot.iris.celeritas.IrisCeleritasShaderProvider;
import com.gtnewhorizons.angelica.rendering.celeritas.api.IrisShaderProviderHolder;
import net.coderbot.iris.compat.dh.DHCompat;
import net.coderbot.iris.config.IrisConfig;
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
import net.coderbot.iris.gl.shader.StandardMacros;
import net.coderbot.iris.gui.screen.ShaderPackScreen;
import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline;
import net.coderbot.iris.pipeline.transform.ShaderTransformer;
import net.coderbot.iris.pipeline.transform.TransformPatcher;
import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability;
import net.coderbot.iris.pipeline.FixedFunctionWorldRenderingPipeline;
import net.coderbot.iris.pipeline.PipelineManager;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
import net.coderbot.iris.pipeline.transform.ShaderTransformer;
import net.coderbot.iris.pipeline.transform.TransformPatcher;
import net.coderbot.iris.shaderpack.OptionalBoolean;
import net.coderbot.iris.shaderpack.ProgramSet;
import net.coderbot.iris.shaderpack.ShaderPack;
@@ -37,7 +35,6 @@ import net.coderbot.iris.shaderpack.option.OptionSet;
import net.coderbot.iris.shaderpack.option.Profile;
import net.coderbot.iris.shaderpack.option.values.MutableOptionValues;
import net.coderbot.iris.shaderpack.option.values.OptionValues;
import net.coderbot.iris.block_context.BlockContextHolder;
import net.coderbot.iris.texture.pbr.PBRTextureManager;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
@@ -66,7 +63,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -74,6 +70,7 @@ import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.zip.ZipError;
@@ -357,7 +354,7 @@ public class Iris {
final boolean released = !Keyboard.getEventKeyState();
if (Minecraft.getMinecraft().gameSettings.showDebugInfo && GuiScreen.isShiftKeyDown() && GuiScreen.isCtrlKeyDown() && released) {
if (key == Keyboard.KEY_N) {
AngelicaMod.animationsMode.next();
ClientProxy.animationsMode.next();
}
}
}
@@ -482,11 +479,9 @@ public class Iris {
// Attempt to load an external shaderpack if it is available
final Optional<String> externalName = irisConfig.getShaderPackName();
if (!externalName.isPresent()) {
if (externalName.isEmpty()) {
logger.info("Shaders are disabled because no valid shaderpack is selected");
setShadersDisabled();
return;
}
@@ -1,7 +1,7 @@
package net.coderbot.iris.client;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.coderbot.iris.Iris;
@@ -47,7 +47,7 @@ public class IrisDebugScreenHandler {
event.right.add("[" + Iris.MODNAME + "] Shaders are disabled");
}
if (AngelicaConfig.speedupAnimations) {
event.right.add(Math.min(event.right.size(), 9), "animationsMode: " + AngelicaMod.animationsMode);
event.right.add(Math.min(event.right.size(), 9), "animationsMode: " + ClientProxy.animationsMode);
}
Iris.getPipelineManager().getPipeline().ifPresent(pipeline -> pipeline.addDebugText(event.left));
@@ -6,4 +6,5 @@ package net.coderbot.iris.gl.state;
public class StateUpdateNotifiers {
public static ValueUpdateNotifier normalTextureChangeNotifier;
public static ValueUpdateNotifier specularTextureChangeNotifier;
public static ValueUpdateNotifier phaseChangeNotifier;
}
@@ -1,7 +1,7 @@
package net.coderbot.iris.gui.screen;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import net.coderbot.iris.Iris;
import net.coderbot.iris.gui.GuiUtil;
import net.coderbot.iris.gui.NavigationController;
@@ -460,7 +460,7 @@ public class ShaderPackScreen extends GuiScreen implements HudHideable {
}
if (openViaSystemClass) {
AngelicaTweaker.LOGGER.debug("Opening via system class!");
AngelicaMod.LOGGER.debug("Opening via system class!");
Sys.openURL("file://" + uri);
}
}
@@ -4,6 +4,7 @@ import net.coderbot.iris.Iris;
import net.coderbot.iris.gbuffer_overrides.matching.SpecialCondition;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import net.coderbot.iris.gl.shader.ProgramCreator;
import net.coderbot.iris.gl.state.StateUpdateNotifiers;
import net.coderbot.iris.pipeline.WorldRenderingPhase;
import net.coderbot.iris.pipeline.WorldRenderingPipeline;
@@ -11,7 +12,12 @@ public class GbufferPrograms {
private static boolean entities;
private static boolean blockEntities;
private static boolean outline;
private static Runnable phaseChangeListener;
static {
StateUpdateNotifiers.phaseChangeNotifier = listener -> phaseChangeListener = listener;
}
private static void checkReentrancy() {
if (entities || blockEntities || outline) {
throw new IllegalStateException("GbufferPrograms in weird state, tried to call begin function when entities = "
@@ -106,7 +112,13 @@ public class GbufferPrograms {
public static void teardownSpecialRenderCondition() {
Iris.getPipelineManager().getPipeline().ifPresent(p -> p.setSpecialCondition(null));
}
public static void runPhaseChangeNotifier() {
if (phaseChangeListener != null) {
phaseChangeListener.run();
}
}
public static void init() {
// Empty initializer to run static
}
@@ -41,6 +41,7 @@ import net.coderbot.iris.gl.state.FogMode;
import net.coderbot.iris.gl.texture.DepthBufferFormat;
import net.coderbot.iris.gl.texture.TextureType;
import net.coderbot.iris.helpers.Tri;
import net.coderbot.iris.layer.GbufferPrograms;
import net.coderbot.iris.pipeline.transform.PatchShaderType;
import net.coderbot.iris.pipeline.transform.TransformPatcher;
import net.coderbot.iris.postprocess.BufferFlipper;
@@ -200,14 +201,17 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
public DeferredWorldRenderingPipeline(ProgramSet programs) {
Objects.requireNonNull(programs);
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> prepareTransformFutures = submitCompositeTransforms(programs.getPrepare());
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> deferredTransformFutures = submitCompositeTransforms(programs.getDeferred());
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> compositeTransformFutures = submitCompositeTransforms(programs.getComposite());
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> prepareTransformFutures =
submitCompositeTransforms(programs.getPrepare(), TextureStage.PREPARE, programs.getPackDirectives().getTextureMap());
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> deferredTransformFutures =
submitCompositeTransforms(programs.getDeferred(), TextureStage.DEFERRED, programs.getPackDirectives().getTextureMap());
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> compositeTransformFutures =
submitCompositeTransforms(programs.getComposite(), TextureStage.COMPOSITE_AND_FINAL, programs.getPackDirectives().getTextureMap());
final CompletableFuture<Map<PatchShaderType, String>> finalTransformFuture =
programs.getCompositeFinal()
.filter(ProgramSource::isValid)
.map(DeferredWorldRenderingPipeline::submitCompositeTransform)
.map(source -> submitCompositeTransform(source, TextureStage.COMPOSITE_AND_FINAL, programs.getPackDirectives().getTextureMap()))
.orElse(null);
resolver = new ProgramFallbackResolver(programs);
@@ -771,10 +775,10 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
if (!isRenderingWorld || isRenderingFullScreenPass || isPostChain || !isMainBound) {
return;
}
final RenderCondition condition = getCondition(getPhase());
final Pass matched = table.match(condition, inputs);
beginPass(matched);
}
@@ -1653,12 +1657,14 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
public void setOverridePhase(WorldRenderingPhase phase) {
this.overridePhase = phase;
matchPass();
GbufferPrograms.runPhaseChangeNotifier();
}
@Override
public void setPhase(WorldRenderingPhase phase) {
this.phase = phase;
matchPass();
GbufferPrograms.runPhaseChangeNotifier();
}
@Override
@@ -1710,11 +1716,20 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
private static final InputAvailability INPUT_TEXTURE_LIGHTMAP = new InputAvailability(true, true);
private static final InputAvailability[] INPUT_AVAILABILITIES = { INPUT_NONE, INPUT_TEXTURE, INPUT_TEXTURE_LIGHTMAP };
private static CompletableFuture<Map<PatchShaderType, String>> submitCompositeTransform(ProgramSource source) {
return Iris.ShaderTransformExecutor.submitTracked(() -> TransformPatcher.patchComposite(source.getVertexSource().orElse(null), source.getGeometrySource().orElse(null), source.getTessControlSource().orElse(null), source.getTessEvalSource().orElse(null), source.getFragmentSource().orElse(null)));
private static CompletableFuture<Map<PatchShaderType, String>> submitCompositeTransform(ProgramSource source, TextureStage stage,
Object2ObjectMap<Tri<String, TextureType, TextureStage>, String> textureMap) {
return Iris.ShaderTransformExecutor.submitTracked(() -> TransformPatcher.patchComposite(
source.getVertexSource().orElse(null),
source.getGeometrySource().orElse(null),
source.getTessControlSource().orElse(null),
source.getTessEvalSource().orElse(null),
source.getFragmentSource().orElse(null),
stage,
textureMap));
}
private static Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> submitCompositeTransforms(ProgramSource[] sources) {
private static Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> submitCompositeTransforms(ProgramSource[] sources,
TextureStage stage, Object2ObjectMap<Tri<String, TextureType, TextureStage>, String> textureMap) {
// Count valid sources for initial capacity
int count = 0;
for (ProgramSource source : sources) {
@@ -1723,7 +1738,7 @@ public class DeferredWorldRenderingPipeline implements WorldRenderingPipeline, R
final Map<Integer, CompletableFuture<Map<PatchShaderType, String>>> futures = new HashMap<>(count);
for (int i = 0; i < sources.length; i++) {
if (sources[i] != null && sources[i].isValid()) {
futures.put(i, submitCompositeTransform(sources[i]));
futures.put(i, submitCompositeTransform(sources[i], stage, textureMap));
}
}
return futures;
@@ -448,6 +448,7 @@ public class ShaderTransformer {
default:
throw new IllegalStateException("Unknown patch type: " + patchType.name());
}
TextureTransformer.transform(transformer, parameters);
CompatibilityTransformer.transformEach(transformer, parameters);
}
@@ -0,0 +1,51 @@
package net.coderbot.iris.pipeline.transform;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import net.coderbot.iris.gl.texture.TextureType;
import net.coderbot.iris.helpers.Tri;
import net.coderbot.iris.pipeline.transform.parameter.Parameters;
import net.coderbot.iris.shaderpack.texture.TextureStage;
import org.taumc.glsl.Transformer;
import org.taumc.glsl.grammar.GLSLLexer;
final class TextureTransformer {
private TextureTransformer() {
}
static void transform(Transformer transformer, Parameters parameters) {
final Object2ObjectMap<Tri<String, TextureType, TextureStage>, String> textureMap = parameters.getTextureMap();
if (textureMap == null || textureMap.isEmpty()) {
return;
}
final TextureStage stage = parameters.getTextureStage();
textureMap.forEach((key, replacement) -> {
if (key.third() != stage) {
return;
}
final String name = key.first();
final int type = transformer.findType(name);
if (type != 0 && isTypeValid(key.second(), type)) {
transformer.rename(name, replacement);
}
});
}
private static boolean isTypeValid(TextureType expectedType, int extractedType) {
return switch (expectedType) {
case TEXTURE_1D -> extractedType == GLSLLexer.SAMPLER1D ||
extractedType == GLSLLexer.ISAMPLER1D ||
extractedType == GLSLLexer.USAMPLER1D;
case TEXTURE_RECTANGLE -> extractedType == GLSLLexer.SAMPLER2DRECT ||
extractedType == GLSLLexer.ISAMPLER2DRECT ||
extractedType == GLSLLexer.USAMPLER2DRECT;
case TEXTURE_2D -> extractedType == GLSLLexer.SAMPLER2D ||
extractedType == GLSLLexer.ISAMPLER2D ||
extractedType == GLSLLexer.USAMPLER2D;
case TEXTURE_3D -> extractedType == GLSLLexer.SAMPLER3D ||
extractedType == GLSLLexer.ISAMPLER3D ||
extractedType == GLSLLexer.USAMPLER3D;
};
}
}
@@ -120,10 +120,6 @@ public class TransformPatcher {
return patchComposite(vertex, geometry, null, null, fragment, TextureStage.COMPOSITE_AND_FINAL, null);
}
public static Map<PatchShaderType, String> patchComposite(String vertex, String geometry, String tessControl, String tessEval, String fragment) {
return patchComposite(vertex, geometry, tessControl, tessEval, fragment, TextureStage.COMPOSITE_AND_FINAL, null);
}
public static Map<PatchShaderType, String> patchComposite(String vertex, String geometry, String tessControl, String tessEval, String fragment, TextureStage stage, Object2ObjectMap<Tri<String, TextureType, TextureStage>, String> textureMap) {
return transform(vertex, geometry, tessControl, tessEval, fragment, new TextureStageParameters(Patch.COMPOSITE, stage, textureMap));
}
@@ -146,7 +146,7 @@ public class FinalPassRenderer {
GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, 0);
}
private static Map<PatchShaderType, String> getTransformed(ProgramSource source, CompletableFuture<Map<PatchShaderType, String>> precomputedTransformFuture, String stageName) {
private Map<PatchShaderType, String> getTransformed(ProgramSource source, CompletableFuture<Map<PatchShaderType, String>> precomputedTransformFuture, String stageName) {
if (precomputedTransformFuture != null) {
try {
final Map<PatchShaderType, String> result = precomputedTransformFuture.join();
@@ -157,7 +157,14 @@ public class FinalPassRenderer {
throw new RuntimeException("Shader transformation failed for '" + source.getName() + "' in stage '" + stageName + "'", e.getCause() != null ? e.getCause() : e);
}
}
return TransformPatcher.patchComposite(source.getVertexSource().orElseThrow(NullPointerException::new), source.getGeometrySource().orElse(null), source.getTessControlSource().orElse(null), source.getTessEvalSource().orElse(null), source.getFragmentSource().orElseThrow(NullPointerException::new));
return TransformPatcher.patchComposite(
source.getVertexSource().orElseThrow(NullPointerException::new),
source.getGeometrySource().orElse(null),
source.getTessControlSource().orElse(null),
source.getTessEvalSource().orElse(null),
source.getFragmentSource().orElseThrow(NullPointerException::new),
TextureStage.COMPOSITE_AND_FINAL,
pipeline != null ? pipeline.getTextureMap() : null);
}
private static final class Pass {
@@ -263,7 +263,9 @@ public class ShadowCompositeRenderer {
source.getGeometrySource().orElse(null),
source.getTessControlSource().orElse(null),
source.getTessEvalSource().orElse(null),
source.getFragmentSource().orElseThrow(NullPointerException::new));
source.getFragmentSource().orElseThrow(NullPointerException::new),
TextureStage.SHADOWCOMP,
pipeline.getTextureMap());
String vertex = transformed.get(PatchShaderType.VERTEX);
String geometry = transformed.get(PatchShaderType.GEOMETRY);
String tessControl = transformed.get(PatchShaderType.TESS_CONTROL);
@@ -9,6 +9,7 @@ import com.gtnewhorizons.angelica.client.rendering.TextureTracker;
import com.gtnewhorizons.angelica.mixins.interfaces.EntityRendererAccessor;
import net.coderbot.iris.compat.dh.DHCompat;
import net.coderbot.iris.gl.state.FogMode;
import net.coderbot.iris.gl.state.StateUpdateNotifiers;
import net.coderbot.iris.gl.state.ValueUpdateNotifier;
import net.coderbot.iris.gl.uniform.DynamicUniformHolder;
import net.coderbot.iris.gl.uniform.UniformHolder;
@@ -100,7 +101,7 @@ public final class CommonUniforms {
return ZERO_VECTOR_4i;
}, ValueUpdateNotifier.NONE);
uniforms.uniform1i("renderStage", () -> GbufferPrograms.getCurrentPhase().ordinal(), ValueUpdateNotifier.NONE);
uniforms.uniform1i("renderStage", () -> GbufferPrograms.getCurrentPhase().ordinal(), StateUpdateNotifiers.phaseChangeNotifier);
uniforms.uniform4f("entityColor", CapturedRenderingState.INSTANCE::getCurrentEntityColor, CapturedRenderingState.INSTANCE.getEntityColorNotifier());
@@ -0,0 +1,8 @@
{
"required": true,
"minVersion": "0.8.3-GTNH",
"refmap": "mixins.angelica.refmap.json",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8"
}
@@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.mixins.early.angelica;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.client.rendering.TessellatorStreamingDrawer;
import com.gtnewhorizons.angelica.mixins.interfaces.IGameSettingsExt;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.GameSettings;
@@ -108,7 +109,7 @@ public abstract class MixinMinecraft {
@Inject(method = "runTick", at = @At("HEAD"))
private void celeritas$renderAheadStartFrame(CallbackInfo ci) {
final int limit = AngelicaMod.options().performance.cpuRenderAheadLimit;
final int limit = ClientProxy.options().performance.cpuRenderAheadLimit;
if (limit > 0) {
celeritas$renderAheadManager.startFrame(limit);
}
@@ -116,7 +117,7 @@ public abstract class MixinMinecraft {
@Inject(method = "runTick", at = @At("RETURN"))
private void celeritas$renderAheadEndFrame(CallbackInfo ci) {
if (AngelicaMod.options().performance.cpuRenderAheadLimit > 0) {
if (ClientProxy.options().performance.cpuRenderAheadLimit > 0) {
celeritas$renderAheadManager.endFrame();
}
}
@@ -1,8 +1,8 @@
package com.gtnewhorizons.angelica.mixins.early.angelica.animation;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.mixins.interfaces.IPatchedTextureAtlasSprite;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import com.gtnewhorizons.angelica.utils.AnimationMode;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.AbstractTexture;
@@ -13,7 +13,6 @@ import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import java.util.List;
@@ -25,19 +24,16 @@ public abstract class MixinTextureMap extends AbstractTexture {
@Final
private List<TextureAtlasSprite> listAnimatedSprites;
@Unique
private static final Minecraft mc = Minecraft.getMinecraft();
/**
* @author laetansky, jss2a98aj
* @reason Only update visible animations; use UV auto-marking via IPatchedTextureAtlasSprite
*/
@Overwrite
public void updateAnimations() {
final boolean renderAll = AngelicaMod.animationsMode.is(AnimationMode.ALL);
final boolean renderVisible = AngelicaMod.animationsMode.is(AnimationMode.VISIBLE_ONLY);
final boolean renderAll = ClientProxy.animationsMode.is(AnimationMode.ALL);
final boolean renderVisible = ClientProxy.animationsMode.is(AnimationMode.VISIBLE_ONLY);
mc.mcProfiler.startSection("updateAnimations");
Minecraft.getMinecraft().mcProfiler.startSection("updateAnimations");
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, this.getGlTextureId());
final int size = listAnimatedSprites.size();
@@ -53,6 +49,6 @@ public abstract class MixinTextureMap extends AbstractTexture {
patched.updateAnimationsDryRun();
}
}
mc.mcProfiler.endSection();
Minecraft.getMinecraft().mcProfiler.endSection();
}
}
@@ -0,0 +1,71 @@
package com.gtnewhorizons.angelica.mixins.early.angelica.bugfixes;
import com.gtnewhorizons.angelica.rendering.WitherArmorState;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.client.model.ModelWither;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Creates a centered armor model for the Wither.
*
* Vanilla 1.7.10 uses glScalef(1.1) on the base model for the armor overlay, which
* shifts off-center and rotating parts away from their correct positions. Modern Minecraft
* (1.21+) replaced this with a separate model using CubeDeformation(0.5).
* This mixin backports that approach.
*/
@Mixin(ModelWither.class)
public class MixinModelWither_ArmorCentering {
// Contains body segments: [0] shoulder, [1] upper body + ribs, [2] tail
@Shadow private ModelRenderer[] field_82905_a;
// Heads: [0] center, [1] left, [2] right
@Shadow private ModelRenderer[] field_82904_b;
private static final float ARMOR_INFLATE = 0.5F;
// If the armor inflate flag is set, rebuild all boxes with uniform inflate.
@Inject(method = "<init>", at = @At("TAIL"))
private void angelica$applyInflate(CallbackInfo ci) {
if (!WitherArmorState.pendingInflate) return;
WitherArmorState.pendingInflate = false;
ModelWither self = (ModelWither) (Object) this;
// [0] Shoulder
field_82905_a[0] = new ModelRenderer(self, 0, 16);
field_82905_a[0].addBox(-10.0F, 3.9F, -0.5F, 20, 3, 3, ARMOR_INFLATE);
// [1] Upper body (spine + ribs things)
field_82905_a[1] = (new ModelRenderer(self)).setTextureSize(self.textureWidth, self.textureHeight);
field_82905_a[1].setRotationPoint(-2.0F, 6.9F, -0.5F);
field_82905_a[1].setTextureOffset(0, 22).addBox(0.0F, 0.0F, 0.0F, 3, 10, 3, ARMOR_INFLATE);
field_82905_a[1].setTextureOffset(24, 22).addBox(-4.0F, 1.5F, 0.5F, 11, 2, 2, ARMOR_INFLATE);
field_82905_a[1].setTextureOffset(24, 22).addBox(-4.0F, 4.0F, 0.5F, 11, 2, 2, ARMOR_INFLATE);
field_82905_a[1].setTextureOffset(24, 22).addBox(-4.0F, 6.5F, 0.5F, 11, 2, 2, ARMOR_INFLATE);
// [2] Tail
field_82905_a[2] = new ModelRenderer(self, 12, 22);
field_82905_a[2].addBox(0.0F, 0.0F, 0.0F, 3, 6, 3, ARMOR_INFLATE);
// [0] Center head
field_82904_b[0] = new ModelRenderer(self, 0, 0);
field_82904_b[0].addBox(-4.0F, -4.0F, -4.0F, 8, 8, 8, ARMOR_INFLATE);
// [1] Left head
field_82904_b[1] = new ModelRenderer(self, 32, 0);
field_82904_b[1].addBox(-4.0F, -4.0F, -4.0F, 6, 6, 6, ARMOR_INFLATE);
field_82904_b[1].rotationPointX = -8.0F;
field_82904_b[1].rotationPointY = 4.0F;
// [2] Right head
field_82904_b[2] = new ModelRenderer(self, 32, 0);
field_82904_b[2].addBox(-4.0F, -4.0F, -4.0F, 6, 6, 6, ARMOR_INFLATE);
field_82904_b[2].rotationPointX = 10.0F;
field_82904_b[2].rotationPointY = 4.0F;
}
}
@@ -5,7 +5,7 @@
* All Rights Reserved
*
* Modifications by Angelica in accordance with LGPL v3.0
*
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
@@ -25,7 +25,7 @@
package com.gtnewhorizons.angelica.mixins.early.angelica.bugfixes;
import com.gtnewhorizons.angelica.config.AngelicaConfig;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks;
@@ -45,7 +45,7 @@ import java.util.Objects;
@Mixin(RenderBlocks.class)
public class MixinRenderBlocks_CrackFix {
@Unique
private static String[] angelica$currentCrackFixBlacklistArr;
@Unique
@@ -72,7 +72,7 @@ public class MixinRenderBlocks_CrackFix {
private boolean angelica$disableCrackFix;
@Unique
private boolean angelica$bypassRenderPassCheck;
@ModifyExpressionValue(method = "renderFaceXNeg",
at = @At(value = "FIELD",
target = "Lnet/minecraft/client/renderer/RenderBlocks;renderMinX:D",
@@ -149,24 +149,24 @@ public class MixinRenderBlocks_CrackFix {
angelica$bounds[3] = renderMaxX;
angelica$bounds[4] = renderMaxY;
angelica$bounds[5] = renderMaxZ;
if (ForgeHooksClient.getWorldRenderPass() != 0 && !angelica$bypassRenderPassCheck) {
return;
}
if (renderMinX != 0 || renderMinY != 0 || renderMinZ != 0 || renderMaxX != 1 || renderMaxY != 1 || renderMaxZ != 1) {
return;
}
double EPSILON = AngelicaConfig.blockCrackFixEpsilon;
renderMinX -= EPSILON;
renderMinY -= EPSILON;
renderMinZ -= EPSILON;
renderMaxX += EPSILON;
renderMaxY += EPSILON;
renderMaxZ += EPSILON;
switch (skipDir) {
case WEST: renderMinX = angelica$bounds[0]; break;
case DOWN: renderMinY = angelica$bounds[1]; break;
@@ -199,7 +199,7 @@ public class MixinRenderBlocks_CrackFix {
angelica$disableCrackFix = angelica$isListed(block.getClass(), false);
angelica$bypassRenderPassCheck = angelica$isListed(block.getClass(), true);
}
@Inject(method = "renderBlockByRenderType",
at = @At("RETURN"),
require = 1)
@@ -207,7 +207,7 @@ public class MixinRenderBlocks_CrackFix {
angelica$disableCrackFix = false;
angelica$bypassRenderPassCheck = false;
}
@Unique
private static boolean angelica$isListed(Class<?> clazz, boolean whitelist) {
Class<?>[] list = whitelist ? angelica$getCrackFixRenderPassWhitelist() : angelica$getCrackFixBlacklist();
@@ -221,7 +221,7 @@ public class MixinRenderBlocks_CrackFix {
}
return false;
}
@Unique
private static Class<?>[] angelica$getCrackFixBlacklist() {
if (angelica$currentCrackFixBlacklistArr != AngelicaConfig.blockCrackFixBlacklist) {
@@ -230,7 +230,7 @@ public class MixinRenderBlocks_CrackFix {
try {
return Class.forName(name);
} catch (ClassNotFoundException e) {
AngelicaTweaker.LOGGER.debug("Could not find class " + name + " for crack fix blacklist!");
AngelicaClientTweaker.LOGGER.debug("Could not find class " + name + " for crack fix blacklist!");
return null;
}
}).filter(Objects::nonNull).toArray(Class<?>[]::new);
@@ -245,7 +245,7 @@ public class MixinRenderBlocks_CrackFix {
try {
return Class.forName(name);
} catch (ClassNotFoundException e) {
AngelicaTweaker.LOGGER.debug("Could not find class " + name + " for crack fix whitelist!");
AngelicaClientTweaker.LOGGER.debug("Could not find class " + name + " for crack fix whitelist!");
return null;
}
}).filter(Objects::nonNull).toArray(Class<?>[]::new);
@@ -0,0 +1,75 @@
package com.gtnewhorizons.angelica.mixins.early.angelica.bugfixes;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.IIcon;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(RenderBlocks.class)
public abstract class MixinRenderBlocks_CrossedSquaresNormal {
@Shadow public IIcon overrideBlockTexture;
@Shadow public abstract boolean hasOverrideBlockTexture();
/**
* Vanilla sets a single (0, -1, 0) normal for all crossed-square vertices in renderBlockAsItem.
* This downward-pointing normal causes shader packs to compute incorrect shadow bias (pushing the
* shadow sample into the parent entity's body, e.g. mooshroom and anything that extends mooshrooms)
* and zero diffuse sun lighting.
*
* This mixin replaces the singular normal with per-quad face normals perpendicular to each diagonal plane.
* This gives each face correct directional lighting and shadow bias that pushes samples away from the face rather
* than into the entity body.
*/
@Redirect(
method = "renderBlockAsItem(Lnet/minecraft/block/Block;IF)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/RenderBlocks;drawCrossedSquares(Lnet/minecraft/util/IIcon;DDDF)V")
)
private void angelica$drawCrossedSquaresWithPerFaceNormals(RenderBlocks instance, IIcon icon, double x, double y, double z, float scale) {
if (this.hasOverrideBlockTexture()) {
icon = this.overrideBlockTexture;
}
final Tessellator tess = Tessellator.instance;
final double minU = icon.getMinU();
final double minV = icon.getMinV();
final double maxU = icon.getMaxU();
final double maxV = icon.getMaxV();
final double d7 = 0.45D * scale;
final double x0 = x + 0.5D - d7;
final double x1 = x + 0.5D + d7;
final double z0 = z + 0.5D - d7;
final double z1 = z + 0.5D + d7;
final double yTop = y + (double) scale;
final float inv_sqrt_2 = (float) (1.0 / Math.sqrt(2.0));
tess.setNormal(inv_sqrt_2, 0.0F, -inv_sqrt_2);
tess.addVertexWithUV(x0, yTop, z0, minU, minV);
tess.addVertexWithUV(x0, y, z0, minU, maxV);
tess.addVertexWithUV(x1, y, z1, maxU, maxV);
tess.addVertexWithUV(x1, yTop, z1, maxU, minV);
tess.setNormal(-inv_sqrt_2, 0.0F, inv_sqrt_2);
tess.addVertexWithUV(x1, yTop, z1, minU, minV);
tess.addVertexWithUV(x1, y, z1, minU, maxV);
tess.addVertexWithUV(x0, y, z0, maxU, maxV);
tess.addVertexWithUV(x0, yTop, z0, maxU, minV);
tess.setNormal(-inv_sqrt_2, 0.0F, -inv_sqrt_2);
tess.addVertexWithUV(x0, yTop, z1, minU, minV);
tess.addVertexWithUV(x0, y, z1, minU, maxV);
tess.addVertexWithUV(x1, y, z0, maxU, maxV);
tess.addVertexWithUV(x1, yTop, z0, maxU, minV);
tess.setNormal(inv_sqrt_2, 0.0F, inv_sqrt_2);
tess.addVertexWithUV(x1, yTop, z0, minU, minV);
tess.addVertexWithUV(x1, y, z0, minU, maxV);
tess.addVertexWithUV(x0, y, z1, maxU, maxV);
tess.addVertexWithUV(x0, yTop, z1, maxU, minV);
}
}
@@ -0,0 +1,58 @@
package com.gtnewhorizons.angelica.mixins.early.angelica.bugfixes;
import com.gtnewhorizons.angelica.rendering.WitherArmorState;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelWither;
import net.minecraft.client.renderer.entity.RenderWither;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Replaces the Wither's glScalef(1.1) armor approach with a per-box inflated model.
* This fixes the armor being visually off-center on off-center and rotating parts.
* Not exactly mod friendly but we can figure that out later.
*/
@Mixin(RenderWither.class)
public class MixinRenderWither_ArmorCentering {
@Unique
private ModelWither angelica$armorModel;
@Inject(method = "<init>", at = @At("TAIL"))
private void angelica$createArmorModel(CallbackInfo ci) {
WitherArmorState.pendingInflate = true;
angelica$armorModel = new ModelWither();
}
// Use the inflated armor model instead of mainModel.
@Redirect(
method = "shouldRenderPass(Lnet/minecraft/entity/boss/EntityWither;IF)I",
at = @At(value = "INVOKE",
target = "Lnet/minecraft/client/renderer/entity/RenderWither;setRenderPassModel(Lnet/minecraft/client/model/ModelBase;)V")
)
private void angelica$useInflatedModel(RenderWither instance, ModelBase model) {
((RenderWither) (Object) this).setRenderPassModel(angelica$armorModel);
}
// No-op glTranslatef
@Redirect(
method = "shouldRenderPass(Lnet/minecraft/entity/boss/EntityWither;IF)I",
at = @At(value = "INVOKE",
target = "Lorg/lwjgl/opengl/GL11;glTranslatef(FFF)V", ordinal = 1, remap = false)
)
private void angelica$skipTranslate(float x, float y, float z) {
}
// No-op glScalef
@Redirect(
method = "shouldRenderPass(Lnet/minecraft/entity/boss/EntityWither;IF)I",
at = @At(value = "INVOKE",
target = "Lorg/lwjgl/opengl/GL11;glScalef(FFF)V", remap = false)
)
private void angelica$skipScale(float x, float y, float z) {
}
}
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.mixins.early.angelica.debug;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import cpw.mods.fml.client.SplashProgress;
@@ -19,8 +19,8 @@ import org.spongepowered.asm.mixin.injection.At;
public class MixinSplashProgress {
@WrapOperation(method="Lcpw/mods/fml/client/SplashProgress;start()V", at=@At(value="INVOKE", target="Lcpw/mods/fml/client/SplashProgress;getBool(Ljava/lang/String;Z)Z"))
private static boolean angelica$disableSplashProgress(String name, boolean def, Operation<Boolean> original) {
if(name.equals("enabled")) {
AngelicaTweaker.LOGGER.info("Disabling splash screen due to LWJGL debug mode");
if (name.equals("enabled")) {
AngelicaClientTweaker.LOGGER.info("Disabling splash screen due to LWJGL debug mode");
return false;
}
return original.call(name, def);
@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.mixins.early.angelica.particles;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.client.rendering.DeferredDrawBatcher;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import net.minecraft.client.particle.EffectRenderer;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
@@ -20,7 +20,7 @@ public class MixinEffectRenderer_DeferredBatch {
target = "Lnet/minecraft/client/renderer/Tessellator;startDrawingQuads()V",
shift = At.Shift.AFTER))
private void angelica$enterDeferredMode(Entity player, float partialTickTime, CallbackInfo ci) {
if (AngelicaMod.options() != null && AngelicaMod.options().advanced.enableDeferredBatching) {
if (ClientProxy.options() != null && ClientProxy.options().advanced.enableDeferredBatching) {
DeferredDrawBatcher.enter();
}
}
@@ -7,6 +7,7 @@ import com.gtnewhorizons.angelica.compat.DriverCompatabilityCheck;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import com.gtnewhorizons.angelica.render.SelectionBoxRenderer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.OpenGlHelper;
@@ -27,7 +28,7 @@ public class MixinInitGLStateManager {
GLStateManager.initialize(GLSMInitConfig.builder()
.displaySize(mc.displayWidth, mc.displayHeight)
.lwjglDebug(AngelicaMod.lwjglDebug)
.streamingUploadStrategy(AngelicaMod.options().advanced.streamingUploadStrategy)
.streamingUploadStrategy(ClientProxy.options().advanced.streamingUploadStrategy)
.framebufferSupported(OpenGlHelper.framebufferSupported)
.fboEnabled(mc.gameSettings.fboEnable)
.directDrawer(TessellatorStreamingDrawer::drawDirect)
@@ -1,6 +1,6 @@
package com.gtnewhorizons.angelica.mixins.early.celeritas.features.culling;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import com.gtnewhorizons.angelica.rendering.celeritas.CeleritasWorldRenderer;
import net.minecraft.client.particle.EffectRenderer;
import net.minecraft.client.particle.EntityFX;
@@ -27,7 +27,7 @@ public class MixinEffectRenderer {
@Unique
private void setupCullingViewport() {
final boolean useCulling = AngelicaMod.options().advanced.useParticleCulling;
final boolean useCulling = ClientProxy.options().advanced.useParticleCulling;
if(useCulling) {
this.cullingViewport = CeleritasWorldRenderer.getInstance().getLastViewport();
} else {
@@ -1,8 +1,8 @@
package com.gtnewhorizons.angelica.mixins.early.rendering;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.common.BlockError;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import com.gtnewhorizons.angelica.rendering.StateAwareTessellator;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.sugar.Local;
@@ -16,7 +16,6 @@ import net.minecraft.block.BlockGrass;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.init.Blocks;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import org.objectweb.asm.Opcodes;
@@ -104,7 +103,7 @@ public abstract class MixinRenderBlocks {
*/
@Inject(method = { "renderStandardBlockWithAmbientOcclusion", "renderStandardBlockWithAmbientOcclusionPartial" }, at = @At("HEAD"), cancellable = true)
private void handleCeleritasAo(Block block, int x, int y, int z, float r, float g, float b, CallbackInfoReturnable<Boolean> cir) {
if ((this.isRenderingByType && Minecraft.isAmbientOcclusionEnabled() && AngelicaMod.options().quality.useCeleritasSmoothLighting) ||
if ((this.isRenderingByType && Minecraft.isAmbientOcclusionEnabled() && ClientProxy.options().quality.useCeleritasSmoothLighting) ||
(Iris.enabled && BlockRenderingSettings.INSTANCE.shouldUseSeparateAo())) {
this.applyingCeleritasAO = true;
try {
@@ -151,13 +150,13 @@ public abstract class MixinRenderBlocks {
// Render Error Block
int meta = exceptionErrorBlockMap.getOrDefault(e.getClass(), 0);
rb.overrideBlockTexture = BlockError.icons[exceptionErrorBlockMap.getOrDefault(e.getClass(), 0)];
rb.renderStandardBlock(AngelicaMod.blockError, x, y, z);
rb.renderStandardBlock(ClientProxy.blockError, x, y, z);
rb.overrideBlockTexture = null;
// Check if we've already caught the exception for this block and log it if we haven't
String key = block.getUnlocalizedName() + ":" + meta;
if (isbrhExceptionCache.add(key)) {
AngelicaTweaker.LOGGER.warn("Caught an exception during ISBRH rendering for {} at position {}, {}, {} with renderer ID {}", block.getUnlocalizedName(), x, y, z, modelId, e);
AngelicaClientTweaker.LOGGER.warn("Caught an exception during ISBRH rendering for {} at position {}, {}, {} with renderer ID {}", block.getUnlocalizedName(), x, y, z, modelId, e);
}
}
return false;
@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.mixins.early.shaders.startup;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.loading.AngelicaTweaker;
import com.gtnewhorizons.angelica.loading.AngelicaClientTweaker;
import net.coderbot.iris.Iris;
import net.minecraft.client.renderer.OpenGlHelper;
import org.spongepowered.asm.mixin.Mixin;
@@ -14,7 +14,7 @@ public class MixinInitRenderer {
@Inject(method = "initializeTextures", at = @At("RETURN"))
private static void angelica$initializeRenderer(CallbackInfo ci) {
if (Thread.currentThread() != GLStateManager.getMainThread()) {
AngelicaTweaker.LOGGER.warn("Renderer initialization called from non-main thread!");
AngelicaClientTweaker.LOGGER.warn("Renderer initialization called from non-main thread!");
return;
}
@@ -0,0 +1,181 @@
package com.gtnewhorizons.angelica.api;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
class GLRedirectsTest {
@ParameterizedTest(name = "{0}.{1} -> {2}")
@CsvSource({
"GL11, glPushMatrix, glPushMatrix",
"GL11, glPopMatrix, glPopMatrix",
"GL11, glTranslatef, glTranslatef",
"GL11, glRotatef, glRotatef",
"GL11, glScalef, glScalef",
"GL11, glEnable, glEnable",
"GL11, glDisable, glDisable",
"GL11, glBegin, glBegin",
"GL11, glEnd, glEnd",
"GL11, glVertex3f, glVertex3f",
"GL11, glColor4f, glColor4f",
"GL11, glNormal3f, glNormal3f",
"GL11, glTexCoord2f, glTexCoord2f",
"GL11, glBindTexture, glBindTexture",
"GL11, glBlendFunc, glBlendFunc",
"GL11, glDepthMask, glDepthMask",
"GL11, glDepthFunc, glDepthFunc",
"GL11, glAlphaFunc, glAlphaFunc",
"GL11, glMatrixMode, glMatrixMode",
"GL11, glLoadIdentity, glLoadIdentity",
"GL11, glShadeModel, glShadeModel",
"GL11, glDrawArrays, glDrawArrays",
"GL11, glDrawElements, glDrawElements",
"GL11, glNewList, glNewList",
"GL11, glEndList, glEndList",
"GL11, glCallList, glCallList",
"GL11, glViewport, glViewport",
"GL12, glTexImage3D, glTexImage3D",
"GL13, glActiveTexture, glActiveTexture",
"GL13, glMultiTexCoord2f, glMultiTexCoord2f",
"GL14, glBlendFuncSeparate, tryBlendFuncSeparate",
"GL14, glBlendColor, glBlendColor",
"GL15, glGenBuffers, glGenBuffers",
"GL15, glBindBuffer, glBindBuffer",
"GL15, glBufferData, glBufferData",
"GL15, glDeleteBuffers, glDeleteBuffers",
"GL20, glUseProgram, glUseProgram",
"GL20, glCreateShader, glCreateShader",
"GL20, glShaderSource, glShaderSource",
"GL20, glLinkProgram, glLinkProgram",
"GL20, glUniform1f, glUniform1f",
"GL20, glUniform4f, glUniform4f",
"GL20, glGetUniformLocation, glGetUniformLocation",
"GL20, glVertexAttribPointer, glVertexAttribPointer",
"GL30, glGenFramebuffers, glGenFramebuffers",
"GL30, glBindFramebuffer, glBindFramebuffer",
"GL30, glGenVertexArrays, glGenVertexArrays",
"GL30, glBindVertexArray, glBindVertexArray",
"GL30, glGenerateMipmap, glGenerateMipmap",
"GL31, glDrawElementsInstanced, glDrawElementsInstanced",
"GL33, glGenSamplers, glGenSamplers",
"GL33, glBindSampler, glBindSampler",
"GL42, glTexStorage2D, glTexStorage2D",
"GL43, glDispatchCompute, glDispatchCompute",
"GL44, glBufferStorage, glBufferStorage",
})
void glVersionClassMethodRedirects(String className, String method, String expected) {
assertEquals(expected, GLRedirects.getTargetMethodName(className + "." + method));
}
@ParameterizedTest(name = "{0}.{1} -> {2}")
@CsvSource({
"GL11C, glDrawArrays, glDrawArrays",
"GL20C, glUseProgram, glUseProgram",
"GL30C, glBindFramebuffer, glBindFramebuffer",
"GL33C, glBindSampler, glBindSampler",
})
void cVariantsUseGLPrefixMap(String className, String method, String expected) {
assertEquals(expected, GLRedirects.getTargetMethodName(className + "." + method));
}
@ParameterizedTest(name = "{0}.{1} -> {2}")
@CsvSource({
"EXTBlendFuncSeparate, glBlendFuncSeparateEXT, tryBlendFuncSeparate",
"ARBMultitexture, glActiveTextureARB, glActiveTextureARB",
"ARBShaderObjects, glUseProgramObjectARB, glUseProgram",
"ARBShaderObjects, glCreateShaderObjectARB, glCreateShader",
"ARBShaderObjects, glCompileShaderARB, glCompileShader",
"ARBShaderObjects, glAttachObjectARB, glAttachShader",
"ARBShaderObjects, glUniform1fARB, glUniform1f",
"ARBShaderObjects, glUniformMatrix4ARB, glUniformMatrix4",
"ARBShaderObjects, glGetUniformLocationARB, glGetUniformLocation",
"ARBVertexArrayObject, glBindVertexArray, glBindVertexArray",
"ARBInstancedArrays, glVertexAttribDivisorARB, glVertexAttribDivisorARB",
"APPLEVertexArrayObject, glBindVertexArrayAPPLE, glBindVertexArray",
"UniversalVAO, bindVertexArray, glBindVertexArray",
"UniversalVAO, deleteVertexArrays, glDeleteVertexArrays",
"OpenGlHelper, glBlendFunc, tryBlendFuncSeparate",
"OpenGlHelper, setActiveTexture, setActiveTexture",
"OpenGlHelper, setLightmapTextureCoords, setLightmapTextureCoords",
"OpenGlHelper, isFramebufferEnabled, isFramebufferEnabled",
"GLU, gluPerspective, gluPerspective",
"GLU, gluLookAt, gluLookAt",
"GLU, gluOrtho2D, gluOrtho2D",
"Project, gluPerspective, gluPerspective",
"Project, gluPickMatrix, gluPickMatrix",
})
void namedClassMethodRedirects(String className, String method, String expected) {
assertEquals(expected, GLRedirects.getTargetMethodName(className + "." + method));
}
@ParameterizedTest(name = "{0} -> null")
@CsvSource({
"GL11.GL_TEXTURE_2D",
"GL11.GL_BLEND",
"GL20.GL_FRAGMENT_SHADER",
"Foo.bar",
"String.valueOf",
"ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB",
})
void nonRedirectedReturnsNull(String classAndMethod) {
assertNull(GLRedirects.getTargetMethodName(classAndMethod));
}
@Test
void nullDotReturnsNull() {
assertNull(GLRedirects.getTargetMethodName("noDotHere"));
}
@ParameterizedTest(name = "pattern matches {0}")
@CsvSource({
"GL11.glPushMatrix",
"GL20C.glUseProgram",
"GL44.glBufferStorage",
"ARBShaderObjects.glUseProgramObjectARB",
"OpenGlHelper.setActiveTexture",
"GLU.gluPerspective",
"UniversalVAO.bindVertexArray",
})
void patternMatchesRedirectedClasses(String input) {
assertTrue(GLRedirects.getMethodRedirectPattern().matcher(input).find(),
"Pattern should match: " + input);
}
@ParameterizedTest(name = "pattern does not match {0}")
@CsvSource({
"String.valueOf",
"Math.abs",
"Tessellator.instance",
"System.currentTimeMillis",
})
void patternDoesNotMatchUnrelatedClasses(String input) {
assertFalse(GLRedirects.getMethodRedirectPattern().matcher(input).find(),
"Pattern should not match: " + input);
}
@Test
void endToEndScriptTransformation() {
String script = "GL11.glPushMatrix();\nGL11.glTranslatef(1,2,3);\nGL11.glEnable(GL11.GL_BLEND);";
String result = GLRedirects.getMethodRedirectPattern().matcher(script).replaceAll(mr -> {
String target = GLRedirects.getTargetMethodName(mr.group(0));
return target != null ? "GLStateManager." + target : mr.group(0);
});
assertTrue(result.contains("GLStateManager.glPushMatrix()"), "glPushMatrix not redirected: " + result);
assertTrue(result.contains("GLStateManager.glTranslatef(1,2,3)"), "glTranslatef not redirected: " + result);
assertTrue(result.contains("GLStateManager.glEnable(GL11.GL_BLEND)"), "glEnable not redirected or GL_BLEND mangled: " + result);
}
@Test
void patternIsNotNull() {
assertNotNull(GLRedirects.getMethodRedirectPattern());
}
}