Backport changes from 1.21.1 (#265)

* Backport changes from 1.21.1

* fix

* Fix building

* fix compile error

* fix

* fix build for real

* address reviews

* Fix sodium compat

* address requested changes

* mark rubidium as incompatible

* add missed call

* Should have worn steel toe boots

- Add "stub" sourceset to each subproject
- Directly pass vararg sourcesets to methods in PlatformExtension to
  avoid automatically shipping jars with the api stubs
- We may have to include stubs in setupLoomMod, but I don't think so
- A lot of this can be stripped back out if we don't need stub sources
  for the forge/fabric subprojects

* Guarded stubs

- Add Sodium 0.6 and Iris API stubs to stubs source set and remove Gradle dependencies on local Sodium jar, Iris, and Oculus
- Ensure usage of APIs that may not exist at runtime is in private classes and access is always guarded
- Change ShadersModHandler
  - Rename to ShadersModHelper
  - Convert methods to check for Iris' and Optifine's presence into static final fields
  - Move implementation to impl source set in form of IrisCompat and OptifineCompat classes
- Rename CompatMods to CompatMod and add public field to access mod ID
- Set BlockEntityType's Sodium predicate to null after it is removed
- Update repository links
- Remove local libs repository

---------

Co-authored-by: Jozufozu <jozsefaug@gmail.com>
Co-authored-by: PepperCode1 <44146161+PepperCode1@users.noreply.github.com>
This commit is contained in:
IThundxr 2024-10-18 23:29:43 -04:00 committed by GitHub
parent 734506b010
commit 3d2fdb7e83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 659 additions and 335 deletions

View File

@ -23,16 +23,11 @@ open class PlatformExtension(val project: Project) {
var apiArtifactId: String = "flywheel-${project.name}-api-${project.property("artifact_minecraft_version")}" var apiArtifactId: String = "flywheel-${project.name}-api-${project.property("artifact_minecraft_version")}"
private val sources = mutableSetOf<SourceSet>()
private val commonSourceSets: SourceSetContainer by lazy { commonProject.the<SourceSetContainer>() } private val commonSourceSets: SourceSetContainer by lazy { commonProject.the<SourceSetContainer>() }
fun sources(vararg sourceSets: SourceSet) { fun setupLoomMod(vararg sourceSets: SourceSet) {
this.sources.addAll(sourceSets)
}
fun setupLoomMod() {
project.the<LoomGradleExtensionAPI>().mods.maybeCreate("main").apply { project.the<LoomGradleExtensionAPI>().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 { project.tasks.apply {
withType<JavaCompile>().configureEach { withType<JavaCompile>().configureEach {
JarTaskSet.excludeDuplicatePackageInfos(this) JarTaskSet.excludeDuplicatePackageInfos(this)
} }
sources.forEach { sourceSets.forEach {
val commonSourceSet = commonSourceSets.named(it.name).get() val commonSourceSet = commonSourceSets.named(it.name).get()
named<JavaCompile>(it.compileJavaTaskName).configure { named<JavaCompile>(it.compileJavaTaskName).configure {
@ -80,10 +75,10 @@ open class PlatformExtension(val project: Project) {
} }
} }
fun setupFatJar() { fun setupFatJar(vararg sourceSets: SourceSet) {
project.tasks.apply { project.tasks.apply {
val extraSourceSets = sources.filter { it.name != "main" }.toList() val extraSourceSets = sourceSets.filter { it.name != "main" }.toList()
val commonSources = sources.map { commonSourceSets.named(it.name).get() } val commonSources = sourceSets.map { commonSourceSets.named(it.name).get() }
named<Jar>("jar").configure { named<Jar>("jar").configure {
extraSourceSets.forEach { from(it.output) } extraSourceSets.forEach { from(it.output) }

View File

@ -96,6 +96,7 @@ class SubprojectPlugin: Plugin<Project> {
private fun setupDependencies(project: Project) { private fun setupDependencies(project: Project) {
project.dependencies.apply { project.dependencies.apply {
val minecraft_version: String by project val minecraft_version: String by project
val parchment_minecraft_version: String by project
val parchment_version: String by project val parchment_version: String by project
val loom = project.the<LoomGradleExtensionAPI>() val loom = project.the<LoomGradleExtensionAPI>()
@ -103,7 +104,7 @@ class SubprojectPlugin: Plugin<Project> {
add("mappings", loom.layered { add("mappings", loom.layered {
officialMojangMappings() 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") add("api", "com.google.code.findbugs:jsr305:3.0.2")

View File

@ -25,7 +25,7 @@ open class TransitiveSourceSetsExtension(private val project: Project) {
fun createCompileConfigurations() { fun createCompileConfigurations() {
val configs = transitives.mapValues { (sourceSet, _) -> val configs = transitives.mapValues { (sourceSet, _) ->
project.configurations.create("for${sourceSet.name.capitalize()}") { project.configurations.create("for${sourceSet.name.replaceFirstChar { it.uppercase() }}") {
isCanBeConsumed = true isCanBeConsumed = true
isCanBeResolved = false isCanBeResolved = false
} }
@ -43,7 +43,7 @@ open class TransitiveSourceSetsExtension(private val project: Project) {
fun createRuntimeConfigurations() { fun createRuntimeConfigurations() {
val configs = transitives.mapValues { (sourceSet, _) -> val configs = transitives.mapValues { (sourceSet, _) ->
project.configurations.create("run${sourceSet.name.capitalize()}") { project.configurations.create("run${sourceSet.name.replaceFirstChar { it.uppercase() }}") {
isCanBeConsumed = true isCanBeConsumed = true
isCanBeResolved = false isCanBeResolved = false
} }

View File

@ -9,6 +9,7 @@ plugins {
val api = sourceSets.create("api") val api = sourceSets.create("api")
val lib = sourceSets.create("lib") val lib = sourceSets.create("lib")
val backend = sourceSets.create("backend") val backend = sourceSets.create("backend")
val stubs = sourceSets.create("stubs")
val main = sourceSets.getByName("main") val main = sourceSets.getByName("main")
transitiveSourceSets { transitiveSourceSets {
@ -25,8 +26,11 @@ transitiveSourceSets {
rootCompile() rootCompile()
compile(api, lib) compile(api, lib)
} }
sourceSet(stubs) {
rootCompile()
}
sourceSet(main) { sourceSet(main) {
compile(api, lib, backend) compile(api, lib, backend, stubs)
} }
sourceSet(sourceSets.getByName("test")) { sourceSet(sourceSets.getByName("test")) {
implementation(api, lib, backend) implementation(api, lib, backend)
@ -42,6 +46,7 @@ jarSets {
outgoing("commonApiOnly", api) outgoing("commonApiOnly", api)
outgoing("commonLib", lib) outgoing("commonLib", lib)
outgoing("commonBackend", backend) outgoing("commonBackend", backend)
outgoing("commonStubs", stubs)
outgoing("commonImpl", main) outgoing("commonImpl", main)
// For publishing. // For publishing.

View File

@ -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.engine.instancing.InstancedDrawManager;
import dev.engine_room.flywheel.backend.gl.GlCompat; import dev.engine_room.flywheel.backend.gl.GlCompat;
import dev.engine_room.flywheel.lib.backend.SimpleBackend; 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 { public final class Backends {
/** /**
@ -18,7 +18,7 @@ public final class Backends {
public static final Backend INSTANCING = SimpleBackend.builder() public static final Backend INSTANCING = SimpleBackend.builder()
.engineFactory(level -> new EngineImpl(level, new InstancedDrawManager(InstancingPrograms.get()), 256)) .engineFactory(level -> new EngineImpl(level, new InstancedDrawManager(InstancingPrograms.get()), 256))
.priority(500) .priority(500)
.supported(() -> GlCompat.SUPPORTS_INSTANCING && InstancingPrograms.allLoaded() && !ShadersModHandler.isShaderPackInUse()) .supported(() -> GlCompat.SUPPORTS_INSTANCING && InstancingPrograms.allLoaded() && !ShadersModHelper.isShaderPackInUse())
.register(Flywheel.rl("instancing")); .register(Flywheel.rl("instancing"));
/** /**
@ -27,7 +27,7 @@ public final class Backends {
public static final Backend INDIRECT = SimpleBackend.builder() public static final Backend INDIRECT = SimpleBackend.builder()
.engineFactory(level -> new EngineImpl(level, new IndirectDrawManager(IndirectPrograms.get()), 256)) .engineFactory(level -> new EngineImpl(level, new IndirectDrawManager(IndirectPrograms.get()), 256))
.priority(1000) .priority(1000)
.supported(() -> GlCompat.SUPPORTS_INDIRECT && IndirectPrograms.allLoaded() && !ShadersModHandler.isShaderPackInUse()) .supported(() -> GlCompat.SUPPORTS_INDIRECT && IndirectPrograms.allLoaded() && !ShadersModHelper.isShaderPackInUse())
.register(Flywheel.rl("indirect")); .register(Flywheel.rl("indirect"));
private Backends() { private Backends() {

View File

@ -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); void compileModelPart(ModelPart part, PoseStack.Pose pose, VertexConsumer consumer, int light, int overlay, float red, float green, float blue, float alpha);
Deque<PoseStack.Pose> getPoseStack(PoseStack stack); Deque<PoseStack.Pose> getPoseStack(PoseStack stack);
boolean isIrisLoaded();
boolean isOptifineInstalled();
boolean isShaderPackInUse();
boolean isRenderingShadowPass();
} }

View File

@ -1,13 +1,11 @@
package dev.engine_room.flywheel.lib.internal; package dev.engine_room.flywheel.lib.internal;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability; import org.jetbrains.annotations.UnknownNullability;
import dev.engine_room.flywheel.api.internal.DependencyInjection; 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.BakedModelBuilder;
import dev.engine_room.flywheel.lib.model.baked.BlockModelBuilder; import dev.engine_room.flywheel.lib.model.baked.BlockModelBuilder;
import dev.engine_room.flywheel.lib.model.baked.MultiBlockModelBuilder; 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.renderer.block.BlockRenderDispatcher;
import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelManager; import net.minecraft.client.resources.model.ModelManager;
@ -29,7 +27,4 @@ public interface FlwLibXplat {
BlockModelBuilder createBlockModelBuilder(BlockState state); BlockModelBuilder createBlockModelBuilder(BlockState state);
MultiBlockModelBuilder createMultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions); MultiBlockModelBuilder createMultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions);
@Nullable
ShadersModHandler.InternalHandler createIrisHandler();
} }

View File

@ -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;
}
}
}
}

View File

@ -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();
}
}

View File

@ -6,7 +6,6 @@ import org.slf4j.LoggerFactory;
import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.Flywheel;
import dev.engine_room.flywheel.backend.FlwBackend; import dev.engine_room.flywheel.backend.FlwBackend;
import dev.engine_room.flywheel.impl.registry.IdRegistryImpl; import dev.engine_room.flywheel.impl.registry.IdRegistryImpl;
import dev.engine_room.flywheel.lib.util.ShadersModHandler;
import dev.engine_room.flywheel.vanilla.VanillaVisuals; import dev.engine_room.flywheel.vanilla.VanillaVisuals;
public final class FlwImpl { public final class FlwImpl {
@ -21,7 +20,6 @@ public final class FlwImpl {
BackendManagerImpl.init(); BackendManagerImpl.init();
// lib // lib
ShadersModHandler.init();
// backend // backend
FlwBackend.init(FlwConfig.INSTANCE.backendConfig()); FlwBackend.init(FlwConfig.INSTANCE.backendConfig());

View File

@ -6,9 +6,15 @@ import net.minecraft.client.multiplayer.ClientLevel;
public interface FlwImplXplat { public interface FlwImplXplat {
FlwImplXplat INSTANCE = DependencyInjection.load(FlwImplXplat.class, "dev.engine_room.flywheel.impl.FlwImplXplatImpl"); FlwImplXplat INSTANCE = DependencyInjection.load(FlwImplXplat.class, "dev.engine_room.flywheel.impl.FlwImplXplatImpl");
boolean isModLoaded(String modId);
void dispatchReloadLevelRendererEvent(ClientLevel level); void dispatchReloadLevelRendererEvent(ClientLevel level);
String getVersionStr(); String getVersionStr();
FlwConfig getConfig(); FlwConfig getConfig();
boolean useSodium0_6Compat();
boolean useIrisCompat();
} }

View File

@ -8,6 +8,8 @@ import org.slf4j.Logger;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer; 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.extension.PoseStackExtension;
import dev.engine_room.flywheel.impl.mixin.ModelPartAccessor; import dev.engine_room.flywheel.impl.mixin.ModelPartAccessor;
import dev.engine_room.flywheel.impl.mixin.PoseStackAccessor; import dev.engine_room.flywheel.impl.mixin.PoseStackAccessor;
@ -40,4 +42,36 @@ public class FlwLibLinkImpl implements FlwLibLink {
public Deque<PoseStack.Pose> getPoseStack(PoseStack stack) { public Deque<PoseStack.Pose> getPoseStack(PoseStack stack) {
return ((PoseStackAccessor) stack).flywheel$getPoseStack(); 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;
}
}
} }

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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 <T extends BlockEntity> Object onSetBlockEntityVisualizer(BlockEntityType<T> type, @Nullable BlockEntityVisualizer<? super T> oldVisualizer, @Nullable BlockEntityVisualizer<? super T> 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 <T extends BlockEntity> Object addPredicate(BlockEntityType<T> type) {
BlockEntityRenderPredicate<T> predicate = (getter, pos, be) -> VisualizationHelper.tryAddBlockEntity(be);
BlockEntityRenderHandler.instance().addRenderPredicate(type, predicate);
return predicate;
}
static <T extends BlockEntity> void removePredicate(BlockEntityType<T> type, Object predicate) {
BlockEntityRenderHandler.instance().removeRenderPredicate(type, (BlockEntityRenderPredicate<T>) predicate);
}
}
}

View File

@ -5,6 +5,7 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import dev.engine_room.flywheel.api.visualization.BlockEntityVisualizer; 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 dev.engine_room.flywheel.impl.extension.BlockEntityTypeExtension;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
@ -12,8 +13,13 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
@Mixin(BlockEntityType.class) @Mixin(BlockEntityType.class)
abstract class BlockEntityTypeMixin<T extends BlockEntity> implements BlockEntityTypeExtension<T> { abstract class BlockEntityTypeMixin<T extends BlockEntity> implements BlockEntityTypeExtension<T> {
@Unique @Unique
@Nullable
private BlockEntityVisualizer<? super T> flywheel$visualizer; private BlockEntityVisualizer<? super T> flywheel$visualizer;
@Unique
@Nullable
private Object flywheel$sodiumPredicate;
@Override @Override
@Nullable @Nullable
public BlockEntityVisualizer<? super T> flywheel$getVisualizer() { public BlockEntityVisualizer<? super T> flywheel$getVisualizer() {
@ -22,6 +28,10 @@ abstract class BlockEntityTypeMixin<T extends BlockEntity> implements BlockEntit
@Override @Override
public void flywheel$setVisualizer(@Nullable BlockEntityVisualizer<? super T> visualizer) { public void flywheel$setVisualizer(@Nullable BlockEntityVisualizer<? super T> visualizer) {
this.flywheel$visualizer = visualizer; if (SodiumCompat.USE_0_6_COMPAT) {
flywheel$sodiumPredicate = SodiumCompat.onSetBlockEntityVisualizer((BlockEntityType<T>) (Object) this, flywheel$visualizer, visualizer, flywheel$sodiumPredicate);
}
flywheel$visualizer = visualizer;
} }
} }

View File

@ -12,6 +12,7 @@ import net.minecraft.world.entity.EntityType;
@Mixin(EntityType.class) @Mixin(EntityType.class)
abstract class EntityTypeMixin<T extends Entity> implements EntityTypeExtension<T> { abstract class EntityTypeMixin<T extends Entity> implements EntityTypeExtension<T> {
@Unique @Unique
@Nullable
private EntityVisualizer<? super T> flywheel$visualizer; private EntityVisualizer<? super T> flywheel$visualizer;
@Override @Override
@ -22,6 +23,6 @@ abstract class EntityTypeMixin<T extends Entity> implements EntityTypeExtension<
@Override @Override
public void flywheel$setVisualizer(@Nullable EntityVisualizer<? super T> visualizer) { public void flywheel$setVisualizer(@Nullable EntityVisualizer<? super T> visualizer) {
this.flywheel$visualizer = visualizer; flywheel$visualizer = visualizer;
} }
} }

View File

@ -125,8 +125,8 @@ abstract class LevelRendererMixin {
} }
@Inject(method = "renderEntity", at = @At("HEAD"), cancellable = true) @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) { private void flywheel$decideNotToRenderEntity(Entity entity, double camX, double camY, double camZ, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, CallbackInfo ci) {
if (VisualizationManager.supportsVisualization(pEntity.level()) && VisualizationHelper.skipVanillaRender(pEntity)) { if (VisualizationManager.supportsVisualization(entity.level()) && VisualizationHelper.skipVanillaRender(entity)) {
ci.cancel(); ci.cancel();
} }
} }

View File

@ -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.
*
* <p>Upon chunk bake, block entities of the given type will have {@code shouldRender} evaluated.
* <br>If <b>all predicates</b> returns {@code true} (and the block entity has a renderer), the block entity will be
* added to the chunk for future rendering.</p>
* @param type The block entity type to associate the given predicate with.
* @param shouldRender The predicate for the block entity to evaluate.
*/
<T extends BlockEntity> void addRenderPredicate(BlockEntityType<T> type, BlockEntityRenderPredicate<T> shouldRender);
/**
* Removes a predicate added by {@code addRenderPredicate}. <b>It must be the same object that was added.</b>
*
* @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.
*/
<T extends BlockEntity> boolean removeRenderPredicate(BlockEntityType<T> type, BlockEntityRenderPredicate<T> shouldRender);
}

View File

@ -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<T extends BlockEntity> {
boolean shouldRender(BlockGetter blockGetter, BlockPos blockPos, T entity);
}

View File

@ -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.
*
* <p>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.
*
* <p>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.
*
* <p>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.
*
* <p>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<ByteBuffer> 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();
}

View File

@ -10,6 +10,7 @@ plugins {
val api = sourceSets.create("api") val api = sourceSets.create("api")
val lib = sourceSets.create("lib") val lib = sourceSets.create("lib")
val backend = sourceSets.create("backend") val backend = sourceSets.create("backend")
val stubs = sourceSets.create("stubs")
val main = sourceSets.getByName("main") val main = sourceSets.getByName("main")
transitiveSourceSets { transitiveSourceSets {
@ -26,7 +27,12 @@ transitiveSourceSets {
rootCompile() rootCompile()
compile(api, lib) compile(api, lib)
} }
sourceSet(stubs) {
rootCompile()
}
sourceSet(main) { sourceSet(main) {
// Don't want stubs at runtime
compile(stubs)
implementation(api, lib, backend) implementation(api, lib, backend)
} }
@ -35,11 +41,10 @@ transitiveSourceSets {
platform { platform {
commonProject = project(":common") commonProject = project(":common")
sources(api, lib, backend, main) compileWithCommonSourceSets(api, lib, backend, stubs, main)
compileWithCommonSourceSets() setupLoomMod(api, lib, backend, main)
setupLoomMod()
setupLoomRuns() setupLoomRuns()
setupFatJar() setupFatJar(api, lib, backend, main)
} }
jarSets { jarSets {
@ -73,10 +78,10 @@ dependencies {
modApi("net.fabricmc.fabric-api:fabric-api:${property("fabric_api_version")}") modApi("net.fabricmc.fabric-api:fabric-api:${property("fabric_api_version")}")
modCompileOnly("maven.modrinth:sodium:${property("sodium_version")}") modCompileOnly("maven.modrinth:sodium:${property("sodium_version")}")
modCompileOnly("maven.modrinth:iris:${property("iris_version")}")
"forApi"(project(path = ":common", configuration = "commonApiOnly")) "forApi"(project(path = ":common", configuration = "commonApiOnly"))
"forLib"(project(path = ":common", configuration = "commonLib")) "forLib"(project(path = ":common", configuration = "commonLib"))
"forBackend"(project(path = ":common", configuration = "commonBackend")) "forBackend"(project(path = ":common", configuration = "commonBackend"))
"forStubs"(project(path = ":common", configuration = "commonStubs"))
"forMain"(project(path = ":common", configuration = "commonImpl")) "forMain"(project(path = ":common", configuration = "commonImpl"))
} }

View File

@ -1,9 +1,17 @@
package dev.engine_room.flywheel.impl; package dev.engine_room.flywheel.impl;
import dev.engine_room.flywheel.api.event.ReloadLevelRendererCallback; 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; import net.minecraft.client.multiplayer.ClientLevel;
public class FlwImplXplatImpl implements FlwImplXplat { public class FlwImplXplatImpl implements FlwImplXplat {
@Override
public boolean isModLoaded(String modId) {
return FabricLoader.getInstance().isModLoaded(modId);
}
@Override @Override
public void dispatchReloadLevelRendererEvent(ClientLevel level) { public void dispatchReloadLevelRendererEvent(ClientLevel level) {
ReloadLevelRendererCallback.EVENT.invoker().onReloadLevelRenderer(level); ReloadLevelRendererCallback.EVENT.invoker().onReloadLevelRenderer(level);
@ -18,4 +26,14 @@ public class FlwImplXplatImpl implements FlwImplXplat {
public FlwConfig getConfig() { public FlwConfig getConfig() {
return FabricFlwConfig.INSTANCE; return FabricFlwConfig.INSTANCE;
} }
@Override
public boolean useSodium0_6Compat() {
return FabricSodiumCompat.USE_0_6_COMPAT;
}
@Override
public boolean useIrisCompat() {
return CompatMod.IRIS.isLoaded;
}
} }

View File

@ -1,6 +1,5 @@
package dev.engine_room.flywheel.impl; package dev.engine_room.flywheel.impl;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability; import org.jetbrains.annotations.UnknownNullability;
import dev.engine_room.flywheel.lib.internal.FlwLibXplat; 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.FabricBlockModelBuilder;
import dev.engine_room.flywheel.lib.model.baked.FabricMultiBlockModelBuilder; import dev.engine_room.flywheel.lib.model.baked.FabricMultiBlockModelBuilder;
import dev.engine_room.flywheel.lib.model.baked.MultiBlockModelBuilder; 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.Minecraft;
import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.renderer.block.BlockRenderDispatcher;
import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.BakedModel;
@ -48,27 +44,4 @@ public class FlwLibXplatImpl implements FlwLibXplat {
public MultiBlockModelBuilder createMultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions) { public MultiBlockModelBuilder createMultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions) {
return new FabricMultiBlockModelBuilder(level, 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();
}
};
}
} }

View File

@ -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<ModContainer> 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() {
}
}

View File

@ -1,8 +1,11 @@
package dev.engine_room.flywheel.impl.mixin.sodium; package dev.engine_room.flywheel.impl.mixin.sodium;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; 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 dev.engine_room.flywheel.lib.visualization.VisualizationHelper;
import me.jellysquid.mods.sodium.client.render.chunk.compile.tasks.ChunkBuilderMeshingTask; 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) @Mixin(value = ChunkBuilderMeshingTask.class, remap = false)
abstract class ChunkBuilderMeshingTaskMixin { 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)) @WrapOperation(
private BlockEntityRenderer<?> flywheel$redirectGetRenderer(BlockEntityRenderDispatcher dispatcher, BlockEntity blockEntity) { 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<BlockEntityRenderer<BlockEntity>> original) {
if (VisualizationHelper.tryAddBlockEntity(blockEntity)) { if (VisualizationHelper.tryAddBlockEntity(blockEntity)) {
return null; return null;
} }
return dispatcher.getRenderer(blockEntity); return original.call(instance, blockEntity);
} }
} }

View File

@ -2,31 +2,28 @@ package dev.engine_room.flywheel.impl.mixin.sodium;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo; import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import com.google.common.base.Suppliers; import dev.engine_room.flywheel.impl.compat.FabricSodiumCompat;
import net.fabricmc.loader.api.FabricLoader;
public class SodiumMixinPlugin implements IMixinConfigPlugin { public class SodiumMixinPlugin implements IMixinConfigPlugin {
private static final Supplier<Boolean> IS_SODIUM_LOADED = Suppliers.memoize(() -> FabricLoader.getInstance().isModLoaded("sodium"));
@Override @Override
public void onLoad(String mixinPackage) { public void onLoad(String mixinPackage) {
} }
@Override @Override
@Nullable
public String getRefMapperConfig() { public String getRefMapperConfig() {
return null; return null;
} }
@Override @Override
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
return IS_SODIUM_LOADED.get(); return FabricSodiumCompat.USE_0_5_COMPAT;
} }
@Override @Override
@ -34,6 +31,7 @@ public class SodiumMixinPlugin implements IMixinConfigPlugin {
} }
@Override @Override
@Nullable
public List<String> getMixins() { public List<String> getMixins() {
return null; return null;
} }

View File

@ -24,14 +24,16 @@
"mixins": [ "mixins": [
"flywheel.backend.mixins.json", "flywheel.backend.mixins.json",
"flywheel.impl.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": { "depends": {
"minecraft": "${minecraft_semver_version_range}", "minecraft": "${minecraft_semver_version_range}",
"fabricloader": ">=0.15.0",
"fabric-api": "${fabric_api_version_range}" "fabric-api": "${fabric_api_version_range}"
}, },
"breaks": { "breaks": {
"sodium": "<0.5.0" "sodium": ["<0.5.0", "~0.6.0- <0.6.0-beta.2"],
"embeddium": "*"
} }
} }

View File

@ -10,6 +10,7 @@ plugins {
val api = sourceSets.create("api") val api = sourceSets.create("api")
val lib = sourceSets.create("lib") val lib = sourceSets.create("lib")
val backend = sourceSets.create("backend") val backend = sourceSets.create("backend")
val stubs = sourceSets.create("stubs")
val main = sourceSets.getByName("main") val main = sourceSets.getByName("main")
transitiveSourceSets { transitiveSourceSets {
@ -26,8 +27,11 @@ transitiveSourceSets {
rootCompile() rootCompile()
compile(api, lib) compile(api, lib)
} }
sourceSet(stubs) {
rootCompile()
}
sourceSet(main) { sourceSet(main) {
compile(api, lib, backend) compile(api, lib, backend, stubs)
} }
createCompileConfigurations() createCompileConfigurations()
@ -35,11 +39,10 @@ transitiveSourceSets {
platform { platform {
commonProject = project(":common") commonProject = project(":common")
sources(api, lib, backend, main) compileWithCommonSourceSets(api, lib, backend, stubs, main)
compileWithCommonSourceSets() setupLoomMod(api, lib, backend, main)
setupLoomMod()
setupLoomRuns() setupLoomRuns()
setupFatJar() setupFatJar(api, lib, backend, main)
} }
jarSets { jarSets {
@ -70,7 +73,6 @@ loom {
forge { forge {
mixinConfig("flywheel.backend.mixins.json") mixinConfig("flywheel.backend.mixins.json")
mixinConfig("flywheel.impl.mixins.json") mixinConfig("flywheel.impl.mixins.json")
mixinConfig("flywheel.impl.sodium.mixins.json")
} }
runs { runs {
@ -85,10 +87,10 @@ dependencies {
forge("net.minecraftforge:forge:${property("minecraft_version")}-${property("forge_version")}") forge("net.minecraftforge:forge:${property("minecraft_version")}-${property("forge_version")}")
modCompileOnly("maven.modrinth:embeddium:${property("embeddium_version")}") modCompileOnly("maven.modrinth:embeddium:${property("embeddium_version")}")
modCompileOnly("maven.modrinth:oculus:${property("oculus_version")}")
"forApi"(project(path = ":common", configuration = "commonApiOnly")) "forApi"(project(path = ":common", configuration = "commonApiOnly"))
"forLib"(project(path = ":common", configuration = "commonLib")) "forLib"(project(path = ":common", configuration = "commonLib"))
"forBackend"(project(path = ":common", configuration = "commonBackend")) "forBackend"(project(path = ":common", configuration = "commonBackend"))
"forStubs"(project(path = ":common", configuration = "commonStubs"))
"forMain"(project(path = ":common", configuration = "commonImpl")) "forMain"(project(path = ":common", configuration = "commonImpl"))
} }

View File

@ -1,10 +1,17 @@
package dev.engine_room.flywheel.impl; package dev.engine_room.flywheel.impl;
import dev.engine_room.flywheel.api.event.ReloadLevelRendererEvent; import dev.engine_room.flywheel.api.event.ReloadLevelRendererEvent;
import dev.engine_room.flywheel.impl.compat.CompatMod;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.loading.LoadingModList;
public class FlwImplXplatImpl implements FlwImplXplat { public class FlwImplXplatImpl implements FlwImplXplat {
@Override
public boolean isModLoaded(String modId) {
return LoadingModList.get().getModFileById(modId) != null;
}
@Override @Override
public void dispatchReloadLevelRendererEvent(ClientLevel level) { public void dispatchReloadLevelRendererEvent(ClientLevel level) {
MinecraftForge.EVENT_BUS.post(new ReloadLevelRendererEvent(level)); MinecraftForge.EVENT_BUS.post(new ReloadLevelRendererEvent(level));
@ -19,4 +26,14 @@ public class FlwImplXplatImpl implements FlwImplXplat {
public FlwConfig getConfig() { public FlwConfig getConfig() {
return ForgeFlwConfig.INSTANCE; 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;
}
} }

View File

@ -2,7 +2,6 @@ package dev.engine_room.flywheel.impl;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability; import org.jetbrains.annotations.UnknownNullability;
import dev.engine_room.flywheel.lib.internal.FlwLibXplat; 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.ForgeBlockModelBuilder;
import dev.engine_room.flywheel.lib.model.baked.ForgeMultiBlockModelBuilder; import dev.engine_room.flywheel.lib.model.baked.ForgeMultiBlockModelBuilder;
import dev.engine_room.flywheel.lib.model.baked.MultiBlockModelBuilder; 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.Minecraft;
import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.renderer.block.BlockRenderDispatcher;
import net.minecraft.client.renderer.block.ModelBlockRenderer; import net.minecraft.client.renderer.block.ModelBlockRenderer;
@ -23,7 +20,6 @@ import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.util.ObfuscationReflectionHelper; import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
public class FlwLibXplatImpl implements FlwLibXplat { public class FlwLibXplatImpl implements FlwLibXplat {
@ -64,27 +60,4 @@ public class FlwLibXplatImpl implements FlwLibXplat {
public MultiBlockModelBuilder createMultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions) { public MultiBlockModelBuilder createMultiBlockModelBuilder(BlockAndTintGetter level, Iterable<BlockPos> positions) {
return new ForgeMultiBlockModelBuilder(level, 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();
}
};
}
} }

View File

@ -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.api.event.ReloadLevelRendererEvent;
import dev.engine_room.flywheel.backend.compile.FlwProgramsReloader; import dev.engine_room.flywheel.backend.compile.FlwProgramsReloader;
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms; 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.impl.visualization.VisualizationEventHandler;
import dev.engine_room.flywheel.lib.model.baked.PartialModelEventHandler; import dev.engine_room.flywheel.lib.model.baked.PartialModelEventHandler;
import dev.engine_room.flywheel.lib.util.LevelAttached; import dev.engine_room.flywheel.lib.util.LevelAttached;
@ -63,6 +64,8 @@ public final class FlywheelForge {
CrashReportCallables.registerCrashCallable("Flywheel Backend", BackendManagerImpl::getBackendString); CrashReportCallables.registerCrashCallable("Flywheel Backend", BackendManagerImpl::getBackendString);
FlwImpl.init(); FlwImpl.init();
EmbeddiumCompat.init();
} }
private static void registerImplEventListeners(IEventBus forgeEventBus, IEventBus modEventBus) { private static void registerImplEventListeners(IEventBus forgeEventBus, IEventBus modEventBus) {

View File

@ -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);
});
}
}
}

View File

@ -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);
}
}

View File

@ -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<Boolean> 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<String> myTargets, Set<String> otherTargets) {
}
@Override
public List<String> 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) {
}
}

View File

@ -26,10 +26,21 @@ mandatory = true
versionRange = "${forge_version_range}" versionRange = "${forge_version_range}"
side = "CLIENT" side = "CLIENT"
# Simulates a breaks/incompatible dependency
[[dependencies.${mod_id}]] [[dependencies.${mod_id}]]
# This replicates a "breaks" dependency.
# There's a mixin crash with Rubidium <0.7.0.
modId = "rubidium" modId = "rubidium"
mandatory = false 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" side = "CLIENT"

View File

@ -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
}
}

View File

@ -7,9 +7,9 @@ mod_name = Flywheel
mod_version = 1.0.0-beta mod_version = 1.0.0-beta
mod_description = An overhauled entity and block entity rendering API. mod_description = An overhauled entity and block entity rendering API.
mod_license = MIT mod_license = MIT
mod_sources = https://github.com/Jozufozu/Flywheel mod_sources = https://github.com/Engine-Room/Flywheel
mod_issues = https://github.com/Jozufozu/Flywheel/issues mod_issues = https://github.com/Engine-Room/Flywheel/issues
mod_homepage = https://github.com/Jozufozu/Flywheel mod_homepage = https://github.com/Engine-Room/Flywheel
# Mod dependency declarations # Mod dependency declarations
minecraft_semver_version_range = >=1.20.1 <1.20.2 minecraft_semver_version_range = >=1.20.1 <1.20.2
@ -21,6 +21,7 @@ forge_version_range = [47.0.0,)
java_version = 17 java_version = 17
arch_loom_version = 1.7.412 arch_loom_version = 1.7.412
cursegradle_version = 1.4.0 cursegradle_version = 1.4.0
parchment_minecraft_version = 1.20.1
parchment_version = 2023.09.03 parchment_version = 2023.09.03
# Minecraft build dependency versions # Minecraft build dependency versions
@ -30,10 +31,8 @@ fabric_loader_version = 0.15.9
fabric_api_version = 0.92.1+1.20.1 fabric_api_version = 0.92.1+1.20.1
# Build dependency mod versions # Build dependency mod versions
sodium_version = mc1.20.1-0.5.8 sodium_version = mc1.20.1-0.5.11
iris_version = 1.6.17+1.20.1 embeddium_version = 0.3.25+mc1.20.1
embeddium_version = 0.3.9+mc1.20.1
oculus_version = 1.20.1-1.6.15a
# Publication info # Publication info
group = dev.engine_room.flywheel group = dev.engine_room.flywheel

Binary file not shown.

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

7
gradlew vendored
View File

@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (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. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # 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. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum

2
gradlew.bat vendored
View File

@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################