Standalone GLSM mod - Umbra (#1642)

This commit is contained in:
Jason Mitchell authored and GitHub committed 2026-04-03 21:51:35 -07:00
1 parent 8d53e25e0a
commit 59a4b84023
52 files changed
+2084 -158

No files matched your search

+1 -1
View File
@@ -3,7 +3,7 @@
/.idea/
/.vs/
/.vscode/
/run/
run/
build/
/eclipse/
.classpath
@@ -0,0 +1,22 @@
package com.gtnewhorizons.angelica.glsm;
/**
* Tessellator data needed by {@link com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer}.
*/
public interface ITessellatorData {
boolean isDrawing();
void setDrawing(boolean drawing);
int getVertexCount();
int[] getRawBuffer();
int getRawBufferIndex();
int getRawBufferSize();
void setRawBufferSize(int size);
void setRawBuffer(int[] buffer);
int getDrawMode();
boolean hasTexture();
boolean hasColor();
boolean hasNormals();
boolean hasBrightness();
void reset();
}
@@ -0,0 +1,32 @@
package com.gtnewhorizons.angelica.glsm.loading;
import java.util.List;
/**
* Verifies required classes/resources are present on the classpath at load time.
* Fails fast with a descriptive error message if a dependency is missing.
*/
public final class DependencyVerifier {
/**
* A single dependency check: a classpath resource that must exist.
* @param resourcePath Resource path to check (e.g. "/com/example/SomeClass.class")
* @param errorMessage Human-readable error message if missing (include download URL)
*/
public record Check(String resourcePath, String errorMessage) {}
/**
* Verify all checks pass. Throws RuntimeException on first failure.
* @param anchor Class whose classloader to use for resource lookups
* @param checks Dependency checks to run
*/
public static void verify(Class<?> anchor, List<Check> checks) {
for (Check check : checks) {
if (anchor.getResource(check.resourcePath()) == null) {
throw new RuntimeException(check.errorMessage());
}
}
}
private DependencyVerifier() {}
}
@@ -0,0 +1,78 @@
package com.gtnewhorizons.angelica.glsm.loading;
import com.gtnewhorizons.angelica.glsm.loading.TransformerNarrower.NarrowRule;
import java.util.List;
public final class EcosystemNarrowRules {
public static final List<NarrowRule> ALL = List.of(
new NarrowRule("DragonAPI", "Reika.DragonAPI.ASM", List.of(
"Reika.DragonAPI.ASM.Patchers",
"Reika.DragonAPI.ASM.Profiling",
"Reika.DragonAPI.ASM.APIStripper",
"Reika.DragonAPI.ASM.ClassReparenter",
"Reika.DragonAPI.ASM.DependentMethodStripper",
"Reika.DragonAPI.ASM.DragonAPIClassTransformer",
"Reika.DragonAPI.ASM.FMLItemBlockPatch",
"Reika.DragonAPI.ASM.FluidNamePatch",
"Reika.DragonAPI.ASM.InterfaceInjector",
"Reika.DragonAPI.ASM.SpecialBiomePlacement",
"Reika.DragonAPI.ASM.StructureLootHooks"
)),
new NarrowRule("Xaeros_common", "xaero.common.core", List.of(
"xaero.common.core.transformer"
)),
new NarrowRule("Xaeros_map", "xaero.map.core", List.of(
"xaero.map.core.transformer"
)),
new NarrowRule("AdvancedLightsabers", "com.fiskmods.lightsabers.asm", List.of(
"com.fiskmods.lightsabers.asm.ALLoadingPlugin",
"com.fiskmods.lightsabers.asm.ASMHooks",
"com.fiskmods.lightsabers.asm.transformers"
)),
new NarrowRule("Ears", "com.unascribed.ears", List.of(
"com.unascribed.ears.asm",
"com.unascribed.ears.common.agent"
)),
new NarrowRule("Alfheim", "alfheim.common.core.asm.hook", List.of(
"alfheim.common.core.asm.hook.AlfheimFieldHookHandler",
"alfheim.common.core.asm.hook.AlfheimHPHooks",
"alfheim.common.core.asm.hook.Botania18AndUpBackport",
"alfheim.common.core.asm.hook.ElementalDamageAdapter",
"alfheim.common.core.asm.hook.integration",
"alfheim.common.core.asm.hook.extender.FurnaceExtender",
"alfheim.common.core.asm.hook.extender.ItemAuraRingExtender",
"alfheim.common.core.asm.hook.extender.ItemLensExtender",
"alfheim.common.core.asm.hook.extender.ItemTwigWandExtender",
"alfheim.common.core.asm.hook.extender.LightRelayExtender",
"alfheim.common.core.asm.hook.extender.PureDaisyExtender",
"alfheim.common.core.asm.hook.extender.QuartzExtender",
"alfheim.common.core.asm.hook.extender.RelicHooks",
"alfheim.common.core.asm.hook.extender.SparkExtender",
"alfheim.common.core.asm.hook.fixes.BotaniaGlowingRenderFixes",
"alfheim.common.core.asm.hook.fixes.CorporeaInputFix",
"alfheim.common.core.asm.hook.fixes.GodAttributesHooks",
"alfheim.common.core.asm.hook.fixes.RecipeAncientWillsFix"
))
);
public static final String[] LWJGL3IFY_EXCLUSIONS_SHARED = {
"org.embeddedt.embeddium",
"org.taumc.celeritas",
"com.mitchej123.lwjgl.lwjgl3",
"com.mitchej123.glsm",
"com.gtnewhorizons.angelica.lwjgl3",
};
public static final String[] EARLY_REDIRECTOR_TARGETS = {
"cn.tesseract.mycelium.",
};
private EcosystemNarrowRules() {}
}
@@ -0,0 +1,66 @@
package com.gtnewhorizons.angelica.glsm.loading;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Narrows overly-broad transformer exclusions that prevent the GLSM redirector from
* transforming GL calls in mod code. Some coremods register exclusions covering their
* entire package tree, which inadvertently shields rendering code from redirection.
* <p>
* This utility removes the broad exclusion and re-adds narrower ones that cover only
* the coremod's own transformer/ASM classes, leaving rendering code exposed.
* <p>
* Operates on a {@code Set<String>} (the transformer exceptions set from LaunchClassLoader)
* and a {@code Map<String, Object>} (the Launch blackboard). Callers extract these from
* the Forge/launchwrapper infrastructure.
*/
public final class TransformerNarrower {
private static final Logger LOGGER = LogManager.getLogger("GLSM");
/**
* A rule that replaces a broad transformer exclusion with narrower ones.
* @param name Human-readable name for logging (e.g. "DragonAPI")
* @param broad The broad exclusion to remove (e.g. "Reika.DragonAPI.ASM")
* @param narrow Narrower exclusions to add in its place
*/
public record NarrowRule(String name, String broad, List<String> narrow) {}
/**
* Apply narrowing rules to the transformer exceptions set.
*
* @param exceptions The mutable Set from LaunchClassLoader.transformerExceptions
* @param blackboard The Launch.blackboard map (checked for disable flags)
* @param keyPrefix Blackboard key prefix for per-rule opt-out (e.g. "angelica" or "umbra").
* Rule "DragonAPI" checks key "{prefix}.narrow.DragonAPI". If explicitly
* set to Boolean.FALSE, the rule is skipped.
* @param callerName Name for log messages (e.g. "Angelica" or "Umbra")
* @param rules The narrowing rules to apply
* @return number of rules that were applied
*/
public static int narrow(Set<String> exceptions, Map<String, Object> blackboard,
String keyPrefix, String callerName, List<NarrowRule> rules) {
int applied = 0;
for (NarrowRule rule : rules) {
final String key = keyPrefix + ".narrow." + rule.name();
if (Boolean.FALSE.equals(blackboard.get(key))) {
continue;
}
if (exceptions.remove(rule.broad())) {
for (String narrow : rule.narrow()) {
exceptions.add(narrow);
}
LOGGER.info("[{}] Narrowed {} transformer exclusion to allow GL redirection", callerName, rule.broad());
applied++;
}
}
return applied;
}
private TransformerNarrower() {}
}
@@ -1,16 +1,14 @@
package com.gtnewhorizons.angelica.client.rendering;
package com.gtnewhorizons.angelica.glsm.streaming;
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags;
import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.ITessellatorData;
import com.gtnewhorizons.angelica.glsm.QuadConverter;
import com.gtnewhorizons.angelica.glsm.RenderSystem;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.glsm.streaming.OrphanStreamingBuffer;
import com.gtnewhorizons.angelica.glsm.streaming.PersistentStreamingBuffer;
import net.minecraft.client.renderer.Tessellator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL11;
@@ -71,41 +69,41 @@ public class TessellatorStreamingDrawer {
/**
* Draw the vanilla Tessellator's data using streaming VBO+VAO instead of FFP client arrays.
*/
public static int draw(Tessellator tess) {
if (!tess.isDrawing) {
public static int draw(ITessellatorData tess) {
if (!tess.isDrawing()) {
throw new IllegalStateException("Not tesselating!");
}
tess.isDrawing = false;
tess.setDrawing(false);
final int vertexCount = tess.vertexCount;
final int vertexCount = tess.getVertexCount();
if (vertexCount == 0) {
final int result = tess.rawBufferIndex * 4;
final int result = tess.getRawBufferIndex() * 4;
tess.reset();
return result;
}
// Determine the optimal vertex format from the tessellator's flags
final int flags = VertexFlags.convertToFlags(tess.hasTexture, tess.hasColor, tess.hasNormals, tess.hasBrightness);
final int flags = VertexFlags.convertToFlags(tess.hasTexture(), tess.hasColor(), tess.hasNormals(), tess.hasBrightness());
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[flags];
final int vertexSize = format.getVertexSize();
final int requiredBytes = vertexCount * vertexSize;
ensureRepackCapacity(requiredBytes);
final long writePtr = format.writeToBuffer0(repackAddress, tess.rawBuffer, tess.rawBufferIndex);
final long writePtr = format.writeToBuffer0(repackAddress, tess.getRawBuffer(), tess.getRawBufferIndex());
repackBuffer.position(0);
repackBuffer.limit((int)(writePtr - repackAddress));
uploadAndDraw(repackBuffer, flags, format, vertexSize, tess.drawMode, vertexCount);
uploadAndDraw(repackBuffer, flags, format, vertexSize, tess.getDrawMode(), vertexCount);
// Shrink rawBuffer if oversized
if (tess.rawBufferSize > 0x20000 && tess.rawBufferIndex < (tess.rawBufferSize << 3)) {
tess.rawBufferSize = 0x10000;
tess.rawBuffer = new int[tess.rawBufferSize];
if (tess.getRawBufferSize() > 0x20000 && tess.getRawBufferIndex() < (tess.getRawBufferSize() << 3)) {
tess.setRawBufferSize(0x10000);
tess.setRawBuffer(new int[tess.getRawBufferSize()]);
}
final int result = tess.rawBufferIndex * 4;
final int result = tess.getRawBufferIndex() * 4;
tess.reset();
return result;
}
@@ -128,6 +126,19 @@ public class TessellatorStreamingDrawer {
uploadAndDraw(buffer, flags, format, vertexSize, drawMode, vertexCount);
}
/**
* Upload pre-packed vertex data and draw. Public API for external batch systems.
* @param packedData buffer positioned at 0 with limit set to total bytes
* @param drawMode GL draw mode (GL_QUADS, GL_TRIANGLES, etc.)
* @param flags vertex format flags (from VertexFlags)
* @param vertexCount number of vertices
*/
public static void drawPacked(ByteBuffer packedData, int drawMode, int flags, int vertexCount) {
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[flags];
final int vertexSize = format.getVertexSize();
uploadAndDraw(packedData, flags, format, vertexSize, drawMode, vertexCount);
}
private static String cachedDebugInfo = "Stream: not initialized";
private static long lastDebugUpdateNanos;
private static final long DEBUG_UPDATE_INTERVAL_NS = 500_000_000L; // 500ms
@@ -172,34 +183,6 @@ public class TessellatorStreamingDrawer {
}
}
/**
* Draw a merged batch of pre-packed vertex data from a DeferredBatchTessellator.
* Copies byte ranges into repackBuffer, then uploads and draws.
*/
static void drawPackedBatch(DeferredBatchTessellator source,
java.util.List<DeferredBatchTessellator.DrawRange> ranges,
int from, int to,
int totalBytes, int totalVertices,
int drawMode, int flags) {
if (totalVertices == 0) return;
final VertexFormat format = DefaultVertexFormat.ALL_FORMATS[flags];
final int vertexSize = format.getVertexSize();
ensureRepackCapacity(totalBytes);
long writePos = repackAddress;
for (int j = from; j < to; j++) {
final DeferredBatchTessellator.DrawRange r = ranges.get(j);
source.copyRange(r.byteOffset(), r.byteLength(), writePos);
writePos += r.byteLength();
}
repackBuffer.position(0);
repackBuffer.limit(totalBytes);
uploadAndDraw(repackBuffer, flags, format, vertexSize, drawMode, totalVertices);
}
/**
* Upload packed vertex data to a streaming buffer and issue the draw call.
* Tries the persistent ring buffer first, falls back to orphan buffer on overflow.
@@ -239,7 +222,11 @@ public class TessellatorStreamingDrawer {
}
}
private static void ensureRepackCapacity(int requiredBytes) {
/**
* Ensure the repack buffer is large enough for the given byte count.
* Public for use by external batch systems that need to pack data before calling {@link #drawPacked}.
*/
public static void ensureRepackCapacity(int requiredBytes) {
if (requiredBytes <= repackCapacity) return;
int newCapacity = repackCapacity;
@@ -253,6 +240,16 @@ public class TessellatorStreamingDrawer {
repackCapacity = newCapacity;
}
/** Get the repack buffer's native address. Valid until next {@link #ensureRepackCapacity} call. */
public static long getRepackAddress() {
return repackAddress;
}
/** Get the repack ByteBuffer. Caller must set position/limit before passing to {@link #drawPacked}. */
public static ByteBuffer getRepackBuffer() {
return repackBuffer;
}
private static void ensureVAO(int flags, VertexFormat format) {
init();
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.client.rendering;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
import lombok.Getter;
import net.minecraft.client.renderer.Tessellator;
import org.lwjgl.opengl.GL11;
@@ -108,12 +109,33 @@ public class DeferredDrawBatcher {
break;
}
TessellatorStreamingDrawer.drawPackedBatch(batchTessellator, ranges, i, subEnd, totalBytes, totalVertices, drawMode, flags);
drawPackedBatch(batchTessellator, ranges, i, subEnd, totalBytes, totalVertices, drawMode, flags);
i = subEnd;
}
}
/**
* Pack ranges from a DeferredBatchTessellator into the streaming drawer's repack buffer
* and issue a single draw call for the merged batch.
*/
private static void drawPackedBatch(DeferredBatchTessellator source, List<DeferredBatchTessellator.DrawRange> ranges, int from, int to, int totalBytes, int totalVertices, int drawMode, int flags) {
if (totalVertices == 0) return;
TessellatorStreamingDrawer.ensureRepackCapacity(totalBytes);
long writePos = TessellatorStreamingDrawer.getRepackAddress();
for (int j = from; j < to; j++) {
final DeferredBatchTessellator.DrawRange r = ranges.get(j);
source.copyRange(r.byteOffset(), r.byteLength(), writePos);
writePos += r.byteLength();
}
final java.nio.ByteBuffer buf = TessellatorStreamingDrawer.getRepackBuffer();
buf.position(0);
buf.limit(totalBytes);
TessellatorStreamingDrawer.drawPacked(buf, drawMode, flags, totalVertices);
}
/**
* Pack current GLSM state into a long key for grouping.
* Layout: [tex1En:1][tex0En:1][textureId:20][srcRgb:12][dstRgb:12][blendEnabled:1][depthMask:1] = 48 bits
@@ -8,6 +8,8 @@ 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.glsm.loading.DependencyVerifier;
import com.gtnewhorizons.angelica.glsm.loading.EcosystemNarrowRules;
import com.gtnewhorizons.angelica.loading.fml.compat.CompatHandlers;
import com.gtnewhorizons.angelica.mixins.Mixins;
import com.gtnewhorizons.retrofuturabootstrap.SharedConfig;
@@ -91,30 +93,25 @@ public final class AngelicaClientTweaker implements IFMLLoadingPlugin, IEarlyMix
.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");
for (String exclusion : EcosystemNarrowRules.LWJGL3IFY_EXCLUSIONS_SHARED) {
handle.exclusions().add(exclusion);
}
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");
}
DependencyVerifier.verify(AngelicaTweaker.class, List.of(
new DependencyVerifier.Check(
"/it/unimi/dsi/fastutil/ints/Int2ObjectMap.class",
"Missing dependency: Angelica requires GTNHLib! Download: https://modrinth.com/mod/gtnhlib"),
new DependencyVerifier.Check(
"/com/gtnewhorizon/gtnhlib/client/renderer/PrimitiveExtractor.class",
"GTNHLib is outdated: Angelica requires GTNHLib 0.8.21 or newer! Download: https://modrinth.com/mod/gtnhlib"),
new DependencyVerifier.Check(
"/xyz/wagyourtail/jvmdg/exc/MissingStubError.class",
"GTNHLib is outdated: Angelica requires GTNHLib 0.9.0 or newer! Download: https://modrinth.com/mod/gtnhlib")
));
}
@Override
@@ -1,11 +1,16 @@
package com.gtnewhorizons.angelica.loading.fml.transformers;
import com.gtnewhorizons.angelica.loading.shared.transformers.AngelicaRedirector;
import com.gtnewhorizons.angelica.glsm.loading.EcosystemNarrowRules;
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A scoped redirector that only transforms classes from known-misbehaving (core)mod packages.
* <p>
@@ -13,28 +18,29 @@ import org.objectweb.asm.tree.ClassNode;
* Those classes get missed by the late-registered {@link AngelicaRedirectorTransformer}, so their
* GL calls permanently bypass GLSM.
* <p>
* This transformer is registered in the {@code AngelicaTweaker} constructor (before any
* coremod's {@code injectData}) and is removed by {@code AngelicaLateTweaker} once the
* full redirector is in its proper post-mixin position.
* <p>
* The mixin service will add this to its delegation list, but it no-ops on game classes
* since it only targets specific mod packages.
* Registered in {@code AngelicaClientTweaker} constructor. Removed by {@code AngelicaLateTweaker}
* once the full redirector is in its proper post-mixin position.
*
* @see EcosystemNarrowRules#EARLY_REDIRECTOR_TARGETS
*/
public class EarlyRedirectorTransformer implements IClassTransformer {
private static final String[] TARGET_PACKAGES = {
"cn.tesseract.mycelium.",
};
private final GLSMRedirector core = new GLSMRedirector();
private final String[] exclusions;
private final AngelicaRedirector inner = new AngelicaRedirector();
private final String[] exclusions = inner.getTransformerExclusions();
public EarlyRedirectorTransformer() {
final List<String> excl = new ArrayList<>(Arrays.asList(core.getCoreExclusions()));
excl.add("com.gtnewhorizons.angelica.lwjgl3.");
excl.add("com.gtnewhorizons.angelica.transform");
exclusions = excl.toArray(new String[0]);
}
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
boolean targeted = false;
for (String pkg : TARGET_PACKAGES) {
for (String pkg : EcosystemNarrowRules.EARLY_REDIRECTOR_TARGETS) {
if (transformedName.startsWith(pkg)) {
targeted = true;
break;
@@ -46,14 +52,12 @@ public class EarlyRedirectorTransformer implements IClassTransformer {
if (transformedName.startsWith(exclusion)) return basicClass;
}
if (!inner.shouldTransform(basicClass)) {
return basicClass;
}
if (!core.shouldTransform(basicClass)) return basicClass;
final ClassReader cr = new ClassReader(basicClass);
final ClassNode cn = new ClassNode();
cr.accept(cn, 0);
final boolean changed = inner.transformClassNode(transformedName, cn);
final boolean changed = core.transformClassNode(transformedName, cn);
if (changed) {
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw);
@@ -1,5 +1,7 @@
package com.gtnewhorizons.angelica.loading.fml.tweakers;
import com.gtnewhorizons.angelica.glsm.loading.EcosystemNarrowRules;
import com.gtnewhorizons.angelica.glsm.loading.TransformerNarrower;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.ITweaker;
@@ -27,8 +29,6 @@ public class AngelicaLateTweaker implements ITweaker {
/**
* Reduces overly broad transformer exclusions that prevent AngelicaRedirector from doing its job.
*
* Looking at you, DragonAPI.
*/
@SuppressWarnings("unchecked")
private static void narrowTransformerExclusions(LaunchClassLoader classLoader) {
@@ -37,79 +37,14 @@ public class AngelicaLateTweaker implements ITweaker {
exceptionsField.setAccessible(true);
final Set<String> exceptions = (Set<String>) exceptionsField.get(classLoader);
if (narrowEnabled("DragonAPI") && exceptions.remove("Reika.DragonAPI.ASM")) {
// Re-add non-GL classes; ASMCallsClient and utility classes with GL calls are left exposed
exceptions.add("Reika.DragonAPI.ASM.Patchers");
exceptions.add("Reika.DragonAPI.ASM.Profiling");
exceptions.add("Reika.DragonAPI.ASM.APIStripper");
exceptions.add("Reika.DragonAPI.ASM.ClassReparenter");
exceptions.add("Reika.DragonAPI.ASM.DependentMethodStripper");
exceptions.add("Reika.DragonAPI.ASM.DragonAPIClassTransformer");
exceptions.add("Reika.DragonAPI.ASM.FMLItemBlockPatch");
exceptions.add("Reika.DragonAPI.ASM.FluidNamePatch");
exceptions.add("Reika.DragonAPI.ASM.InterfaceInjector");
exceptions.add("Reika.DragonAPI.ASM.SpecialBiomePlacement");
exceptions.add("Reika.DragonAPI.ASM.StructureLootHooks");
FMLRelaunchLog.info("[Angelica] Narrowed Reika.DragonAPI.ASM transformer exclusion to allow GL redirection");
}
TransformerNarrower.narrow(exceptions, Launch.blackboard, "angelica", "Angelica",
EcosystemNarrowRules.ALL);
if (narrowEnabled("Xaeros")) {
if (exceptions.remove("xaero.common.core")) {
exceptions.add("xaero.common.core.transformer");
FMLRelaunchLog.info("[Angelica] Narrowed xaero.common.core transformer exclusion to allow GL redirection");
}
if (exceptions.remove("xaero.map.core")) {
exceptions.add("xaero.map.core.transformer");
FMLRelaunchLog.info("[Angelica] Narrowed xaero.map.core transformer exclusion to allow GL redirection");
}
}
if (narrowEnabled("AdvancedLightsabers") && exceptions.remove("com.fiskmods.lightsabers.asm")) {
exceptions.add("com.fiskmods.lightsabers.asm.ALLoadingPlugin");
exceptions.add("com.fiskmods.lightsabers.asm.ASMHooks");
exceptions.add("com.fiskmods.lightsabers.asm.transformers");
FMLRelaunchLog.info("[Angelica] Narrowed com.fiskmods.lightsabers.asm transformer exclusion to allow GL redirection");
}
if (narrowEnabled("Ears") && exceptions.remove("com.unascribed.ears")) {
exceptions.add("com.unascribed.ears.asm");
exceptions.add("com.unascribed.ears.common.agent");
FMLRelaunchLog.info("[Angelica] Narrowed com.unascribed.ears transformer exclusion to allow GL redirection");
}
if (narrowEnabled("Alfheim") && exceptions.remove("alfheim.common.core.asm.hook")) {
exceptions.add("alfheim.common.core.asm.hook.AlfheimFieldHookHandler");
exceptions.add("alfheim.common.core.asm.hook.AlfheimHPHooks");
exceptions.add("alfheim.common.core.asm.hook.Botania18AndUpBackport");
exceptions.add("alfheim.common.core.asm.hook.ElementalDamageAdapter");
exceptions.add("alfheim.common.core.asm.hook.integration");
exceptions.add("alfheim.common.core.asm.hook.extender.FurnaceExtender");
exceptions.add("alfheim.common.core.asm.hook.extender.ItemAuraRingExtender");
exceptions.add("alfheim.common.core.asm.hook.extender.ItemLensExtender");
exceptions.add("alfheim.common.core.asm.hook.extender.ItemTwigWandExtender");
exceptions.add("alfheim.common.core.asm.hook.extender.LightRelayExtender");
exceptions.add("alfheim.common.core.asm.hook.extender.PureDaisyExtender");
exceptions.add("alfheim.common.core.asm.hook.extender.QuartzExtender");
exceptions.add("alfheim.common.core.asm.hook.extender.RelicHooks");
exceptions.add("alfheim.common.core.asm.hook.extender.SparkExtender");
exceptions.add("alfheim.common.core.asm.hook.fixes.BotaniaGlowingRenderFixes");
exceptions.add("alfheim.common.core.asm.hook.fixes.CorporeaInputFix");
exceptions.add("alfheim.common.core.asm.hook.fixes.GodAttributesHooks");
exceptions.add("alfheim.common.core.asm.hook.fixes.RecipeAncientWillsFix");
FMLRelaunchLog.info("[Angelica] Narrowed alfheim.common.core.asm.hook transformer exclusion to allow GL redirection");
}
} catch (Exception e) {
FMLRelaunchLog.warning("[Angelica] Failed to narrow transformer exclusions: %s", e.getMessage());
}
}
private static boolean narrowEnabled(String mod) {
return !Boolean.FALSE.equals(Launch.blackboard.get("angelica.narrow." + mod));
}
@Override
public String getLaunchTarget() {
return null;
@@ -6,7 +6,7 @@ import com.gtnewhorizons.angelica.dynamiclights.DynamicLights;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND;
import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager;
import com.gtnewhorizons.angelica.client.rendering.TessellatorStreamingDrawer;
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
import com.gtnewhorizons.angelica.glsm.states.FogState;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
@@ -1,7 +1,7 @@
package com.gtnewhorizons.angelica.mixins.early.angelica;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.client.rendering.TessellatorStreamingDrawer;
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
import com.gtnewhorizons.angelica.mixins.interfaces.IGameSettingsExt;
import com.gtnewhorizons.angelica.proxy.ClientProxy;
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
@@ -1,9 +1,11 @@
package com.gtnewhorizons.angelica.mixins.early.angelica.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.client.rendering.TessellatorStreamingDrawer;
import com.gtnewhorizons.angelica.glsm.ITessellatorData;
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
import net.minecraft.client.renderer.Tessellator;
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.CallbackInfoReturnable;
@@ -13,12 +15,39 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
* except when GTNHLib needs to intercept (display list compilation, DirectTessellator capture).
*/
@Mixin(Tessellator.class)
public class MixinTessellator_CoreProfile {
public class MixinTessellator_CoreProfile implements ITessellatorData {
@Shadow public boolean isDrawing;
@Shadow public int vertexCount;
@Shadow public int[] rawBuffer;
@Shadow public int rawBufferIndex;
@Shadow public int rawBufferSize;
@Shadow public int drawMode;
@Shadow public boolean hasTexture;
@Shadow public boolean hasColor;
@Shadow public boolean hasNormals;
@Shadow public boolean hasBrightness;
@Shadow public void reset() {}
@Inject(method = "draw", at = @At("HEAD"), cancellable = true)
private void angelica$coreProfileDraw(CallbackInfoReturnable<Integer> cir) {
// Let GTNHLib handle display list compilation and DirectTessellator capture
if (TessellatorManager.shouldInterceptDraw((Tessellator)(Object)this)) return;
cir.setReturnValue(TessellatorStreamingDrawer.draw((Tessellator)(Object)this));
cir.setReturnValue(TessellatorStreamingDrawer.draw((ITessellatorData) this));
}
@Override public boolean isDrawing() { return isDrawing; }
@Override public void setDrawing(boolean drawing) { isDrawing = drawing; }
@Override public int getVertexCount() { return vertexCount; }
@Override public int[] getRawBuffer() { return rawBuffer; }
@Override public int getRawBufferIndex() { return rawBufferIndex; }
@Override public int getRawBufferSize() { return rawBufferSize; }
@Override public void setRawBufferSize(int size) { rawBufferSize = size; }
@Override public void setRawBuffer(int[] buffer) { rawBuffer = buffer; }
@Override public int getDrawMode() { return drawMode; }
@Override public boolean hasTexture() { return hasTexture; }
@Override public boolean hasColor() { return hasColor; }
@Override public boolean hasNormals() { return hasNormals; }
@Override public boolean hasBrightness() { return hasBrightness; }
}
@@ -2,7 +2,7 @@ package com.gtnewhorizons.angelica.mixins.early.angelica.startup;
import com.gtnewhorizons.angelica.AngelicaMod;
import com.gtnewhorizons.angelica.client.rendering.TextureTracker;
import com.gtnewhorizons.angelica.client.rendering.TessellatorStreamingDrawer;
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
import com.gtnewhorizons.angelica.compat.DriverCompatabilityCheck;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
+37
View File
@@ -0,0 +1,37 @@
plugins {
id("com.gtnewhorizons.gtnhconvention")
}
minecraft {
extraRunJvmArguments.add("-Dumbra.dumpClass=true")
}
tasks.processResources {
val projectVersion = project.version.toString()
inputs.property("version", projectVersion)
filesMatching("META-INF/rfb-plugin/*") {
expand("version" to projectVersion)
}
}
val embedOnly: Configuration by configurations
val shadowImplementation: Configuration by configurations
tasks.shadowJar {
dependsOn(embedOnly)
from(embedOnly.map(::zipTree))
duplicatesStrategy = DuplicatesStrategy.INCLUDE
mergeServiceFiles()
minimize {
exclude(project(":glsm"))
exclude(project(":lwjgl3-backend"))
}
relocate("com.mitchej123", "com.mitchej123")
relocate("org.embeddedt", "org.embeddedt")
relocate("com.gtnewhorizons.angelica.glsm", "com.gtnewhorizons.angelica.glsm")
relocate("com.gtnewhorizons.angelica.lwjgl3", "com.gtnewhorizons.angelica.lwjgl3")
}
+40
View File
@@ -0,0 +1,40 @@
configurations {
// Embed in JAR without relocation (compile only, shadowed into JAR)
embedOnly
embedOnly.canBeConsumed = false
embedOnly.canBeResolved = true
compileClasspath.extendsFrom(embedOnly)
runtimeClasspath.extendsFrom(embedOnly)
testCompileClasspath.extendsFrom(embedOnly)
testRuntimeClasspath.extendsFrom(embedOnly)
}
// Exclude self-reference from all configurations
configurations.all {
exclude group: "com.github.GTNewHorizons", module: "Angelica"
}
dependencies {
compileOnly("com.gtnewhorizons.retrofuturabootstrap:RetroFuturaBootstrap:1.0.7") { transitive = false }
compileOnly('org.jetbrains:annotations:26.0.2')
compileOnly("org.projectlombok:lombok:1.18.42") { transitive = false }
annotationProcessor("org.projectlombok:lombok:1.18.42")
api("com.github.GTNewHorizons:GTNHLib:0.9.36:dev")
shadowImplementation(project(":glsm")) { transitive = false }
shadowImplementation(project(":lwjgl3-backend")) { transitive = false }
shadowImplementation("net.minecraftforge:eventbus:7.0-beta.15")
shadowImplementation("org.anarres:jcpp:1.4.14") // Apache 2.0
shadowImplementation("org.taumc:glsl-transformation-lib:0.2.0-32.g7dd88a4-GTNH") {
exclude module: "antlr4" // we only want to shadow the runtime module
}
shadowImplementation("org.antlr:antlr4-runtime:4.13.2")
embedOnly("org.embeddedt.celeritas:celeritas-common:2.5.4-GTNH") { transitive = false }
embedOnly("org.embeddedt.celeritas:celeritas-lwjgl2-service:2.5.3-GTNH") { transitive = false }
runtimeOnlyNonPublishable("org.embeddedt.celeritas:celeritas-common:2.5.4-GTNH") { transitive = false }
runtimeOnlyNonPublishable("org.embeddedt.celeritas:celeritas-lwjgl2-service:2.5.3-GTNH") { transitive = false }
}
+47
View File
@@ -0,0 +1,47 @@
gtnh.settings.blowdryerTag = 0.2.2
modName = Umbra
modId = umbra
modGroup = com.gtnewhorizons.umbra
useModGroupForPublishing = false
autoUpdateBuildScript = false
minecraftVersion = 1.7.10
forgeVersion = 10.13.4.1614
channel = stable
mappingsVersion = 12
remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/conf/
developmentEnvironmentUserName = Developer
enableModernJavaSyntax = jvmDowngrader
jvmDowngraderStubsProvider = gtnhlib
jvmDowngraderMultiReleaseVersions = 17, 21, 25
enableGenericInjection = true
generateGradleTokenClass = com.gtnewhorizons.umbra.Tags
gradleTokenVersion = VERSION
coreModClass = loading.UmbraTweaker
containsMixinsAndOrCoreModOnly = false
usesMixins = true
separateMixinSourceSet = mixin
mixinsPackage = mixins
usesShadowedDependencies = true
minimizeShadowedDependencies = true
relocateShadowedDependencies = true
includeWellKnownRepositories = true
usesMavenPublishing = true
disableSpotless = true
org.gradle.configuration-cache = true
org.gradle.jvmargs = -Xmx2g
org.gradle.parallel = true
+12
View File
@@ -0,0 +1,12 @@
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/e1118b4c7c2858d28154d086dbff010e/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/59a9771cad43219260d9aac9a8ec4d6a/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c5ee947fbfb70bc347d8d531e3a578c4/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a4c09dd2e2d7079373d30e524bbc2829/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect
toolchainVersion=25
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+248
View File
@@ -0,0 +1,248 @@
#!/bin/sh
#
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
+93
View File
@@ -0,0 +1,93 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+26
View File
@@ -0,0 +1,26 @@
pluginManagement {
repositories {
maven {
// RetroFuturaGradle
name = "GTNH Maven"
url = uri("https://nexus.gtnewhorizons.com/repository/public/")
mavenContent {
includeGroup("com.gtnewhorizons")
includeGroupByRegex("com\\.gtnewhorizons\\..+")
}
}
gradlePluginPortal()
mavenCentral()
mavenLocal()
}
}
plugins {
id("com.gtnewhorizons.gtnhsettingsconvention") version ("2.0.24")
}
include("glsm")
project(":glsm").projectDir = file("../glsm")
include("lwjgl3-backend")
project(":lwjgl3-backend").projectDir = file("../lwjgl3-backend")
@@ -0,0 +1,50 @@
package com.gtnewhorizons.umbra;
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 com.gtnewhorizons.umbra.proxy.CommonProxy;
import net.minecraft.launchwrapper.Launch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Mod(modid = UmbraMod.MOD_ID, name = "Umbra", version = Tags.VERSION,
dependencies = "required-after:gtnhlib@[0.8.21,);before:lwjgl3ify@[1.5.3,)",
acceptableRemoteVersions = "*")
public class UmbraMod {
public static final String MOD_ID = "umbra";
public static final Logger LOGGER = LogManager.getLogger("Umbra");
@SidedProxy(
clientSide = "com.gtnewhorizons.umbra.proxy.ClientProxy",
serverSide = "com.gtnewhorizons.umbra.proxy.CommonProxy")
public static CommonProxy proxy;
private static boolean isDisabled() {
return Launch.blackboard != null && Boolean.TRUE.equals(Launch.blackboard.get("umbra.disabled"));
}
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
if (isDisabled()) {
LOGGER.info("Umbra is disabled (Angelica detected), skipping preInit");
return;
}
proxy.preInit(event);
}
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
if (isDisabled()) return;
proxy.init(event);
}
@Mod.EventHandler
public void postInit(FMLPostInitializationEvent event) {
if (isDisabled()) return;
proxy.postInit(event);
}
}
@@ -0,0 +1,115 @@
package com.gtnewhorizons.umbra.client.rendering;
import com.gtnewhorizon.gtnhlib.client.opengl.UniversalVAO;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.mitchej123.glsm.GLStateManagerService;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import java.nio.IntBuffer;
public class UmbraGLStateManagerService implements GLStateManagerService {
private IntBuffer intArrayBuffer = BufferUtils.createIntBuffer(16);
private IntBuffer getIntArrayBuffer(int size) {
if (intArrayBuffer.capacity() < size) {
intArrayBuffer = BufferUtils.createIntBuffer(size);
}
intArrayBuffer.clear();
return intArrayBuffer;
}
@Override public int getPriority() { return 100; }
@Override public int glGetInteger(int pname) { return GLStateManager.glGetInteger(pname); }
@Override public String glGetString(int pname) { return GLStateManager.glGetString(pname); }
@Override public int glGenFramebuffers() { return GLStateManager.glGenFramebuffers(); }
@Override public void glDeleteFramebuffers(int fb) { GLStateManager.glDeleteFramebuffers(fb); }
@Override public void glBindFramebuffer(int target, int fb) { GLStateManager.glBindFramebuffer(target, fb); }
@Override public int glCheckFramebufferStatus(int target) { return GLStateManager.glCheckFramebufferStatus(target); }
@Override public void glFramebufferTexture2D(int target, int attachment, int textarget, int texture, int level) {
GLStateManager.glFramebufferTexture2D(target, attachment, textarget, texture, level);
}
@Override public int glCreateShader(int type) { return GLStateManager.glCreateShader(type); }
@Override public void glCompileShader(int shader) { GLStateManager.glCompileShader(shader); }
@Override public int glGetShaderi(int shader, int pname) { return GLStateManager.glGetShaderi(shader, pname); }
@Override public String glGetShaderInfoLog(int shader, int maxLen) { return GLStateManager.glGetShaderInfoLog(shader, maxLen); }
@Override public void glDeleteShader(int shader) { GLStateManager.glDeleteShader(shader); }
@Override public int glCreateProgram() { return GLStateManager.glCreateProgram(); }
@Override public void glAttachShader(int program, int shader) { GLStateManager.glAttachShader(program, shader); }
@Override public void glLinkProgram(int program) { GLStateManager.glLinkProgram(program); }
@Override public int glGetProgrami(int program, int pname) { return GLStateManager.glGetProgrami(program, pname); }
@Override public void glUseProgram(int program) { GLStateManager.glUseProgram(program); }
@Override public void glDeleteProgram(int program) { GLStateManager.glDeleteProgram(program); }
@Override public int glGetUniformLocation(int program, CharSequence name) { return GLStateManager.glGetUniformLocation(program, name); }
@Override public void glUniform1i(int location, int value) { GLStateManager.glUniform1i(location, value); }
@Override public int glGetAttribLocation(int program, CharSequence name) { return GLStateManager.glGetAttribLocation(program, name); }
@Override public void glBindAttribLocation(int program, int index, CharSequence name) { GLStateManager.glBindAttribLocation(program, index, name); }
@Override public int glGenVertexArrays() { return UniversalVAO.genVertexArrays(); }
@Override public void glBindVertexArray(int array) { GLStateManager.glBindVertexArray(array); }
@Override public void glDeleteVertexArrays(int array) { GLStateManager.glDeleteVertexArrays(array); }
@Override public int glGenBuffers() { return GLStateManager.glGenBuffers(); }
@Override public void glBindBuffer(int target, int buffer) { GLStateManager.glBindBuffer(target, buffer); }
@Override public void glDeleteBuffers(int buffer) { GLStateManager.glDeleteBuffers(buffer); }
@Override public int glGenTextures() { return GLStateManager.glGenTextures(); }
@Override public void glGenTextures(int[] textures) {
final IntBuffer buffer = getIntArrayBuffer(textures.length);
GLStateManager.glGenTextures(buffer);
buffer.get(textures);
}
@Override public void glDeleteTextures(int texture) { GLStateManager.glDeleteTextures(texture); }
@Override public void glDeleteTextures(int[] textures) {
final IntBuffer buffer = getIntArrayBuffer(textures.length);
buffer.put(textures);
buffer.flip();
GLStateManager.glDeleteTextures(buffer);
}
@Override public void glActiveTexture(int texture) { GLStateManager.glActiveTexture(texture); }
@Override public int glGetTexLevelParameteri(int target, int level, int pname) { return GLStateManager.glGetTexLevelParameteri(target, level, pname); }
@Override public int glGetTexLevelParameter(int target, int level, int pname) { return GLStateManager.glGetTexLevelParameteri(target, level, pname); }
@Override public void glCopyTexSubImage2D(int target, int level, int xo, int yo, int x, int y, int w, int h) {
GLStateManager.glCopyTexSubImage2D(target, level, xo, yo, x, y, w, h);
}
@Override public void glPixelStorei(int pname, int param) { GLStateManager.glPixelStorei(pname, param); }
@Override public void enableCullFace() { GLStateManager.enableCull(); }
@Override public void disableCullFace() { GLStateManager.disableCull(); }
@Override public void enableBlend() { GLStateManager.enableBlend(); }
@Override public void disableBlend() { GLStateManager.disableBlend(); }
@Override public void glBlendFuncSeparate(int srcRGB, int dstRGB, int srcA, int dstA) { GLStateManager.tryBlendFuncSeparate(srcRGB, dstRGB, srcA, dstA); }
@Override public void enableDepthTest() { GLStateManager.enableDepthTest(); }
@Override public void disableDepthTest() { GLStateManager.disableDepthTest(); }
@Override public void glDepthFunc(int func) { GLStateManager.glDepthFunc(func); }
@Override public void glDepthMask(boolean flag) { GLStateManager.glDepthMask(flag); }
@Override public void glViewport(int x, int y, int w, int h) { GLStateManager.glViewport(x, y, w, h); }
@Override public void glColorMask(boolean r, boolean g, boolean b, boolean a) { GLStateManager.glColorMask(r, g, b, a); }
@Override public void glClearColor(float r, float g, float b, float a) { GLStateManager.glClearColor(r, g, b, a); }
@Override public void glClear(int mask) { GLStateManager.glClear(mask); }
@Override public void clear(int mask, boolean checkError) { GLStateManager.glClear(mask); }
@Override public void bindTexture(int texture) { GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture); }
@Override public int getActiveTexture() { return GLStateManager.getActiveTextureUnit() + GL13.GL_TEXTURE0; }
@Override public int getActiveTextureAccessor() { return GLStateManager.getActiveTextureUnit(); }
@Override public int getBoundTexture(int internalUnit) { return GLStateManager.getBoundTextureForServerState(internalUnit); }
@Override public int getActiveBoundTexture() { return GLStateManager.getBoundTextureForServerState(); }
@Override public int getViewportWidth() { return GLStateManager.getViewportState().width; }
@Override public int getViewportHeight() { return GLStateManager.getViewportState().height; }
@Override public boolean getDepthStateMask() { return GLStateManager.getDepthState().isEnabled(); }
@Override public boolean isBlendEnabled() { return GLStateManager.getBlendMode().isEnabled(); }
@Override public void setBoundTexture(int unit, int texture) {
final int currentUnit = GLStateManager.getActiveTextureUnit();
if (currentUnit != unit) GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit);
GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture);
if (currentUnit != unit) GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + currentUnit);
}
@Override public int getTextureBinding(int unit) { return GLStateManager.getBoundTextureForServerState(unit); }
}
@@ -0,0 +1,75 @@
package com.gtnewhorizons.umbra.client.rendering;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.mitchej123.glsm.RenderSystemService;
import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import java.nio.FloatBuffer;
public class UmbraRenderSystemService implements RenderSystemService {
private final int[] shaderTextures = new int[16];
private final float[] shaderColor = {1f, 1f, 1f, 1f};
private final float[] shaderFogColor = {0f, 0f, 0f, 1f};
private float shaderFogStart = 0f;
private float shaderFogEnd = 0f;
private int fogShape = 0;
private float shaderLineWidth = 1f;
private final Matrix4f projectionMatrix = new Matrix4f();
private final FloatBuffer projectionMatrixBuffer = BufferUtils.createFloatBuffer(16);
@Override public int getPriority() { return 100; }
@Override public void glActiveTexture(int texture) { GLStateManager.glActiveTexture(texture); }
@Override public void bindTexture(int texture) { GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture); }
@Override public void enableCullFace() { GLStateManager.enableCull(); }
@Override public void disableCullFace() { GLStateManager.disableCull(); }
@Override public void enableBlend() { GLStateManager.enableBlend(); }
@Override public void disableBlend() { GLStateManager.disableBlend(); }
@Override public void defaultBlendFunc() { GLStateManager.defaultBlendFunc(); }
@Override public void blendFuncSeparate(int srcRGB, int dstRGB, int srcA, int dstA) { GLStateManager.tryBlendFuncSeparate(srcRGB, dstRGB, srcA, dstA); }
@Override public void setUnknownBlendState() { }
@Override public void enableDepthTest() { GLStateManager.enableDepthTest(); }
@Override public void disableDepthTest() { GLStateManager.disableDepthTest(); }
@Override public void depthFunc(int func) { GLStateManager.glDepthFunc(func); }
@Override public void depthMask(boolean flag) { GLStateManager.glDepthMask(flag); }
@Override public void glViewport(int x, int y, int w, int h) { GLStateManager.glViewport(x, y, w, h); }
@Override public void glClearColor(float r, float g, float b, float a) { GLStateManager.glClearColor(r, g, b, a); }
@Override public void clear(int mask, boolean checkError) { GLStateManager.glClear(mask); }
@Override public void glUniform1i(int loc, int val) { GLStateManager.glUniform1i(loc, val); }
@Override public void glUniformMatrix3(int loc, boolean transpose, FloatBuffer val) { GLStateManager.glUniformMatrix3(loc, transpose, val); }
@Override public void glUniformMatrix4(int loc, boolean transpose, FloatBuffer val) { GLStateManager.glUniformMatrix4(loc, transpose, val); }
@Override public void assertOnRenderThread() { }
@Override public void assertOnRenderThreadOrInit() { }
@Override public void setShaderTexture(int slot, int id) { if (slot >= 0 && slot < 16) shaderTextures[slot] = id; }
@Override public int getShaderTexture(int slot) { return (slot >= 0 && slot < 16) ? shaderTextures[slot] : 0; }
@Override public void setShaderColor(float r, float g, float b, float a) { shaderColor[0]=r; shaderColor[1]=g; shaderColor[2]=b; shaderColor[3]=a; }
@Override public float[] getShaderColor() { return shaderColor.clone(); }
@Override public void setShaderLineWidth(float w) { shaderLineWidth = w; }
@Override public float getShaderLineWidth() { return shaderLineWidth; }
@Override public void setShaderFogColor(float r, float g, float b, float a) { shaderFogColor[0]=r; shaderFogColor[1]=g; shaderFogColor[2]=b; shaderFogColor[3]=a; }
@Override public float[] getShaderFogColor() { return shaderFogColor.clone(); }
@Override public void setShaderFogStart(float start) { shaderFogStart = start; }
@Override public float getShaderFogStart() { return shaderFogStart; }
@Override public void setShaderFogEnd(float end) { shaderFogEnd = end; }
@Override public float getShaderFogEnd() { return shaderFogEnd; }
@Override public void setFogShape(int shape) { fogShape = shape; }
@Override public int getFogShape() { return fogShape; }
@Override public Matrix4f getProjectionMatrix() { return new Matrix4f(projectionMatrix); }
@Override public void setProjectionMatrixOrth(Matrix4f m) {
projectionMatrix.set(m);
GLStateManager.glMatrixMode(GL11.GL_PROJECTION);
projectionMatrix.get(projectionMatrixBuffer);
projectionMatrixBuffer.rewind();
GLStateManager.glLoadMatrix(projectionMatrixBuffer);
}
@Override public void setProjectionMatrixOrigin(Matrix4f m) { setProjectionMatrixOrth(m); }
}
@@ -0,0 +1,136 @@
package com.gtnewhorizons.umbra.loading;
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
import com.gtnewhorizons.angelica.glsm.loading.DependencyVerifier;
import com.gtnewhorizons.angelica.glsm.loading.EcosystemNarrowRules;
import com.gtnewhorizons.retrofuturabootstrap.SharedConfig;
import com.gtnewhorizons.umbra.loading.shared.AngelicaDetector;
import com.gtnewhorizons.umbra.mixins.Mixins;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import net.minecraft.launchwrapper.Launch;
import org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Client-side coremod logic. Handles Angelica detection, transformer exclusions,
* mixin registration, dependency verification, and late tweaker registration.
*/
public class UmbraClientTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
private static Boolean OBF_ENV;
private final boolean disabled;
@SuppressWarnings("unchecked")
public UmbraClientTweaker() {
Launch.classLoader.addTransformerExclusion("com.gtnewhorizons.umbra.loading");
Launch.classLoader.addClassLoaderExclusion("com.gtnewhorizons.angelica.glsm.redirect");
// Detect Angelica -- if present, disable Umbra entirely
if (AngelicaDetector.isPresent()) {
FMLRelaunchLog.info("[Umbra] Angelica detected, Disabling Umbra");
Launch.blackboard.put("umbra.disabled", Boolean.TRUE);
disabled = true;
return;
}
disabled = false;
if (Boolean.TRUE.equals(Launch.blackboard.get("umbra.rfbPluginLoaded"))) {
addLwjgl3ifyExclusions();
}
verifyDependencies();
// Register early redirector if RFB is not loaded (RFB handles its own registration)
if (!Boolean.TRUE.equals(Launch.blackboard.get("umbra.rfbPluginLoaded"))) {
final String earlyTransformer = "com.gtnewhorizons.umbra.loading.fml.transformers.EarlyRedirectorTransformer";
Launch.classLoader.registerTransformer(earlyTransformer);
FMLRelaunchLog.info("[Umbra] Registered early redirector transformer");
}
}
private static void addLwjgl3ifyExclusions() {
final var handle = SharedConfig.getRfbTransformers().stream()
.filter(transformer -> transformer.id().equals("lwjgl3ify:redirect"))
.findFirst()
.orElse(null);
if (handle != null) {
for (String exclusion : EcosystemNarrowRules.LWJGL3IFY_EXCLUSIONS_SHARED) {
handle.exclusions().add(exclusion);
}
}
}
private static void verifyDependencies() {
DependencyVerifier.verify(UmbraClientTweaker.class, List.of(
new DependencyVerifier.Check(
"/com/gtnewhorizon/gtnhlib/client/renderer/PrimitiveExtractor.class",
"Missing dependency: Umbra requires GTNHLib 0.8.21+! Download: https://modrinth.com/mod/gtnhlib"),
new DependencyVerifier.Check(
"/xyz/wagyourtail/jvmdg/exc/MissingStubError.class",
"GTNHLib is outdated: Umbra requires GTNHLib 0.9.0+! Download: https://modrinth.com/mod/gtnhlib")
));
}
@Override
public String[] getASMTransformerClass() {
if (disabled) return new String[0];
return new String[0]; // Redirector registered via late tweaker, not here
}
@Override
public String getModContainerClass() {
return null;
}
@Override
public String getSetupClass() {
return null;
}
@Override
@SuppressWarnings("unchecked")
public void injectData(Map<String, Object> data) {
if (disabled) return;
OBF_ENV = (Boolean) data.get("runtimeDeobfuscationEnabled");
// Register the late tweaker (handles narrowing + full redirector registration)
if (!Boolean.TRUE.equals(Launch.blackboard.get("umbra.rfbPluginLoaded"))) {
final List<String> tweakClasses = org.spongepowered.asm.launch.GlobalProperties.get(
MixinServiceLaunchWrapper.BLACKBOARD_KEY_TWEAKCLASSES);
if (tweakClasses != null) {
tweakClasses.add("com.gtnewhorizons.umbra.loading.fml.tweakers.UmbraLateTweaker");
FMLRelaunchLog.info("[Umbra] Registered late tweaker");
}
}
}
@Override
public String getAccessTransformerClass() {
return null;
}
@Override
public String getMixinConfig() {
if (disabled) return null;
return "mixins.umbra.early.json";
}
@Override
public List<String> getMixins(Set<String> loadedCoreMods) {
if (disabled) return Collections.emptyList();
return IMixins.getEarlyMixins(Mixins.class, loadedCoreMods);
}
public static boolean isObfEnv() {
if (OBF_ENV == null) throw new IllegalStateException("OBF_ENV not yet initialized");
return OBF_ENV;
}
}
@@ -0,0 +1,76 @@
package com.gtnewhorizons.umbra.loading;
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Umbra coremod entry point. Detects Angelica and delegates to client tweaker.
*/
@IFMLLoadingPlugin.MCVersion("1.7.10")
public class UmbraTweaker implements IFMLLoadingPlugin, IEarlyMixinLoader {
private final IFMLLoadingPlugin loadingPlugin;
private final IEarlyMixinLoader mixinLoader;
public UmbraTweaker() {
if (FMLLaunchHandler.side().isClient()) {
final UmbraClientTweaker clientTweaker = new UmbraClientTweaker();
loadingPlugin = clientTweaker;
mixinLoader = clientTweaker;
} else {
loadingPlugin = null;
mixinLoader = null;
FMLRelaunchLog.info("[Umbra] Server side detected, skipping client initialization");
}
}
@Override
public String[] getASMTransformerClass() {
return loadingPlugin != null ? loadingPlugin.getASMTransformerClass() : new String[0];
}
@Override
public String getModContainerClass() {
return null;
}
@Override
public String getSetupClass() {
return null;
}
@Override
public void injectData(Map<String, Object> data) {
if (loadingPlugin != null) {
loadingPlugin.injectData(data);
}
}
@Override
public String getAccessTransformerClass() {
return null;
}
@Override
public String getMixinConfig() {
if (mixinLoader != null) {
return mixinLoader.getMixinConfig();
}
return null;
}
@Override
public List<String> getMixins(Set<String> loadedCoreMods) {
if (mixinLoader != null) {
return mixinLoader.getMixins(loadedCoreMods);
}
return Collections.emptyList();
}
}
@@ -0,0 +1,67 @@
package com.gtnewhorizons.umbra.loading.fml.transformers;
import com.gtnewhorizons.angelica.glsm.loading.EcosystemNarrowRules;
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A scoped redirector that only transforms classes from known-misbehaving (core)mod packages.
* <p>
* Some mods prematurely load classes that call GL functions during coremod discovery/injectData.
* Those classes get missed by the late-registered {@link UmbraRedirectorTransformer}, so their
* GL calls permanently bypass GLSM.
* <p>
* Registered in {@code UmbraClientTweaker} constructor. Removed by {@code UmbraLateTweaker}
* once the full redirector is in its proper post-mixin position.
*
* @see EcosystemNarrowRules#EARLY_REDIRECTOR_TARGETS
*/
public class EarlyRedirectorTransformer implements IClassTransformer {
private final GLSMRedirector core = new GLSMRedirector();
private final String[] exclusions;
public EarlyRedirectorTransformer() {
final List<String> excl = new ArrayList<>(Arrays.asList(core.getCoreExclusions()));
excl.add("com.gtnewhorizons.umbra.loading");
exclusions = excl.toArray(new String[0]);
}
@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
boolean targeted = false;
for (String pkg : EcosystemNarrowRules.EARLY_REDIRECTOR_TARGETS) {
if (transformedName.startsWith(pkg)) {
targeted = true;
break;
}
}
if (!targeted) return basicClass;
for (String exclusion : exclusions) {
if (transformedName.startsWith(exclusion)) return basicClass;
}
if (!core.shouldTransform(basicClass)) return basicClass;
final ClassReader cr = new ClassReader(basicClass);
final ClassNode cn = new ClassNode();
cr.accept(cn, 0);
final boolean changed = core.transformClassNode(transformedName, cn);
if (changed) {
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw);
return cw.toByteArray();
}
return basicClass;
}
}
@@ -0,0 +1,46 @@
package com.gtnewhorizons.umbra.loading.fml.transformers;
import com.gtnewhorizons.umbra.loading.shared.UmbraClassDump;
import com.gtnewhorizons.umbra.loading.shared.transformers.UmbraRedirector;
import net.minecraft.launchwrapper.IClassTransformer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
/** IClassTransformer wrapper for {@link UmbraRedirector} */
public class UmbraRedirectorTransformer implements IClassTransformer {
private final UmbraRedirector inner;
private final String[] exclusions;
public UmbraRedirectorTransformer() {
inner = new UmbraRedirector();
exclusions = inner.getTransformerExclusions();
}
@Override
public byte[] transform(final String className, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;
for (String exclusion : exclusions) {
if (transformedName.startsWith(exclusion)) return basicClass;
}
if (!inner.shouldTransform(basicClass)) {
return basicClass;
}
final ClassReader cr = new ClassReader(basicClass);
final ClassNode cn = new ClassNode();
cr.accept(cn, 0);
final boolean changed = inner.transformClassNode(transformedName, cn);
if (changed) {
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw);
final byte[] bytes = cw.toByteArray();
UmbraClassDump.dumpClass(transformedName, basicClass, bytes, this);
return bytes;
}
return basicClass;
}
}
@@ -0,0 +1,75 @@
package com.gtnewhorizons.umbra.loading.fml.tweakers;
import com.gtnewhorizons.angelica.glsm.loading.EcosystemNarrowRules;
import com.gtnewhorizons.angelica.glsm.loading.TransformerNarrower;
import cpw.mods.fml.relauncher.FMLRelaunchLog;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.ITweaker;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import java.io.File;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Set;
/**
* Late tweaker that:
* 1. Narrows overly-broad transformer exclusions from other coremods
* 2. Removes the scoped early redirector
* 3. Registers the full redirector in its proper post-mixin position
*/
public class UmbraLateTweaker implements ITweaker {
private static final String EARLY_REDIRECTOR_CLASS = "com.gtnewhorizons.umbra.loading.fml.transformers.EarlyRedirectorTransformer";
@Override
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
}
@Override
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
narrowTransformerExclusions(classLoader);
}
@SuppressWarnings("unchecked")
private static void narrowTransformerExclusions(LaunchClassLoader classLoader) {
try {
final Field exceptionsField = Launch.classLoader.getClass().getDeclaredField("transformerExceptions");
exceptionsField.setAccessible(true);
final Set<String> exceptions = (Set<String>) exceptionsField.get(classLoader);
TransformerNarrower.narrow(exceptions, Launch.blackboard, "umbra", "Umbra",
EcosystemNarrowRules.ALL);
} catch (Exception e) {
FMLRelaunchLog.warning("[Umbra] Failed to narrow transformer exclusions: %s", e.getMessage());
}
}
@Override
public String getLaunchTarget() {
return null;
}
@Override
@SuppressWarnings("unchecked")
public String[] getLaunchArguments() {
// Remove the scoped early redirector and register the full one after mixins
try {
final Field transformersField = Launch.classLoader.getClass().getDeclaredField("transformers");
transformersField.setAccessible(true);
final List<IClassTransformer> transformers =
(List<IClassTransformer>) transformersField.get(Launch.classLoader);
transformers.removeIf(t -> t.getClass().getName().equals(EARLY_REDIRECTOR_CLASS));
} catch (Exception e) {
FMLRelaunchLog.warning("[Umbra] Failed to remove EarlyRedirectorTransformer: %s", e.getMessage());
}
final String transformer =
"com.gtnewhorizons.umbra.loading.fml.transformers.UmbraRedirectorTransformer";
FMLRelaunchLog.finer("[Umbra] Registering transformer %s", transformer);
Launch.classLoader.registerTransformer(transformer);
return new String[0];
}
}
@@ -0,0 +1,39 @@
package com.gtnewhorizons.umbra.loading.rfb;
import com.gtnewhorizons.umbra.loading.rfb.transformers.RFBUmbraRedirector;
import com.gtnewhorizons.umbra.loading.shared.AngelicaDetector;
import com.gtnewhorizons.retrofuturabootstrap.api.PluginContext;
import com.gtnewhorizons.retrofuturabootstrap.api.RetroFuturaBootstrap;
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
import com.gtnewhorizons.retrofuturabootstrap.api.RfbPlugin;
import net.minecraft.launchwrapper.Launch;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class UmbraRfbPlugin implements RfbPlugin {
@Override
public void onConstruction(@NotNull PluginContext ctx) {
Launch.blackboard.put("umbra.rfbPluginLoaded", Boolean.TRUE);
Launch.classLoader.addClassLoaderExclusion("com.gtnewhorizons.angelica.glsm.redirect.");
}
@Override
public @NotNull RfbClassTransformer @Nullable [] makeTransformers() {
// Detect Angelica -- if present, don't register any transformers
if (AngelicaDetector.isPresent()) {
Launch.blackboard.put("umbra.disabled", Boolean.TRUE);
return null;
}
final boolean isServer = (null == RetroFuturaBootstrap.API.launchClassLoader()
.findClassMetadata("net.minecraft.client.main.Main"));
if (isServer) {
return null;
}
return new RfbClassTransformer[] {
new RFBUmbraRedirector()
};
}
}
@@ -0,0 +1,63 @@
package com.gtnewhorizons.umbra.loading.rfb.transformers;
import com.gtnewhorizons.umbra.loading.shared.UmbraClassDump;
import com.gtnewhorizons.umbra.loading.shared.transformers.UmbraRedirector;
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
import com.gtnewhorizons.retrofuturabootstrap.api.ExtensibleClassLoader;
import com.gtnewhorizons.retrofuturabootstrap.api.RfbClassTransformer;
import org.intellij.lang.annotations.Pattern;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.jar.Manifest;
/** RfbClassTransformer wrapper for {@link UmbraRedirector} */
public class RFBUmbraRedirector implements RfbClassTransformer {
private final UmbraRedirector inner;
public RFBUmbraRedirector() {
inner = new UmbraRedirector();
}
@Pattern("[a-z0-9-]+")
@Override
public @NotNull String id() {
return "umbra-redirector";
}
@Override
public @NotNull String @Nullable [] sortAfter() {
return new String[] {"*", "mixin:mixin"};
}
@Override
public @NotNull String @Nullable [] sortBefore() {
return new String[] {"lwjgl3ify:redirect"};
}
@Override
public @NotNull String @Nullable [] additionalExclusions() {
return inner.getTransformerExclusions();
}
@Override
public boolean shouldTransformClass(@NotNull ExtensibleClassLoader classLoader,
@NotNull RfbClassTransformer.Context context, @Nullable Manifest manifest, @NotNull String className,
@NotNull ClassNodeHandle classNode) {
if (!classNode.isPresent()) {
return false;
}
return inner.shouldTransform(classNode.getOriginalBytes());
}
@Override
public void transformClass(@NotNull ExtensibleClassLoader classLoader, @NotNull RfbClassTransformer.Context context,
@Nullable Manifest manifest, @NotNull String className, @NotNull ClassNodeHandle classNode) {
final boolean changed = inner.transformClassNode(className, classNode.getNode());
if (changed) {
classNode.computeMaxs();
UmbraClassDump.dumpRFBClass(className, classNode, this);
}
}
}
@@ -0,0 +1,26 @@
package com.gtnewhorizons.umbra.loading.shared;
import net.minecraft.launchwrapper.Launch;
/**
* Detects whether Angelica is present. If so, Umbra should disable itself entirely since Angelica provides its own GLSM integration.
*/
public final class AngelicaDetector {
private static Boolean result;
public static boolean isPresent() {
if (result != null) return result;
boolean found = AngelicaDetector.class.getClassLoader().getResource("com/gtnewhorizons/angelica/loading/AngelicaTweaker.class") != null;
if (!found && Launch.blackboard != null) {
found = Boolean.TRUE.equals(Launch.blackboard.get("angelica.rfbPluginLoaded"));
}
result = found;
return found;
}
private AngelicaDetector() {}
}
@@ -0,0 +1,25 @@
package com.gtnewhorizons.umbra.loading.shared;
import com.gtnewhorizon.gtnhlib.asm.ASMUtil;
import com.gtnewhorizons.retrofuturabootstrap.api.ClassNodeHandle;
public class UmbraClassDump {
private static final boolean DUMP_CLASS = Boolean.getBoolean("umbra.dumpClass");
public static void dumpClass(String className, byte[] originalBytes, byte[] transformedBytes, Object transformer) {
if (DUMP_CLASS) {
ASMUtil.saveAsRawClassFile(originalBytes, className + "_PRE", transformer);
ASMUtil.saveAsRawClassFile(transformedBytes, className + "_POST", transformer);
}
}
public static void dumpRFBClass(String className, ClassNodeHandle classNode, Object transformer) {
if (DUMP_CLASS) {
final byte[] originalBytes = classNode.getOriginalBytes();
final byte[] transformedBytes = classNode.computeBytes();
ASMUtil.saveAsRawClassFile(originalBytes, className + "_PRE", transformer);
ASMUtil.saveAsRawClassFile(transformedBytes, className + "_POST", transformer);
}
}
}
@@ -0,0 +1,41 @@
package com.gtnewhorizons.umbra.loading.shared.transformers;
import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector;
import org.objectweb.asm.tree.ClassNode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Mod-level wrapper around {@link GLSMRedirector}. Adds Umbra-specific transformer exclusions
* and delegates all redirect logic to GLSM.
* <p>
* THIS CLASS MIGHT BE LOADED ON A DIFFERENT CLASS LOADER,
* IT SHOULD NOT CALL ANY CODE FROM THE MAIN MOD
*/
public final class UmbraRedirector {
private final GLSMRedirector core = new GLSMRedirector();
private final String[] allExclusions;
public UmbraRedirector() {
final List<String> exclusions = new ArrayList<>(Arrays.asList(core.getCoreExclusions()));
exclusions.add("com.gtnewhorizons.umbra.loading");
exclusions.add("com.gtnewhorizons.angelica.lwjgl3.");
allExclusions = exclusions.toArray(new String[0]);
}
public String[] getTransformerExclusions() {
return allExclusions;
}
public boolean shouldTransform(byte[] basicClass) {
return core.shouldTransform(basicClass);
}
/** @return Was the class changed? */
public boolean transformClassNode(String transformedName, ClassNode cn) {
return core.transformClassNode(transformedName, cn);
}
}
@@ -0,0 +1,39 @@
package com.gtnewhorizons.umbra.mixins;
import com.gtnewhorizon.gtnhmixins.builders.IMixins;
import com.gtnewhorizon.gtnhmixins.builders.MixinBuilder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public enum Mixins implements IMixins {
UMBRA_STARTUP(new MixinBuilder()
.setPhase(Phase.EARLY)
.addClientMixins(
"early.startup.MixinInitGLStateManager"
)
),
UMBRA_CORE_PROFILE(new MixinBuilder()
.setPhase(Phase.EARLY)
.addClientMixins(
"early.ffp.MixinTessellator_CoreProfile"
)
),
UMBRA_GLSM(new MixinBuilder()
.setPhase(Phase.EARLY)
.addClientMixins(
"early.glsm.MixinForgeHooksClient_CoreProfile",
"early.glsm.MixinSplashProgressCaching",
"early.glsm.MixinMinecraft_FrameHook",
"early.glsm.MixinWorldRenderer_VertexState"
)
),
;
private final MixinBuilder builder;
}
@@ -0,0 +1,23 @@
package com.gtnewhorizons.umbra.proxy;
import com.gtnewhorizons.umbra.UmbraMod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
public class ClientProxy extends CommonProxy {
@Override
public void preInit(FMLPreInitializationEvent event) {
UmbraMod.LOGGER.info("Umbra client preInit - GLSM initialized via mixin");
}
@Override
public void init(FMLInitializationEvent event) {
UmbraMod.LOGGER.info("Umbra client init");
}
@Override
public void postInit(FMLPostInitializationEvent event) {
}
}
@@ -0,0 +1,17 @@
package com.gtnewhorizons.umbra.proxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
public class CommonProxy {
public void preInit(FMLPreInitializationEvent event) {
}
public void init(FMLInitializationEvent event) {
}
public void postInit(FMLPostInitializationEvent event) {
}
}
@@ -0,0 +1,9 @@
name=Umbra
version=${version}
additionalVersions=
className=com.gtnewhorizons.umbra.loading.rfb.UmbraRfbPlugin
transformerExclusions=com.gtnewhorizons.umbra.loading.shared;com.gtnewhorizon.gtnhlib.asm
versionConstraints=
loadBefore=
loadAfter=mixin; lwjgl3ify
loadRequires=
@@ -0,0 +1 @@
com.gtnewhorizons.umbra.client.rendering.UmbraGLStateManagerService
@@ -0,0 +1 @@
com.gtnewhorizons.umbra.client.rendering.UmbraRenderSystemService
@@ -0,0 +1,2 @@
com.mitchej123.lwjgl.lwjgl3.LWJGL3Service
com.mitchej123.lwjgl.lwjgl2.LWJGL2Service
+13
View File
@@ -0,0 +1,13 @@
[{
"modid": "umbra",
"name": "Umbra",
"description": "Umbra replaces Minecraft 1.7.10’s legacy rendering pipeline with a modern, backend-agnostic core.",
"version": "${modVersion}",
"mcversion": "${minecraftVersion}",
"url": "",
"authorList": ["GTNH"],
"credits": "",
"logoFile": "",
"screenshots": [],
"dependencies": ["gtnhlib"]
}]
@@ -0,0 +1,11 @@
{
"required": true,
"minVersion": "0.8.3-GTNH",
"refmap": "mixins.umbra.refmap.json",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8",
"package": "com.gtnewhorizons.umbra.mixins",
"overwrites": {
"conformVisibility": true
}
}
@@ -0,0 +1,11 @@
{
"required": true,
"minVersion": "0.8.3-GTNH",
"refmap": "mixins.umbra.refmap.json",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8",
"package": "com.gtnewhorizons.umbra.mixins",
"overwrites": {
"conformVisibility": true
}
}
@@ -0,0 +1,53 @@
package com.gtnewhorizons.umbra.mixins.early.ffp;
import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager;
import com.gtnewhorizons.angelica.glsm.ITessellatorData;
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
import net.minecraft.client.renderer.Tessellator;
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.CallbackInfoReturnable;
/**
* Intercepts Tessellator.draw() to use streaming VBO+VAO for core profile rendering.
* Implements ITessellatorData so TessellatorStreamingDrawer can access Tessellator fields
* without depending on Minecraft classes.
*/
@Mixin(Tessellator.class)
public class MixinTessellator_CoreProfile implements ITessellatorData {
@Shadow public boolean isDrawing;
@Shadow public int vertexCount;
@Shadow public int[] rawBuffer;
@Shadow public int rawBufferIndex;
@Shadow public int rawBufferSize;
@Shadow public int drawMode;
@Shadow public boolean hasTexture;
@Shadow public boolean hasColor;
@Shadow public boolean hasNormals;
@Shadow public boolean hasBrightness;
@Shadow public void reset() {}
@Inject(method = "draw", at = @At("HEAD"), cancellable = true)
private void umbra$coreProfileDraw(CallbackInfoReturnable<Integer> cir) {
if (TessellatorManager.shouldInterceptDraw((Tessellator)(Object)this)) return;
cir.setReturnValue(TessellatorStreamingDrawer.draw((ITessellatorData) this));
}
@Override public boolean isDrawing() { return isDrawing; }
@Override public void setDrawing(boolean drawing) { isDrawing = drawing; }
@Override public int getVertexCount() { return vertexCount; }
@Override public int[] getRawBuffer() { return rawBuffer; }
@Override public int getRawBufferIndex() { return rawBufferIndex; }
@Override public int getRawBufferSize() { return rawBufferSize; }
@Override public void setRawBufferSize(int size) { rawBufferSize = size; }
@Override public void setRawBuffer(int[] buffer) { rawBuffer = buffer; }
@Override public int getDrawMode() { return drawMode; }
@Override public boolean hasTexture() { return hasTexture; }
@Override public boolean hasColor() { return hasColor; }
@Override public boolean hasNormals() { return hasNormals; }
@Override public boolean hasBrightness() { return hasBrightness; }
}
@@ -0,0 +1,101 @@
package com.gtnewhorizons.umbra.mixins.early.glsm;
import net.minecraftforge.client.ForgeHooksClient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.LWJGLException;
import org.lwjgl.LWJGLUtil;
import org.lwjgl.opengl.ContextAttribs;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.PixelFormat;
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 javax.imageio.ImageIO;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
/**
* Requests highest GL version with core profile context.
* Required for GLSM's FFP emulation to work correctly.
*/
@Mixin(value = ForgeHooksClient.class, remap = false)
public abstract class MixinForgeHooksClient_CoreProfile {
@Unique private static final Logger LOGGER = LogManager.getLogger("Umbra");
@Shadow static int stencilBits;
/**
* @author Umbra
* @reason Request highest GL version with core profile context for GLSM
*/
@Overwrite
public static void createDisplay() throws LWJGLException {
ImageIO.setUseCache(false);
stencilBits = 8;
final PixelFormat format = new PixelFormat().withDepthBits(24).withStencilBits(stencilBits);
final boolean lwjglDebug = Boolean.getBoolean("org.lwjgl.util.Debug");
final ContextAttribs attribs = new ContextAttribs(3, 3)
.withProfileCore(true).withForwardCompatible(true).withDebug(lwjglDebug);
final MethodHandle setMajor, setMinor;
try {
final Field majorField = ContextAttribs.class.getDeclaredField("majorVersion");
final Field minorField = ContextAttribs.class.getDeclaredField("minorVersion");
majorField.setAccessible(true);
minorField.setAccessible(true);
final MethodHandles.Lookup lookup = MethodHandles.lookup();
setMajor = lookup.unreflectSetter(majorField);
setMinor = lookup.unreflectSetter(minorField);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to obtain ContextAttribs version setters", e);
}
final int platformMaxMajor = 4;
final int platformMaxMinor = (LWJGLUtil.getPlatform() == LWJGLUtil.PLATFORM_MACOSX) ? 1 : 6;
// Probe from highest to lowest
Exception lastException = null;
for (int major = platformMaxMajor; major >= 3; --major) {
final int maxMinor = (major == 4) ? platformMaxMinor : 3;
final int minMinor = (major == 3) ? 3 : 0;
for (int minor = maxMinor; minor >= minMinor; --minor) {
final Exception e = umbra$tryCreate(attribs, format, setMajor, setMinor, major, minor);
if (e == null) {
LOGGER.info("Created GL {}.{} core profile context", major, minor);
return;
}
lastException = e;
}
}
LOGGER.error("FATAL: Failed to create OpenGL core profile context. Error: {}", lastException != null ? lastException.getMessage() : "unknown");
if (lastException instanceof LWJGLException lwjgl) {
throw lwjgl;
}
throw new LWJGLException("Failed to create OpenGL core profile context", lastException);
}
@Unique
private static Exception umbra$tryCreate(ContextAttribs attribs, PixelFormat format, MethodHandle setMajor, MethodHandle setMinor, int major, int minor) {
try {
setMajor.invokeExact(attribs, major);
setMinor.invokeExact(attribs, minor);
} catch (Throwable t) {
throw new RuntimeException("Failed to set ContextAttribs version", t);
}
try {
Display.create(format, attribs);
return null;
} catch (LWJGLException | RuntimeException e) {
try { Display.destroy(); } catch (Exception ignored) {}
return e;
}
}
}
@@ -0,0 +1,20 @@
package com.gtnewhorizons.umbra.mixins.early.glsm;
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
import net.minecraft.client.Minecraft;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Per-frame hook to flush streaming buffer fences.
*/
@Mixin(Minecraft.class)
public class MixinMinecraft_FrameHook {
@Inject(method = "runGameLoop", at = @At("HEAD"))
private void umbra$onFrameStart(CallbackInfo ci) {
TessellatorStreamingDrawer.endFrame();
}
}
@@ -0,0 +1,32 @@
package com.gtnewhorizons.umbra.mixins.early.glsm;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/**
* Controls GLSM state during splash screen lifecycle.
* Initializes splash tessellator on start, marks splash complete on finish.
*/
@SuppressWarnings("deprecation")
@Mixin(value = cpw.mods.fml.client.SplashProgress.class, remap = false)
public class MixinSplashProgressCaching {
private static final Logger LOGGER = LogManager.getLogger("Umbra");
@Inject(method = "start", at = @At("HEAD"))
private static void umbra$initSplashTessellator(CallbackInfo ci) {
ImmediateModeRecorder.initSplashTessellator();
}
@Inject(method = "finish", at = @At("RETURN"))
private static void umbra$enableCachingOnFinish(CallbackInfo ci) {
ImmediateModeRecorder.destroySplashTessellator();
GLStateManager.markSplashComplete();
LOGGER.info("Splash Complete");
}
}
@@ -0,0 +1,32 @@
package com.gtnewhorizons.umbra.mixins.early.glsm;
import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.shader.TesselatorVertexState;
import net.minecraft.entity.EntityLivingBase;
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;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import net.minecraftforge.client.ForgeHooksClient;
@Mixin(WorldRenderer.class)
public class MixinWorldRenderer_VertexState {
@Shadow private TesselatorVertexState vertexState;
@Inject(method = "postRenderBlocks", at = @At("HEAD"), cancellable = true)
private void umbra$skipVertexStateForDirectTessellator(int pass, EntityLivingBase entity, CallbackInfo ci) {
if (Tessellator.instance instanceof DirectTessellator) {
// Can't get vertex state from DirectTessellator, draw and return
Tessellator.instance.draw();
ForgeHooksClient.onPostRenderWorld((WorldRenderer)(Object)this, pass);
GLStateManager.glPopMatrix();
GLStateManager.glEndList();
ci.cancel();
}
}
}
@@ -0,0 +1,35 @@
package com.gtnewhorizons.umbra.mixins.early.startup;
import com.gtnewhorizons.angelica.glsm.GLStateManager;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks;
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
import com.gtnewhorizons.angelica.glsm.streaming.TessellatorStreamingDrawer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.OpenGlHelper;
import org.lwjgl.opengl.Display;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = OpenGlHelper.class, priority = 100)
public class MixinInitGLStateManager {
@Inject(method = "initializeTextures", at = @At("RETURN"))
private static void umbra$initializeGLStateManager(CallbackInfo ci) {
final Minecraft mc = Minecraft.getMinecraft();
GLStateManager.setDrawableGL(Display.getDrawable());
GLStateManager.initialize(GLSMInitConfig.builder()
.displaySize(mc.displayWidth, mc.displayHeight)
.framebufferSupported(OpenGlHelper.framebufferSupported)
.fboEnabled(mc.gameSettings.fboEnable)
.directDrawer(TessellatorStreamingDrawer::drawDirect)
.streamingDrawerDestroy(TessellatorStreamingDrawer::destroy)
.build());
GLSMHooks.LIGHTMAP_COORDS.addListener(event -> {
OpenGlHelper.lastBrightnessX = event.x;
OpenGlHelper.lastBrightnessY = event.y;
});
}
}