diff --git a/dependencies.gradle b/dependencies.gradle index 155f2eb..87fbe36 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -22,4 +22,7 @@ dependencies { compileOnly('org.jetbrains:annotations:24.0.1') compileOnly("org.projectlombok:lombok:1.18.22") {transitive = false } annotationProcessor("org.projectlombok:lombok:1.18.22") + + // For CapturingTesselator compat + compileOnly("com.falsepattern:falsetweaks-mc1.7.10:3.3.2:api") } diff --git a/gradle.properties b/gradle.properties index 869cc51..276cfa3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -75,7 +75,7 @@ replaceGradleTokenInFile = # In case your mod provides an API for other mods to implement you may declare its package here. Otherwise, you can # leave this property empty. # Example value: (apiPackage = api) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.api -apiPackage = +apiPackage = api # Specify the configuration file for Forge's access transformers here. It must be placed into /src/main/resources/META-INF/ # There can be multiple files in a space-separated list. diff --git a/repositories.gradle b/repositories.gradle index c884390..ccb1da2 100644 --- a/repositories.gradle +++ b/repositories.gradle @@ -1,5 +1,11 @@ // Add any additional repositories for your dependencies here repositories { - + maven { + name = 'mavenpattern' + url = 'https://mvn.falsepattern.com/releases' + content { + includeGroup 'com.falsepattern' + } + } } diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/ClientProxy.java b/src/main/java/com/gtnewhorizon/gtnhlib/ClientProxy.java index edc3e3e..5eaefcc 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/ClientProxy.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/ClientProxy.java @@ -10,6 +10,8 @@ import com.gtnewhorizon.gtnhlib.client.model.ModelLoader; import com.gtnewhorizon.gtnhlib.commands.ItemInHandCommand; +import com.gtnewhorizon.gtnhlib.compat.FalseTweaks; +import com.gtnewhorizon.gtnhlib.compat.Mods; import com.gtnewhorizon.gtnhlib.eventbus.EventBusSubscriber; import com.gtnewhorizon.gtnhlib.util.AboveHotbarHUD; @@ -23,7 +25,7 @@ public class ClientProxy extends CommonProxy { private static boolean modelsBaked = false; - + public static boolean doThreadSafetyChecks = true; private final Minecraft mc = Minecraft.getMinecraft(); @Override @@ -41,6 +43,13 @@ public void init(FMLInitializationEvent event) { public void postInit(FMLPostInitializationEvent event) { super.postInit(event); + if (Mods.FALSETWEAKS) { + doThreadSafetyChecks = FalseTweaks.doTessSafetyChecks(); + if (!doThreadSafetyChecks) { + GTNHLib.info("FalseTweaks threaded rendering is enabled - disabling GTNHLib's thread safety checks"); + } + } + if (shouldLoadModels()) { Minecraft.getMinecraft().refreshResources(); ModelLoader.loadModels(); diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/api/CapturingTesselator.java b/src/main/java/com/gtnewhorizon/gtnhlib/api/CapturingTesselator.java new file mode 100644 index 0000000..37e0f06 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/api/CapturingTesselator.java @@ -0,0 +1,25 @@ +package com.gtnewhorizon.gtnhlib.api; + +import net.minecraft.client.renderer.Tessellator; + +import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager; + +@SuppressWarnings("unused") +public interface CapturingTesselator { + + /** + * @return True if this thread is capturing quads, false otherwise + */ + static boolean isCapturing() { + return TessellatorManager.isCurrentlyCapturing(); + } + + /** + * @throws IllegalStateException If the thread is not capturing and is not the main one. + * @return The CapturingTesselator for this thread if capturing, or else {@link Tessellator#instance} if on the main + * one. + */ + static Tessellator getThreadTesselator() { + return TessellatorManager.get(); + } +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/client/renderer/TessellatorManager.java b/src/main/java/com/gtnewhorizon/gtnhlib/client/renderer/TessellatorManager.java index c08631f..a8b11c4 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/client/renderer/TessellatorManager.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/client/renderer/TessellatorManager.java @@ -32,6 +32,10 @@ public static Tessellator get() { } } + public static boolean isCurrentlyCapturing() { + return currentlyCapturing.get(); + } + public static boolean isOnMainThread() { return Thread.currentThread() == mainThread; } diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/compat/FalseTweaks.java b/src/main/java/com/gtnewhorizon/gtnhlib/compat/FalseTweaks.java new file mode 100644 index 0000000..595fdaa --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/compat/FalseTweaks.java @@ -0,0 +1,14 @@ +package com.gtnewhorizon.gtnhlib.compat; + +import com.falsepattern.falsetweaks.api.ThreadedChunkUpdates; + +public class FalseTweaks { + + /** + * When FalseTweaks is loaded, it may inject into the Tesselator to do its own threaded chunk building. If it's + * doing that, disable our checks and let FT handle it. + */ + public static boolean doTessSafetyChecks() { + return !ThreadedChunkUpdates.isEnabled(); + } +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/compat/Mods.java b/src/main/java/com/gtnewhorizon/gtnhlib/compat/Mods.java new file mode 100644 index 0000000..7a4ade5 --- /dev/null +++ b/src/main/java/com/gtnewhorizon/gtnhlib/compat/Mods.java @@ -0,0 +1,8 @@ +package com.gtnewhorizon.gtnhlib.compat; + +import cpw.mods.fml.common.Loader; + +public class Mods { + + public static final boolean FALSETWEAKS = Loader.isModLoaded("falsetweaks"); +} diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/mixins/Mixins.java b/src/main/java/com/gtnewhorizon/gtnhlib/mixins/Mixins.java index e6e094c..e1cd63a 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/mixins/Mixins.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/mixins/Mixins.java @@ -14,8 +14,8 @@ public enum Mixins { - TESSELLATOR(new Builder("Sodium").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT).setPhase(Phase.EARLY) - .setApplyIf(() -> true).addMixinClasses("MixinTessellator")), + TESSELLATOR(new Builder("Thread safety checks for the Tesselator").addTargetedMod(TargetedMod.VANILLA) + .setSide(Side.CLIENT).setPhase(Phase.EARLY).setApplyIf(() -> true).addMixinClasses("MixinTessellator")), WAVEFRONT_VBO(new Builder("WavefrontObject").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT) .setPhase(Phase.EARLY).setApplyIf(() -> true).addMixinClasses("MixinWavefrontObject")), GUI_MOD_LIST(new Builder("Auto config ui").addTargetedMod(TargetedMod.VANILLA).setSide(Side.CLIENT) diff --git a/src/main/java/com/gtnewhorizon/gtnhlib/mixins/early/MixinTessellator.java b/src/main/java/com/gtnewhorizon/gtnhlib/mixins/early/MixinTessellator.java index f1cc4ca..99a395f 100644 --- a/src/main/java/com/gtnewhorizon/gtnhlib/mixins/early/MixinTessellator.java +++ b/src/main/java/com/gtnewhorizon/gtnhlib/mixins/early/MixinTessellator.java @@ -1,5 +1,7 @@ package com.gtnewhorizon.gtnhlib.mixins.early; +import static com.gtnewhorizon.gtnhlib.ClientProxy.doThreadSafetyChecks; + import java.nio.Buffer; import java.nio.ByteBuffer; @@ -18,8 +20,6 @@ @Mixin(Tessellator.class) public abstract class MixinTessellator implements ITessellatorInstance { - @Shadow - public int vertexCount; @Shadow public boolean isDrawing; @@ -40,7 +40,7 @@ private Buffer removeStaticBufferResetOutsideSingleton(ByteBuffer buffer) { @Inject(method = "draw", at = @At("HEAD")) private void preventOffMainThreadDrawing(CallbackInfoReturnable cir) { - if (!TessellatorManager.isMainInstance(this)) { + if (doThreadSafetyChecks && !TessellatorManager.isMainInstance(this)) { throw new RuntimeException("Tried to draw on a tessellator that isn't on the main thread!"); } }