mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-10-25 20:22:22 +00:00
Standalone GLSM mod - Umbra (#1642)
This commit is contained in:
1 parent
8d53e25e0a
commit
59a4b84023
52 files changed
+2084
-158
No files matched your search
+1
-1
@@ -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() {}
|
||||
}
|
||||
+42
-45
@@ -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
|
||||
|
||||
+21
-17
@@ -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);
|
||||
|
||||
+4
-69
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+32
-3
@@ -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; }
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
BIN
Binary file not shown.
@@ -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
|
||||
Vendored
+248
@@ -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" "$@"
|
||||
Vendored
+93
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+115
@@ -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); }
|
||||
}
|
||||
+75
@@ -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();
|
||||
}
|
||||
}
|
||||
+67
@@ -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;
|
||||
}
|
||||
}
|
||||
+46
@@ -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;
|
||||
}
|
||||
}
|
||||
+75
@@ -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()
|
||||
};
|
||||
}
|
||||
}
|
||||
+63
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
+53
@@ -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; }
|
||||
}
|
||||
+101
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -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();
|
||||
}
|
||||
}
|
||||
+32
@@ -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");
|
||||
}
|
||||
}
|
||||
+32
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user