diff --git a/buildSrc/src/main/kotlin/dev/engine_room/gradle/platform/PlatformExtension.kt b/buildSrc/src/main/kotlin/dev/engine_room/gradle/platform/PlatformExtension.kt index fc5df1a45..66831cedf 100644 --- a/buildSrc/src/main/kotlin/dev/engine_room/gradle/platform/PlatformExtension.kt +++ b/buildSrc/src/main/kotlin/dev/engine_room/gradle/platform/PlatformExtension.kt @@ -23,16 +23,11 @@ open class PlatformExtension(val project: Project) { var apiArtifactId: String = "flywheel-${project.name}-api-${project.property("artifact_minecraft_version")}" - private val sources = mutableSetOf() private val commonSourceSets: SourceSetContainer by lazy { commonProject.the() } - fun sources(vararg sourceSets: SourceSet) { - this.sources.addAll(sourceSets) - } - - fun setupLoomMod() { + fun setupLoomMod(vararg sourceSets: SourceSet) { project.the().mods.maybeCreate("main").apply { - sources.forEach(::sourceSet) + sourceSets.forEach(::sourceSet) } } @@ -61,13 +56,13 @@ open class PlatformExtension(val project: Project) { } } - fun compileWithCommonSourceSets() { + fun compileWithCommonSourceSets(vararg sourceSets: SourceSet) { project.tasks.apply { withType().configureEach { JarTaskSet.excludeDuplicatePackageInfos(this) } - sources.forEach { + sourceSets.forEach { val commonSourceSet = commonSourceSets.named(it.name).get() named(it.compileJavaTaskName).configure { @@ -80,10 +75,10 @@ open class PlatformExtension(val project: Project) { } } - fun setupFatJar() { + fun setupFatJar(vararg sourceSets: SourceSet) { project.tasks.apply { - val extraSourceSets = sources.filter { it.name != "main" }.toList() - val commonSources = sources.map { commonSourceSets.named(it.name).get() } + val extraSourceSets = sourceSets.filter { it.name != "main" }.toList() + val commonSources = sourceSets.map { commonSourceSets.named(it.name).get() } named("jar").configure { extraSourceSets.forEach { from(it.output) } diff --git a/buildSrc/src/main/kotlin/dev/engine_room/gradle/subproject/SubprojectPlugin.kt b/buildSrc/src/main/kotlin/dev/engine_room/gradle/subproject/SubprojectPlugin.kt index e6783a313..6c3230f0d 100644 --- a/buildSrc/src/main/kotlin/dev/engine_room/gradle/subproject/SubprojectPlugin.kt +++ b/buildSrc/src/main/kotlin/dev/engine_room/gradle/subproject/SubprojectPlugin.kt @@ -96,6 +96,7 @@ class SubprojectPlugin: Plugin { private fun setupDependencies(project: Project) { project.dependencies.apply { val minecraft_version: String by project + val parchment_minecraft_version: String by project val parchment_version: String by project val loom = project.the() @@ -103,7 +104,7 @@ class SubprojectPlugin: Plugin { add("mappings", loom.layered { officialMojangMappings() - parchment("org.parchmentmc.data:parchment-${minecraft_version}:${parchment_version}@zip") + parchment("org.parchmentmc.data:parchment-${parchment_minecraft_version}:${parchment_version}@zip") }) add("api", "com.google.code.findbugs:jsr305:3.0.2") diff --git a/buildSrc/src/main/kotlin/dev/engine_room/gradle/transitive/TransitiveSourceSetsExtension.kt b/buildSrc/src/main/kotlin/dev/engine_room/gradle/transitive/TransitiveSourceSetsExtension.kt index c259546df..05fa4dc46 100644 --- a/buildSrc/src/main/kotlin/dev/engine_room/gradle/transitive/TransitiveSourceSetsExtension.kt +++ b/buildSrc/src/main/kotlin/dev/engine_room/gradle/transitive/TransitiveSourceSetsExtension.kt @@ -25,7 +25,7 @@ open class TransitiveSourceSetsExtension(private val project: Project) { fun createCompileConfigurations() { val configs = transitives.mapValues { (sourceSet, _) -> - project.configurations.create("for${sourceSet.name.capitalize()}") { + project.configurations.create("for${sourceSet.name.replaceFirstChar { it.uppercase() }}") { isCanBeConsumed = true isCanBeResolved = false } @@ -43,7 +43,7 @@ open class TransitiveSourceSetsExtension(private val project: Project) { fun createRuntimeConfigurations() { val configs = transitives.mapValues { (sourceSet, _) -> - project.configurations.create("run${sourceSet.name.capitalize()}") { + project.configurations.create("run${sourceSet.name.replaceFirstChar { it.uppercase() }}") { isCanBeConsumed = true isCanBeResolved = false } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 23d1f23b4..5aaf88778 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -9,6 +9,7 @@ plugins { val api = sourceSets.create("api") val lib = sourceSets.create("lib") val backend = sourceSets.create("backend") +val stubs = sourceSets.create("stubs") val main = sourceSets.getByName("main") transitiveSourceSets { @@ -25,8 +26,11 @@ transitiveSourceSets { rootCompile() compile(api, lib) } + sourceSet(stubs) { + rootCompile() + } sourceSet(main) { - compile(api, lib, backend) + compile(api, lib, backend, stubs) } sourceSet(sourceSets.getByName("test")) { implementation(api, lib, backend) @@ -42,6 +46,7 @@ jarSets { outgoing("commonApiOnly", api) outgoing("commonLib", lib) outgoing("commonBackend", backend) + outgoing("commonStubs", stubs) outgoing("commonImpl", main) // For publishing. diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java b/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java index 81cdf2408..c6de7eb47 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/Backends.java @@ -9,7 +9,7 @@ import dev.engine_room.flywheel.backend.engine.indirect.IndirectDrawManager; import dev.engine_room.flywheel.backend.engine.instancing.InstancedDrawManager; import dev.engine_room.flywheel.backend.gl.GlCompat; import dev.engine_room.flywheel.lib.backend.SimpleBackend; -import dev.engine_room.flywheel.lib.util.ShadersModHandler; +import dev.engine_room.flywheel.lib.util.ShadersModHelper; public final class Backends { /** @@ -18,7 +18,7 @@ public final class Backends { public static final Backend INSTANCING = SimpleBackend.builder() .engineFactory(level -> new EngineImpl(level, new InstancedDrawManager(InstancingPrograms.get()), 256)) .priority(500) - .supported(() -> GlCompat.SUPPORTS_INSTANCING && InstancingPrograms.allLoaded() && !ShadersModHandler.isShaderPackInUse()) + .supported(() -> GlCompat.SUPPORTS_INSTANCING && InstancingPrograms.allLoaded() && !ShadersModHelper.isShaderPackInUse()) .register(Flywheel.rl("instancing")); /** @@ -27,7 +27,7 @@ public final class Backends { public static final Backend INDIRECT = SimpleBackend.builder() .engineFactory(level -> new EngineImpl(level, new IndirectDrawManager(IndirectPrograms.get()), 256)) .priority(1000) - .supported(() -> GlCompat.SUPPORTS_INDIRECT && IndirectPrograms.allLoaded() && !ShadersModHandler.isShaderPackInUse()) + .supported(() -> GlCompat.SUPPORTS_INDIRECT && IndirectPrograms.allLoaded() && !ShadersModHelper.isShaderPackInUse()) .register(Flywheel.rl("indirect")); private Backends() { diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/internal/FlwLibLink.java b/common/src/lib/java/dev/engine_room/flywheel/lib/internal/FlwLibLink.java index 9a112c07d..c33ae35c1 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/internal/FlwLibLink.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/internal/FlwLibLink.java @@ -24,4 +24,12 @@ public interface FlwLibLink { void compileModelPart(ModelPart part, PoseStack.Pose pose, VertexConsumer consumer, int light, int overlay, float red, float green, float blue, float alpha); Deque getPoseStack(PoseStack stack); + + boolean isIrisLoaded(); + + boolean isOptifineInstalled(); + + boolean isShaderPackInUse(); + + boolean isRenderingShadowPass(); } diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/internal/FlwLibXplat.java b/common/src/lib/java/dev/engine_room/flywheel/lib/internal/FlwLibXplat.java index 2809537ee..fa58991ca 100644 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/internal/FlwLibXplat.java +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/internal/FlwLibXplat.java @@ -1,13 +1,11 @@ package dev.engine_room.flywheel.lib.internal; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnknownNullability; import dev.engine_room.flywheel.api.internal.DependencyInjection; import dev.engine_room.flywheel.lib.model.baked.BakedModelBuilder; import dev.engine_room.flywheel.lib.model.baked.BlockModelBuilder; import dev.engine_room.flywheel.lib.model.baked.MultiBlockModelBuilder; -import dev.engine_room.flywheel.lib.util.ShadersModHandler; import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.ModelManager; @@ -29,7 +27,4 @@ public interface FlwLibXplat { BlockModelBuilder createBlockModelBuilder(BlockState state); MultiBlockModelBuilder createMultiBlockModelBuilder(BlockAndTintGetter level, Iterable positions); - - @Nullable - ShadersModHandler.InternalHandler createIrisHandler(); } diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/util/ShadersModHandler.java b/common/src/lib/java/dev/engine_room/flywheel/lib/util/ShadersModHandler.java deleted file mode 100644 index ccf26ce07..000000000 --- a/common/src/lib/java/dev/engine_room/flywheel/lib/util/ShadersModHandler.java +++ /dev/null @@ -1,128 +0,0 @@ -package dev.engine_room.flywheel.lib.util; - -import java.lang.reflect.Field; -import java.util.function.BooleanSupplier; - -import org.jetbrains.annotations.ApiStatus; - -import dev.engine_room.flywheel.lib.internal.FlwLibLink; -import dev.engine_room.flywheel.lib.internal.FlwLibXplat; - -public final class ShadersModHandler { - private static final String OPTIFINE_ROOT_PACKAGE = "net.optifine"; - - private static final boolean IS_IRIS_LOADED; - private static final boolean IS_OPTIFINE_INSTALLED; - private static final InternalHandler INTERNAL_HANDLER; - - static { - InternalHandler irisHandler = FlwLibXplat.INSTANCE - .createIrisHandler(); - IS_IRIS_LOADED = irisHandler != null; - - Package optifinePackage = Package.getPackage(OPTIFINE_ROOT_PACKAGE); - IS_OPTIFINE_INSTALLED = optifinePackage != null; - - // OptiFine and Iris/Oculus are assumed to be mutually exclusive - - if (IS_IRIS_LOADED) { - FlwLibLink.INSTANCE.getLogger().debug("Iris detected."); - INTERNAL_HANDLER = irisHandler; - } else if (IS_OPTIFINE_INSTALLED) { - FlwLibLink.INSTANCE.getLogger().debug("OptiFine detected."); - INTERNAL_HANDLER = new OptifineHandler(); - } else { - FlwLibLink.INSTANCE.getLogger().debug("No shaders mod detected."); - INTERNAL_HANDLER = new InternalHandler() {}; - } - } - - private ShadersModHandler() { - } - - public static boolean isIrisLoaded() { - return IS_IRIS_LOADED; - } - - public static boolean isOptifineInstalled() { - return IS_OPTIFINE_INSTALLED; - } - - public static boolean isShaderPackInUse() { - return INTERNAL_HANDLER.isShaderPackInUse(); - } - - public static boolean isRenderingShadowPass() { - return INTERNAL_HANDLER.isRenderingShadowPass(); - } - - @ApiStatus.Internal - public static void init() { - } - - @ApiStatus.Internal - public interface InternalHandler { - default boolean isShaderPackInUse() { - return false; - } - - default boolean isRenderingShadowPass() { - return false; - } - } - - // evil reflection - private static class OptifineHandler implements InternalHandler { - private final BooleanSupplier shadersEnabledSupplier; - private final BooleanSupplier shadowPassSupplier; - - private OptifineHandler() { - shadersEnabledSupplier = createShadersEnabledSupplier(); - shadowPassSupplier = createShadowPassSupplier(); - } - - @Override - public boolean isShaderPackInUse() { - return shadersEnabledSupplier.getAsBoolean(); - } - - @Override - public boolean isRenderingShadowPass() { - return shadowPassSupplier.getAsBoolean(); - } - - private static BooleanSupplier createShadersEnabledSupplier() { - try { - Class ofShaders = Class.forName("net.optifine.shaders.Shaders"); - Field field = ofShaders.getDeclaredField("shaderPackLoaded"); - field.setAccessible(true); - return () -> { - try { - return field.getBoolean(null); - } catch (IllegalAccessException e) { - return false; - } - }; - } catch (Exception e) { - return () -> false; - } - } - - private static BooleanSupplier createShadowPassSupplier() { - try { - Class ofShaders = Class.forName("net.optifine.shaders.Shaders"); - Field field = ofShaders.getDeclaredField("isShadowPass"); - field.setAccessible(true); - return () -> { - try { - return field.getBoolean(null); - } catch (IllegalAccessException e) { - return false; - } - }; - } catch (Exception e) { - return () -> false; - } - } - } -} diff --git a/common/src/lib/java/dev/engine_room/flywheel/lib/util/ShadersModHelper.java b/common/src/lib/java/dev/engine_room/flywheel/lib/util/ShadersModHelper.java new file mode 100644 index 000000000..abfc3bcbe --- /dev/null +++ b/common/src/lib/java/dev/engine_room/flywheel/lib/util/ShadersModHelper.java @@ -0,0 +1,19 @@ +package dev.engine_room.flywheel.lib.util; + +import dev.engine_room.flywheel.lib.internal.FlwLibLink; + +public final class ShadersModHelper { + public static final boolean IS_IRIS_LOADED = FlwLibLink.INSTANCE.isIrisLoaded(); + public static final boolean IS_OPTIFINE_INSTALLED = FlwLibLink.INSTANCE.isOptifineInstalled(); + + private ShadersModHelper() { + } + + public static boolean isShaderPackInUse() { + return FlwLibLink.INSTANCE.isShaderPackInUse(); + } + + public static boolean isRenderingShadowPass() { + return FlwLibLink.INSTANCE.isRenderingShadowPass(); + } +} diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/FlwImpl.java b/common/src/main/java/dev/engine_room/flywheel/impl/FlwImpl.java index 914959997..909c39647 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/FlwImpl.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/FlwImpl.java @@ -6,7 +6,6 @@ import org.slf4j.LoggerFactory; import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.backend.FlwBackend; import dev.engine_room.flywheel.impl.registry.IdRegistryImpl; -import dev.engine_room.flywheel.lib.util.ShadersModHandler; import dev.engine_room.flywheel.vanilla.VanillaVisuals; public final class FlwImpl { @@ -21,7 +20,6 @@ public final class FlwImpl { BackendManagerImpl.init(); // lib - ShadersModHandler.init(); // backend FlwBackend.init(FlwConfig.INSTANCE.backendConfig()); diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/FlwImplXplat.java b/common/src/main/java/dev/engine_room/flywheel/impl/FlwImplXplat.java index 13675de93..c8fb9fec1 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/FlwImplXplat.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/FlwImplXplat.java @@ -6,9 +6,15 @@ import net.minecraft.client.multiplayer.ClientLevel; public interface FlwImplXplat { FlwImplXplat INSTANCE = DependencyInjection.load(FlwImplXplat.class, "dev.engine_room.flywheel.impl.FlwImplXplatImpl"); + boolean isModLoaded(String modId); + void dispatchReloadLevelRendererEvent(ClientLevel level); String getVersionStr(); FlwConfig getConfig(); + + boolean useSodium0_6Compat(); + + boolean useIrisCompat(); } diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/FlwLibLinkImpl.java b/common/src/main/java/dev/engine_room/flywheel/impl/FlwLibLinkImpl.java index 47fffeeb6..1b1e19c2e 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/FlwLibLinkImpl.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/FlwLibLinkImpl.java @@ -8,6 +8,8 @@ import org.slf4j.Logger; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; +import dev.engine_room.flywheel.impl.compat.IrisCompat; +import dev.engine_room.flywheel.impl.compat.OptifineCompat; import dev.engine_room.flywheel.impl.extension.PoseStackExtension; import dev.engine_room.flywheel.impl.mixin.ModelPartAccessor; import dev.engine_room.flywheel.impl.mixin.PoseStackAccessor; @@ -40,4 +42,36 @@ public class FlwLibLinkImpl implements FlwLibLink { public Deque getPoseStack(PoseStack stack) { return ((PoseStackAccessor) stack).flywheel$getPoseStack(); } + + @Override + public boolean isIrisLoaded() { + return IrisCompat.ACTIVE; + } + + @Override + public boolean isOptifineInstalled() { + return OptifineCompat.IS_INSTALLED; + } + + @Override + public boolean isShaderPackInUse() { + if (IrisCompat.ACTIVE) { + return IrisCompat.isShaderPackInUse(); + } else if (OptifineCompat.IS_INSTALLED) { + return OptifineCompat.isShaderPackInUse(); + } else { + return false; + } + } + + @Override + public boolean isRenderingShadowPass() { + if (IrisCompat.ACTIVE) { + return IrisCompat.isRenderingShadowPass(); + } else if (OptifineCompat.IS_INSTALLED) { + return OptifineCompat.isRenderingShadowPass(); + } else { + return false; + } + } } diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/compat/CompatMod.java b/common/src/main/java/dev/engine_room/flywheel/impl/compat/CompatMod.java new file mode 100644 index 000000000..725208bfa --- /dev/null +++ b/common/src/main/java/dev/engine_room/flywheel/impl/compat/CompatMod.java @@ -0,0 +1,18 @@ +package dev.engine_room.flywheel.impl.compat; + +import dev.engine_room.flywheel.impl.FlwImplXplat; + +public enum CompatMod { + EMBEDDIUM("embeddium"), + IRIS("iris"), + OCULUS("oculus"), + SODIUM("sodium"); + + public final String id; + public final boolean isLoaded; + + CompatMod(String modId) { + id = modId; + isLoaded = FlwImplXplat.INSTANCE.isModLoaded(modId); + } +} diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/compat/IrisCompat.java b/common/src/main/java/dev/engine_room/flywheel/impl/compat/IrisCompat.java new file mode 100644 index 000000000..97fc5f5e5 --- /dev/null +++ b/common/src/main/java/dev/engine_room/flywheel/impl/compat/IrisCompat.java @@ -0,0 +1,46 @@ +package dev.engine_room.flywheel.impl.compat; + +import dev.engine_room.flywheel.impl.FlwImpl; +import dev.engine_room.flywheel.impl.FlwImplXplat; +import net.irisshaders.iris.api.v0.IrisApi; + +public final class IrisCompat { + public static final boolean ACTIVE = FlwImplXplat.INSTANCE.useIrisCompat(); + + static { + if (ACTIVE) { + FlwImpl.LOGGER.debug("Detected Iris"); + } + } + + private IrisCompat() { + } + + public static boolean isShaderPackInUse() { + if (!ACTIVE) { + return false; + } + + return Internals.isShaderPackInUse(); + } + + public static boolean isRenderingShadowPass() { + if (!ACTIVE) { + return false; + } + + return Internals.isRenderingShadowPass(); + } + + private static final class Internals { + static boolean isShaderPackInUse() { + return IrisApi.getInstance() + .isShaderPackInUse(); + } + + static boolean isRenderingShadowPass() { + return IrisApi.getInstance() + .isRenderingShadowPass(); + } + } +} diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/compat/OptifineCompat.java b/common/src/main/java/dev/engine_room/flywheel/impl/compat/OptifineCompat.java new file mode 100644 index 000000000..42dfe4687 --- /dev/null +++ b/common/src/main/java/dev/engine_room/flywheel/impl/compat/OptifineCompat.java @@ -0,0 +1,73 @@ +package dev.engine_room.flywheel.impl.compat; + +import java.lang.reflect.Field; + +import org.jetbrains.annotations.Nullable; + +import dev.engine_room.flywheel.impl.FlwImpl; + +public final class OptifineCompat { + private static final String OPTIFINE_ROOT_PACKAGE = "net.optifine"; + + public static final boolean IS_INSTALLED; + @Nullable + private static final Field SHADER_PACK_LOADED_FIELD; + @Nullable + private static final Field IS_SHADOW_PASS_FIELD; + + static { + Field shaderPackLoadedField = null; + Field isShadowPassField = null; + + Package optifinePackage = Package.getPackage(OPTIFINE_ROOT_PACKAGE); + IS_INSTALLED = optifinePackage != null; + + if (IS_INSTALLED) { + try { + Class shadersClass = Class.forName("net.optifine.shaders.Shaders"); + shaderPackLoadedField = shadersClass.getDeclaredField("shaderPackLoaded"); + shaderPackLoadedField.setAccessible(true); + isShadowPassField = shadersClass.getDeclaredField("isShadowPass"); + isShadowPassField.setAccessible(true); + } catch (Exception e) { + FlwImpl.LOGGER.debug("Failed to access OptiFine internals", e); + } + } + + SHADER_PACK_LOADED_FIELD = shaderPackLoadedField; + IS_SHADOW_PASS_FIELD = isShadowPassField; + } + + static { + if (IS_INSTALLED) { + FlwImpl.LOGGER.debug("Detected OptiFine"); + } + } + + private OptifineCompat() { + } + + public static boolean isShaderPackInUse() { + if (SHADER_PACK_LOADED_FIELD == null) { + return false; + } + + try { + return SHADER_PACK_LOADED_FIELD.getBoolean(null); + } catch (IllegalAccessException e) { + return false; + } + } + + public static boolean isRenderingShadowPass() { + if (IS_SHADOW_PASS_FIELD == null) { + return false; + } + + try { + return IS_SHADOW_PASS_FIELD.getBoolean(null); + } catch (IllegalAccessException e) { + return false; + } + } +} diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/compat/SodiumCompat.java b/common/src/main/java/dev/engine_room/flywheel/impl/compat/SodiumCompat.java new file mode 100644 index 000000000..76fa1d499 --- /dev/null +++ b/common/src/main/java/dev/engine_room/flywheel/impl/compat/SodiumCompat.java @@ -0,0 +1,61 @@ +package dev.engine_room.flywheel.impl.compat; + +import org.jetbrains.annotations.Nullable; + +import dev.engine_room.flywheel.api.visualization.BlockEntityVisualizer; +import dev.engine_room.flywheel.impl.FlwImpl; +import dev.engine_room.flywheel.impl.FlwImplXplat; +import dev.engine_room.flywheel.lib.visualization.VisualizationHelper; +import net.caffeinemc.mods.sodium.api.blockentity.BlockEntityRenderHandler; +import net.caffeinemc.mods.sodium.api.blockentity.BlockEntityRenderPredicate; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; + +public final class SodiumCompat { + public static final boolean USE_0_6_COMPAT = FlwImplXplat.INSTANCE.useSodium0_6Compat(); + + static { + if (USE_0_6_COMPAT) { + FlwImpl.LOGGER.debug("Detected Sodium 0.6"); + } + } + + private SodiumCompat() { + } + + @Nullable + public static Object onSetBlockEntityVisualizer(BlockEntityType type, @Nullable BlockEntityVisualizer oldVisualizer, @Nullable BlockEntityVisualizer newVisualizer, @Nullable Object predicate) { + if (!USE_0_6_COMPAT) { + return null; + } + + if (oldVisualizer == null && newVisualizer != null) { + if (predicate != null) { + throw new IllegalArgumentException("Sodium predicate must be null when old visualizer is null"); + } + + return Internals.addPredicate(type); + } else if (oldVisualizer != null && newVisualizer == null) { + if (predicate == null) { + throw new IllegalArgumentException("Sodium predicate must not be null when old visualizer is not null"); + } + + Internals.removePredicate(type, predicate); + return null; + } + + return predicate; + } + + private static final class Internals { + static Object addPredicate(BlockEntityType type) { + BlockEntityRenderPredicate predicate = (getter, pos, be) -> VisualizationHelper.tryAddBlockEntity(be); + BlockEntityRenderHandler.instance().addRenderPredicate(type, predicate); + return predicate; + } + + static void removePredicate(BlockEntityType type, Object predicate) { + BlockEntityRenderHandler.instance().removeRenderPredicate(type, (BlockEntityRenderPredicate) predicate); + } + } +} diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/mixin/BlockEntityTypeMixin.java b/common/src/main/java/dev/engine_room/flywheel/impl/mixin/BlockEntityTypeMixin.java index 93c660d8d..a3e178fac 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/mixin/BlockEntityTypeMixin.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/mixin/BlockEntityTypeMixin.java @@ -5,6 +5,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import dev.engine_room.flywheel.api.visualization.BlockEntityVisualizer; +import dev.engine_room.flywheel.impl.compat.SodiumCompat; import dev.engine_room.flywheel.impl.extension.BlockEntityTypeExtension; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; @@ -12,8 +13,13 @@ import net.minecraft.world.level.block.entity.BlockEntityType; @Mixin(BlockEntityType.class) abstract class BlockEntityTypeMixin implements BlockEntityTypeExtension { @Unique + @Nullable private BlockEntityVisualizer flywheel$visualizer; + @Unique + @Nullable + private Object flywheel$sodiumPredicate; + @Override @Nullable public BlockEntityVisualizer flywheel$getVisualizer() { @@ -22,6 +28,10 @@ abstract class BlockEntityTypeMixin implements BlockEntit @Override public void flywheel$setVisualizer(@Nullable BlockEntityVisualizer visualizer) { - this.flywheel$visualizer = visualizer; + if (SodiumCompat.USE_0_6_COMPAT) { + flywheel$sodiumPredicate = SodiumCompat.onSetBlockEntityVisualizer((BlockEntityType) (Object) this, flywheel$visualizer, visualizer, flywheel$sodiumPredicate); + } + + flywheel$visualizer = visualizer; } } diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/mixin/EntityTypeMixin.java b/common/src/main/java/dev/engine_room/flywheel/impl/mixin/EntityTypeMixin.java index e8b605a32..91676872f 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/mixin/EntityTypeMixin.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/mixin/EntityTypeMixin.java @@ -12,6 +12,7 @@ import net.minecraft.world.entity.EntityType; @Mixin(EntityType.class) abstract class EntityTypeMixin implements EntityTypeExtension { @Unique + @Nullable private EntityVisualizer flywheel$visualizer; @Override @@ -22,6 +23,6 @@ abstract class EntityTypeMixin implements EntityTypeExtension< @Override public void flywheel$setVisualizer(@Nullable EntityVisualizer visualizer) { - this.flywheel$visualizer = visualizer; + flywheel$visualizer = visualizer; } } diff --git a/common/src/main/java/dev/engine_room/flywheel/impl/mixin/LevelRendererMixin.java b/common/src/main/java/dev/engine_room/flywheel/impl/mixin/LevelRendererMixin.java index 6fa747581..c7714b228 100644 --- a/common/src/main/java/dev/engine_room/flywheel/impl/mixin/LevelRendererMixin.java +++ b/common/src/main/java/dev/engine_room/flywheel/impl/mixin/LevelRendererMixin.java @@ -125,8 +125,8 @@ abstract class LevelRendererMixin { } @Inject(method = "renderEntity", at = @At("HEAD"), cancellable = true) - private void flywheel$decideNotToRenderEntity(Entity pEntity, double pCamX, double pCamY, double pCamZ, float pPartialTick, PoseStack pPoseStack, MultiBufferSource pBufferSource, CallbackInfo ci) { - if (VisualizationManager.supportsVisualization(pEntity.level()) && VisualizationHelper.skipVanillaRender(pEntity)) { + private void flywheel$decideNotToRenderEntity(Entity entity, double camX, double camY, double camZ, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, CallbackInfo ci) { + if (VisualizationManager.supportsVisualization(entity.level()) && VisualizationHelper.skipVanillaRender(entity)) { ci.cancel(); } } diff --git a/common/src/stubs/java/net/caffeinemc/mods/sodium/api/blockentity/BlockEntityRenderHandler.java b/common/src/stubs/java/net/caffeinemc/mods/sodium/api/blockentity/BlockEntityRenderHandler.java new file mode 100644 index 000000000..5c4a588d7 --- /dev/null +++ b/common/src/stubs/java/net/caffeinemc/mods/sodium/api/blockentity/BlockEntityRenderHandler.java @@ -0,0 +1,38 @@ +// https://github.com/CaffeineMC/sodium-fabric/blob/e7643f4544f61180ed2f0ff4952d7daa2c1feaf4/common/src/api/java/net/caffeinemc/mods/sodium/api/blockentity/BlockEntityRenderHandler.java +// PolyForm Shield License 1.0.0 + +package net.caffeinemc.mods.sodium.api.blockentity; + +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +@ApiStatus.AvailableSince("0.6.0") +public interface BlockEntityRenderHandler { + BlockEntityRenderHandler INSTANCE = null; + + static BlockEntityRenderHandler instance() { + return INSTANCE; + } + + /** + * Adds a predicate to determine if a block entity should be rendered. + * + *

Upon chunk bake, block entities of the given type will have {@code shouldRender} evaluated. + *
If all predicates returns {@code true} (and the block entity has a renderer), the block entity will be + * added to the chunk for future rendering.

+ * @param type The block entity type to associate the given predicate with. + * @param shouldRender The predicate for the block entity to evaluate. + */ + void addRenderPredicate(BlockEntityType type, BlockEntityRenderPredicate shouldRender); + + /** + * Removes a predicate added by {@code addRenderPredicate}. It must be the same object that was added. + * + * @param type The block entity type to associate the given predicate with. + * @param shouldRender The predicate to remove. + * @return If the predicate existed and was removed. + */ + boolean removeRenderPredicate(BlockEntityType type, BlockEntityRenderPredicate shouldRender); +} diff --git a/common/src/stubs/java/net/caffeinemc/mods/sodium/api/blockentity/BlockEntityRenderPredicate.java b/common/src/stubs/java/net/caffeinemc/mods/sodium/api/blockentity/BlockEntityRenderPredicate.java new file mode 100644 index 000000000..8734005f2 --- /dev/null +++ b/common/src/stubs/java/net/caffeinemc/mods/sodium/api/blockentity/BlockEntityRenderPredicate.java @@ -0,0 +1,16 @@ +// https://github.com/CaffeineMC/sodium-fabric/blob/e7643f4544f61180ed2f0ff4952d7daa2c1feaf4/common/src/api/java/net/caffeinemc/mods/sodium/api/blockentity/BlockEntityRenderPredicate.java +// PolyForm Shield License 1.0.0 + +package net.caffeinemc.mods.sodium.api.blockentity; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.entity.BlockEntity; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +@ApiStatus.AvailableSince("0.6.0") +@FunctionalInterface +public interface BlockEntityRenderPredicate { + boolean shouldRender(BlockGetter blockGetter, BlockPos blockPos, T entity); +} diff --git a/common/src/stubs/java/net/irisshaders/iris/api/v0/IrisApi.java b/common/src/stubs/java/net/irisshaders/iris/api/v0/IrisApi.java new file mode 100644 index 000000000..571667e1c --- /dev/null +++ b/common/src/stubs/java/net/irisshaders/iris/api/v0/IrisApi.java @@ -0,0 +1,114 @@ +// https://github.com/IrisShaders/Iris/blob/20be7fc1ff8a48048cb4eb787e1299782bb1caa4/src/main/java/net/irisshaders/iris/api/v0/IrisApi.java +// GNU Lesser General Public License v3.0 + +package net.irisshaders.iris.api.v0; + +/** + * The entry point to the Iris API, major version 0. This is currently the latest + * version of the API. + * + * To access the API, use {@link #getInstance()}. + */ +public interface IrisApi { + /** + * @since API v0.0 + */ + static IrisApi getInstance() { + return null; + } + + /** + * Gets the minor revision of this API. This is incremented when + * new methods are added without breaking API. Mods can check this + * if they wish to check whether given API calls are available on + * the currently installed Iris version. + * + * @return The current minor revision. Currently, revision 2. + */ + int getMinorApiRevision(); + + /** + * Checks whether a shader pack is currently in use and being used + * for rendering. If there is no shader pack enabled or a shader + * pack failed to compile and is therefore not in use, this will + * return false. + * + *

Mods that need to enable custom workarounds for shaders + * should use this method. + * + * @return Whether shaders are being used for rendering. + * @since {@link #getMinorApiRevision() API v0.0} + */ + boolean isShaderPackInUse(); + + /** + * Checks whether the shadow pass is currently being rendered. + * + *

Generally, mods won't need to call this function for much. + * Mods should be fine with things being rendered multiple times + * each frame from different camera perspectives. Often, there's + * a better approach to fixing bugs than calling this function. + * + *

Pretty much the main legitimate use for this function that + * I've seen is in a mod like Immersive Portals, where it has + * very custom culling that doesn't work when the Iris shadow + * pass is active. + * + *

Naturally, this function can only return true if + * {@link #isShaderPackInUse()} returns true. + * + * @return Whether Iris is currently rendering the shadow pass. + * @since API v0.0 + */ + boolean isRenderingShadowPass(); + + /** + * Opens the main Iris GUI screen. It's up to Iris to decide + * what this screen is, but generally this is the shader selection + * screen. + * + * This method takes and returns Objects instead of any concrete + * Minecraft screen class to avoid referencing Minecraft classes. + * Nevertheless, the passed parent must either be null, or an + * object that is a subclass of the appropriate {@code Screen} + * class for the given Minecraft version. + * + * @param parent The parent screen, an instance of the appropriate + * {@code Screen} class. + * @return A {@code Screen} class for the main Iris GUI screen. + * @since API v0.0 + */ + Object openMainIrisScreenObj(Object parent); + + /** + * Gets the language key of the main screen. Currently, this + * is "options.iris.shaderPackSelection". + * + * @return the language key, for use with {@code TranslatableText} + * / {@code TranslatableComponent} + * @since API v0.0 + */ + String getMainScreenLanguageKey(); + +// /** +// * Gets a config object that can edit the Iris configuration. +// * @since API v0.0 +// */ +// IrisApiConfig getConfig(); + +// /** +// * Gets a text vertex sink to render into. +// * @param maxQuadCount Maximum amount of quads that will be rendered with this sink +// * @param bufferProvider An IntFunction that can provide a {@code ByteBuffer} with at minimum the bytes provided by the input parameter +// * @since API 0.1 +// */ +// IrisTextVertexSink createTextVertexSink(int maxQuadCount, IntFunction bufferProvider); + + /** + * Gets the sun path rotation used by the current shader pack. + * + * @return The sun path rotation as specified by the shader pack, or 0 if no shader pack is in use. + * @since API v0.2 + */ + float getSunPathRotation(); +} diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index ff987b524..865773be5 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -10,6 +10,7 @@ plugins { val api = sourceSets.create("api") val lib = sourceSets.create("lib") val backend = sourceSets.create("backend") +val stubs = sourceSets.create("stubs") val main = sourceSets.getByName("main") transitiveSourceSets { @@ -26,7 +27,12 @@ transitiveSourceSets { rootCompile() compile(api, lib) } + sourceSet(stubs) { + rootCompile() + } sourceSet(main) { + // Don't want stubs at runtime + compile(stubs) implementation(api, lib, backend) } @@ -35,11 +41,10 @@ transitiveSourceSets { platform { commonProject = project(":common") - sources(api, lib, backend, main) - compileWithCommonSourceSets() - setupLoomMod() + compileWithCommonSourceSets(api, lib, backend, stubs, main) + setupLoomMod(api, lib, backend, main) setupLoomRuns() - setupFatJar() + setupFatJar(api, lib, backend, main) } jarSets { @@ -73,10 +78,10 @@ dependencies { modApi("net.fabricmc.fabric-api:fabric-api:${property("fabric_api_version")}") modCompileOnly("maven.modrinth:sodium:${property("sodium_version")}") - modCompileOnly("maven.modrinth:iris:${property("iris_version")}") "forApi"(project(path = ":common", configuration = "commonApiOnly")) "forLib"(project(path = ":common", configuration = "commonLib")) "forBackend"(project(path = ":common", configuration = "commonBackend")) + "forStubs"(project(path = ":common", configuration = "commonStubs")) "forMain"(project(path = ":common", configuration = "commonImpl")) } diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwImplXplatImpl.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwImplXplatImpl.java index 4bd60bd7d..d1c346906 100644 --- a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwImplXplatImpl.java +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwImplXplatImpl.java @@ -1,9 +1,17 @@ package dev.engine_room.flywheel.impl; import dev.engine_room.flywheel.api.event.ReloadLevelRendererCallback; +import dev.engine_room.flywheel.impl.compat.CompatMod; +import dev.engine_room.flywheel.impl.compat.FabricSodiumCompat; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.multiplayer.ClientLevel; public class FlwImplXplatImpl implements FlwImplXplat { + @Override + public boolean isModLoaded(String modId) { + return FabricLoader.getInstance().isModLoaded(modId); + } + @Override public void dispatchReloadLevelRendererEvent(ClientLevel level) { ReloadLevelRendererCallback.EVENT.invoker().onReloadLevelRenderer(level); @@ -18,4 +26,14 @@ public class FlwImplXplatImpl implements FlwImplXplat { public FlwConfig getConfig() { return FabricFlwConfig.INSTANCE; } + + @Override + public boolean useSodium0_6Compat() { + return FabricSodiumCompat.USE_0_6_COMPAT; + } + + @Override + public boolean useIrisCompat() { + return CompatMod.IRIS.isLoaded; + } } diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwLibXplatImpl.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwLibXplatImpl.java index 897a7edd7..13b13505a 100644 --- a/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwLibXplatImpl.java +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/FlwLibXplatImpl.java @@ -1,6 +1,5 @@ package dev.engine_room.flywheel.impl; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnknownNullability; import dev.engine_room.flywheel.lib.internal.FlwLibXplat; @@ -10,9 +9,6 @@ import dev.engine_room.flywheel.lib.model.baked.FabricBakedModelBuilder; import dev.engine_room.flywheel.lib.model.baked.FabricBlockModelBuilder; import dev.engine_room.flywheel.lib.model.baked.FabricMultiBlockModelBuilder; import dev.engine_room.flywheel.lib.model.baked.MultiBlockModelBuilder; -import dev.engine_room.flywheel.lib.util.ShadersModHandler; -import net.fabricmc.loader.api.FabricLoader; -import net.irisshaders.iris.api.v0.IrisApi; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.resources.model.BakedModel; @@ -48,27 +44,4 @@ public class FlwLibXplatImpl implements FlwLibXplat { public MultiBlockModelBuilder createMultiBlockModelBuilder(BlockAndTintGetter level, Iterable positions) { return new FabricMultiBlockModelBuilder(level, positions); } - - @Override - @Nullable - public ShadersModHandler.InternalHandler createIrisHandler() { - if (!FabricLoader.getInstance() - .isModLoaded("iris")) { - return null; - } - - return new ShadersModHandler.InternalHandler() { - @Override - public boolean isShaderPackInUse() { - return IrisApi.getInstance() - .isShaderPackInUse(); - } - - @Override - public boolean isRenderingShadowPass() { - return IrisApi.getInstance() - .isRenderingShadowPass(); - } - }; - } } diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/compat/FabricSodiumCompat.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/compat/FabricSodiumCompat.java new file mode 100644 index 000000000..4a6ee1da4 --- /dev/null +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/compat/FabricSodiumCompat.java @@ -0,0 +1,48 @@ +package dev.engine_room.flywheel.impl.compat; + +import java.util.Optional; + +import dev.engine_room.flywheel.impl.FlwImpl; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; +import net.fabricmc.loader.api.Version; +import net.fabricmc.loader.api.VersionParsingException; +import net.fabricmc.loader.api.metadata.version.VersionPredicate; + +public final class FabricSodiumCompat { + public static final boolean USE_0_5_COMPAT; + public static final boolean USE_0_6_COMPAT; + + static { + boolean use0_5Compat = false; + boolean use0_6Compat = false; + + Optional optionalModContainer = FabricLoader.getInstance().getModContainer(CompatMod.SODIUM.id); + + if (optionalModContainer.isPresent()) { + ModContainer modContainer = optionalModContainer.get(); + Version sodiumVersion = modContainer.getMetadata().getVersion(); + + try { + VersionPredicate predicate0_5 = VersionPredicate.parse("~0.5.0"); + VersionPredicate predicate0_6 = VersionPredicate.parse(">=0.6.0-beta.2"); + use0_5Compat = predicate0_5.test(sodiumVersion); + use0_6Compat = predicate0_6.test(sodiumVersion); + } catch (VersionParsingException e) { + FlwImpl.LOGGER.debug("Failed to parse Sodium version predicates", e); + } + } + + USE_0_5_COMPAT = use0_5Compat; + USE_0_6_COMPAT = use0_6Compat; + } + + static { + if (USE_0_5_COMPAT) { + FlwImpl.LOGGER.debug("Detected Sodium 0.5"); + } + } + + private FabricSodiumCompat() { + } +} diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/ChunkBuilderMeshingTaskMixin.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/ChunkBuilderMeshingTaskMixin.java index 64e4b31e2..18b7772de 100644 --- a/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/ChunkBuilderMeshingTaskMixin.java +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/ChunkBuilderMeshingTaskMixin.java @@ -1,8 +1,11 @@ package dev.engine_room.flywheel.impl.mixin.sodium; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import dev.engine_room.flywheel.lib.visualization.VisualizationHelper; import me.jellysquid.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask; @@ -12,11 +15,19 @@ import net.minecraft.world.level.block.entity.BlockEntity; @Mixin(value = ChunkBuilderMeshingTask.class, remap = false) abstract class ChunkBuilderMeshingTaskMixin { - @Redirect(method = "execute", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderDispatcher;getRenderer(Lnet/minecraft/world/level/block/entity/BlockEntity;)Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderer;", remap = true)) - private BlockEntityRenderer flywheel$redirectGetRenderer(BlockEntityRenderDispatcher dispatcher, BlockEntity blockEntity) { + @WrapOperation( + method = "execute(Lme/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuildContext;Lme/jellysquid/mods/sodium/client/util/task/CancellationToken;)Lme/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuildOutput;", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderDispatcher;getRenderer(Lnet/minecraft/world/level/block/entity/BlockEntity;)Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderer;", + remap = true + ) + ) + @Nullable + private BlockEntityRenderer flywheel$wrapGetRenderer(BlockEntityRenderDispatcher instance, BlockEntity blockEntity, Operation> original) { if (VisualizationHelper.tryAddBlockEntity(blockEntity)) { return null; } - return dispatcher.getRenderer(blockEntity); + return original.call(instance, blockEntity); } } diff --git a/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/SodiumMixinPlugin.java b/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/SodiumMixinPlugin.java index 0eda36012..d96386dad 100644 --- a/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/SodiumMixinPlugin.java +++ b/fabric/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/SodiumMixinPlugin.java @@ -2,31 +2,28 @@ package dev.engine_room.flywheel.impl.mixin.sodium; import java.util.List; import java.util.Set; -import java.util.function.Supplier; +import org.jetbrains.annotations.Nullable; import org.objectweb.asm.tree.ClassNode; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; -import com.google.common.base.Suppliers; - -import net.fabricmc.loader.api.FabricLoader; +import dev.engine_room.flywheel.impl.compat.FabricSodiumCompat; public class SodiumMixinPlugin implements IMixinConfigPlugin { - private static final Supplier IS_SODIUM_LOADED = Suppliers.memoize(() -> FabricLoader.getInstance().isModLoaded("sodium")); - @Override public void onLoad(String mixinPackage) { } @Override + @Nullable public String getRefMapperConfig() { return null; } @Override public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { - return IS_SODIUM_LOADED.get(); + return FabricSodiumCompat.USE_0_5_COMPAT; } @Override @@ -34,6 +31,7 @@ public class SodiumMixinPlugin implements IMixinConfigPlugin { } @Override + @Nullable public List getMixins() { return null; } diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 7bfb5a37f..d90f772c9 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -24,14 +24,16 @@ "mixins": [ "flywheel.backend.mixins.json", "flywheel.impl.mixins.json", - "flywheel.impl.sodium.mixins.json", - "flywheel.impl.fabric.mixins.json" + "flywheel.impl.fabric.mixins.json", + "flywheel.impl.sodium.mixins.json" ], "depends": { "minecraft": "${minecraft_semver_version_range}", + "fabricloader": ">=0.15.0", "fabric-api": "${fabric_api_version_range}" }, "breaks": { - "sodium": "<0.5.0" + "sodium": ["<0.5.0", "~0.6.0- <0.6.0-beta.2"], + "embeddium": "*" } } diff --git a/forge/build.gradle.kts b/forge/build.gradle.kts index c9569e26c..d1c6b3617 100644 --- a/forge/build.gradle.kts +++ b/forge/build.gradle.kts @@ -10,6 +10,7 @@ plugins { val api = sourceSets.create("api") val lib = sourceSets.create("lib") val backend = sourceSets.create("backend") +val stubs = sourceSets.create("stubs") val main = sourceSets.getByName("main") transitiveSourceSets { @@ -26,8 +27,11 @@ transitiveSourceSets { rootCompile() compile(api, lib) } + sourceSet(stubs) { + rootCompile() + } sourceSet(main) { - compile(api, lib, backend) + compile(api, lib, backend, stubs) } createCompileConfigurations() @@ -35,11 +39,10 @@ transitiveSourceSets { platform { commonProject = project(":common") - sources(api, lib, backend, main) - compileWithCommonSourceSets() - setupLoomMod() + compileWithCommonSourceSets(api, lib, backend, stubs, main) + setupLoomMod(api, lib, backend, main) setupLoomRuns() - setupFatJar() + setupFatJar(api, lib, backend, main) } jarSets { @@ -70,7 +73,6 @@ loom { forge { mixinConfig("flywheel.backend.mixins.json") mixinConfig("flywheel.impl.mixins.json") - mixinConfig("flywheel.impl.sodium.mixins.json") } runs { @@ -85,10 +87,10 @@ dependencies { forge("net.minecraftforge:forge:${property("minecraft_version")}-${property("forge_version")}") modCompileOnly("maven.modrinth:embeddium:${property("embeddium_version")}") - modCompileOnly("maven.modrinth:oculus:${property("oculus_version")}") "forApi"(project(path = ":common", configuration = "commonApiOnly")) "forLib"(project(path = ":common", configuration = "commonLib")) "forBackend"(project(path = ":common", configuration = "commonBackend")) + "forStubs"(project(path = ":common", configuration = "commonStubs")) "forMain"(project(path = ":common", configuration = "commonImpl")) } diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/FlwImplXplatImpl.java b/forge/src/main/java/dev/engine_room/flywheel/impl/FlwImplXplatImpl.java index fa4f901e0..78e99c9e0 100644 --- a/forge/src/main/java/dev/engine_room/flywheel/impl/FlwImplXplatImpl.java +++ b/forge/src/main/java/dev/engine_room/flywheel/impl/FlwImplXplatImpl.java @@ -1,10 +1,17 @@ package dev.engine_room.flywheel.impl; import dev.engine_room.flywheel.api.event.ReloadLevelRendererEvent; +import dev.engine_room.flywheel.impl.compat.CompatMod; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.loading.LoadingModList; public class FlwImplXplatImpl implements FlwImplXplat { + @Override + public boolean isModLoaded(String modId) { + return LoadingModList.get().getModFileById(modId) != null; + } + @Override public void dispatchReloadLevelRendererEvent(ClientLevel level) { MinecraftForge.EVENT_BUS.post(new ReloadLevelRendererEvent(level)); @@ -19,4 +26,14 @@ public class FlwImplXplatImpl implements FlwImplXplat { public FlwConfig getConfig() { return ForgeFlwConfig.INSTANCE; } + + @Override + public boolean useSodium0_6Compat() { + return CompatMod.SODIUM.isLoaded && !CompatMod.EMBEDDIUM.isLoaded; + } + + @Override + public boolean useIrisCompat() { + return CompatMod.IRIS.isLoaded || CompatMod.OCULUS.isLoaded; + } } diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/FlwLibXplatImpl.java b/forge/src/main/java/dev/engine_room/flywheel/impl/FlwLibXplatImpl.java index eeed7907a..363e51382 100644 --- a/forge/src/main/java/dev/engine_room/flywheel/impl/FlwLibXplatImpl.java +++ b/forge/src/main/java/dev/engine_room/flywheel/impl/FlwLibXplatImpl.java @@ -2,7 +2,6 @@ package dev.engine_room.flywheel.impl; import java.lang.reflect.Field; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnknownNullability; import dev.engine_room.flywheel.lib.internal.FlwLibXplat; @@ -12,8 +11,6 @@ import dev.engine_room.flywheel.lib.model.baked.ForgeBakedModelBuilder; import dev.engine_room.flywheel.lib.model.baked.ForgeBlockModelBuilder; import dev.engine_room.flywheel.lib.model.baked.ForgeMultiBlockModelBuilder; import dev.engine_room.flywheel.lib.model.baked.MultiBlockModelBuilder; -import dev.engine_room.flywheel.lib.util.ShadersModHandler; -import net.irisshaders.iris.api.v0.IrisApi; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.renderer.block.ModelBlockRenderer; @@ -23,7 +20,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.util.ObfuscationReflectionHelper; public class FlwLibXplatImpl implements FlwLibXplat { @@ -64,27 +60,4 @@ public class FlwLibXplatImpl implements FlwLibXplat { public MultiBlockModelBuilder createMultiBlockModelBuilder(BlockAndTintGetter level, Iterable positions) { return new ForgeMultiBlockModelBuilder(level, positions); } - - @Override - @Nullable - public ShadersModHandler.InternalHandler createIrisHandler() { - if (!ModList.get() - .isLoaded("oculus")) { - return null; - } - - return new ShadersModHandler.InternalHandler() { - @Override - public boolean isShaderPackInUse() { - return IrisApi.getInstance() - .isShaderPackInUse(); - } - - @Override - public boolean isRenderingShadowPass() { - return IrisApi.getInstance() - .isRenderingShadowPass(); - } - }; - } } diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java b/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java index 205db0497..cb8874dab 100644 --- a/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java +++ b/forge/src/main/java/dev/engine_room/flywheel/impl/FlywheelForge.java @@ -8,6 +8,7 @@ import dev.engine_room.flywheel.api.event.EndClientResourceReloadEvent; import dev.engine_room.flywheel.api.event.ReloadLevelRendererEvent; import dev.engine_room.flywheel.backend.compile.FlwProgramsReloader; import dev.engine_room.flywheel.backend.engine.uniform.Uniforms; +import dev.engine_room.flywheel.impl.compat.EmbeddiumCompat; import dev.engine_room.flywheel.impl.visualization.VisualizationEventHandler; import dev.engine_room.flywheel.lib.model.baked.PartialModelEventHandler; import dev.engine_room.flywheel.lib.util.LevelAttached; @@ -63,6 +64,8 @@ public final class FlywheelForge { CrashReportCallables.registerCrashCallable("Flywheel Backend", BackendManagerImpl::getBackendString); FlwImpl.init(); + + EmbeddiumCompat.init(); } private static void registerImplEventListeners(IEventBus forgeEventBus, IEventBus modEventBus) { diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/compat/EmbeddiumCompat.java b/forge/src/main/java/dev/engine_room/flywheel/impl/compat/EmbeddiumCompat.java new file mode 100644 index 000000000..cd1e5e138 --- /dev/null +++ b/forge/src/main/java/dev/engine_room/flywheel/impl/compat/EmbeddiumCompat.java @@ -0,0 +1,33 @@ +package dev.engine_room.flywheel.impl.compat; + +import org.embeddedt.embeddium.api.ChunkDataBuiltEvent; + +import dev.engine_room.flywheel.impl.FlwImpl; +import dev.engine_room.flywheel.lib.visualization.VisualizationHelper; + +public final class EmbeddiumCompat { + public static final boolean ACTIVE = CompatMod.EMBEDDIUM.isLoaded; + + static { + if (ACTIVE) { + FlwImpl.LOGGER.debug("Detected Embeddium"); + } + } + + private EmbeddiumCompat() { + } + + public static void init() { + if (ACTIVE) { + Internals.init(); + } + } + + private static final class Internals { + static void init() { + ChunkDataBuiltEvent.BUS.addListener(event -> { + event.getDataBuilder().removeBlockEntitiesIf(VisualizationHelper::tryAddBlockEntity); + }); + } + } +} diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/ChunkBuilderMeshingTaskMixin.java b/forge/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/ChunkBuilderMeshingTaskMixin.java deleted file mode 100644 index 64e4b31e2..000000000 --- a/forge/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/ChunkBuilderMeshingTaskMixin.java +++ /dev/null @@ -1,22 +0,0 @@ -package dev.engine_room.flywheel.impl.mixin.sodium; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -import dev.engine_room.flywheel.lib.visualization.VisualizationHelper; -import me.jellysquid.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; -import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; -import net.minecraft.world.level.block.entity.BlockEntity; - -@Mixin(value = ChunkBuilderMeshingTask.class, remap = false) -abstract class ChunkBuilderMeshingTaskMixin { - @Redirect(method = "execute", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderDispatcher;getRenderer(Lnet/minecraft/world/level/block/entity/BlockEntity;)Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderer;", remap = true)) - private BlockEntityRenderer flywheel$redirectGetRenderer(BlockEntityRenderDispatcher dispatcher, BlockEntity blockEntity) { - if (VisualizationHelper.tryAddBlockEntity(blockEntity)) { - return null; - } - return dispatcher.getRenderer(blockEntity); - } -} diff --git a/forge/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/SodiumMixinPlugin.java b/forge/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/SodiumMixinPlugin.java deleted file mode 100644 index 1048d91fd..000000000 --- a/forge/src/main/java/dev/engine_room/flywheel/impl/mixin/sodium/SodiumMixinPlugin.java +++ /dev/null @@ -1,48 +0,0 @@ -package dev.engine_room.flywheel.impl.mixin.sodium; - -import java.util.List; -import java.util.Set; -import java.util.function.Supplier; - -import org.objectweb.asm.tree.ClassNode; -import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; -import org.spongepowered.asm.mixin.extensibility.IMixinInfo; - -import com.google.common.base.Suppliers; - -import net.minecraftforge.fml.loading.LoadingModList; - -public class SodiumMixinPlugin implements IMixinConfigPlugin { - private static final Supplier IS_SODIUM_LOADED = Suppliers.memoize(() -> LoadingModList.get().getModFileById("rubidium") != null); - - @Override - public void onLoad(String mixinPackage) { - } - - @Override - public String getRefMapperConfig() { - return null; - } - - @Override - public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { - return IS_SODIUM_LOADED.get(); - } - - @Override - public void acceptTargets(Set myTargets, Set otherTargets) { - } - - @Override - public List getMixins() { - return null; - } - - @Override - public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - } - - @Override - public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - } -} diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml index 2dc330582..be991ac88 100644 --- a/forge/src/main/resources/META-INF/mods.toml +++ b/forge/src/main/resources/META-INF/mods.toml @@ -26,10 +26,21 @@ mandatory = true versionRange = "${forge_version_range}" side = "CLIENT" +# Simulates a breaks/incompatible dependency [[dependencies.${mod_id}]] -# This replicates a "breaks" dependency. -# There's a mixin crash with Rubidium <0.7.0. modId = "rubidium" mandatory = false -versionRange = "[0.7.0,)" +versionRange = "[0.0-INCOMPATIBLE]" +side = "CLIENT" + +[[dependencies.${mod_id}]] +modId = "embeddium" +mandatory = false +versionRange = "[0.3.25,)" +side = "CLIENT" + +[[dependencies.${mod_id}]] +modId = "sodium" +mandatory = false +versionRange = "[0.6.0-beta.2,)" side = "CLIENT" diff --git a/forge/src/main/resources/flywheel.impl.sodium.mixins.json b/forge/src/main/resources/flywheel.impl.sodium.mixins.json deleted file mode 100644 index 7820cb594..000000000 --- a/forge/src/main/resources/flywheel.impl.sodium.mixins.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "required": true, - "minVersion": "0.8", - "package": "dev.engine_room.flywheel.impl.mixin.sodium", - "compatibilityLevel": "JAVA_17", - "refmap": "flywheel.refmap.json", - "plugin": "dev.engine_room.flywheel.impl.mixin.sodium.SodiumMixinPlugin", - "client": [ - "ChunkBuilderMeshingTaskMixin" - ], - "injectors": { - "defaultRequire": 1 - } -} diff --git a/gradle.properties b/gradle.properties index d8c4ff106..57a9985e6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,9 +7,9 @@ mod_name = Flywheel mod_version = 1.0.0-beta mod_description = An overhauled entity and block entity rendering API. mod_license = MIT -mod_sources = https://github.com/Jozufozu/Flywheel -mod_issues = https://github.com/Jozufozu/Flywheel/issues -mod_homepage = https://github.com/Jozufozu/Flywheel +mod_sources = https://github.com/Engine-Room/Flywheel +mod_issues = https://github.com/Engine-Room/Flywheel/issues +mod_homepage = https://github.com/Engine-Room/Flywheel # Mod dependency declarations minecraft_semver_version_range = >=1.20.1 <1.20.2 @@ -21,6 +21,7 @@ forge_version_range = [47.0.0,) java_version = 17 arch_loom_version = 1.7.412 cursegradle_version = 1.4.0 +parchment_minecraft_version = 1.20.1 parchment_version = 2023.09.03 # Minecraft build dependency versions @@ -30,10 +31,8 @@ fabric_loader_version = 0.15.9 fabric_api_version = 0.92.1+1.20.1 # Build dependency mod versions -sodium_version = mc1.20.1-0.5.8 -iris_version = 1.6.17+1.20.1 -embeddium_version = 0.3.9+mc1.20.1 -oculus_version = 1.20.1-1.6.15a +sodium_version = mc1.20.1-0.5.11 +embeddium_version = 0.3.25+mc1.20.1 # Publication info group = dev.engine_room.flywheel diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136f..a4b76b953 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6f7a6eb33..df97d72b8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a426..f5feea6d6 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/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/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 25da30dbd..9d21a2183 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @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 ##########################################################################