diff --git a/build.gradle b/build.gradle index d92e484..767ae81 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '1.7.+' + id 'fabric-loom' version '1.10.1' id 'io.github.juuxel.loom-quiltflower' version '1.8.0' id 'maven-publish' } diff --git a/gradle.properties b/gradle.properties index 848c5b6..e990cf2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,12 +2,12 @@ org.gradle.jvmargs=-Xmx1G # Fabric Properties # https://modmuss50.me/fabric.html -minecraft_version=1.21 -yarn_mappings=1.21+build.7 -loader_version=0.15.11 -fabric_version=0.100.4+1.21 +minecraft_version=1.21.8 +yarn_mappings=1.21.8+build.1 +loader_version=0.17.2 +fabric_version=0.133.4+1.21.8 # Mod Properties -mod_version=1.12 +mod_version=1.13 maven_group=suso archives_base_name=shader-reload \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0d8ab51..82d65ca 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1 +1 @@ -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip diff --git a/src/main/java/suso/shaderreload/CustomShaderLoader.java b/src/main/java/suso/shaderreload/CustomShaderLoader.java new file mode 100644 index 0000000..4feb9a3 --- /dev/null +++ b/src/main/java/suso/shaderreload/CustomShaderLoader.java @@ -0,0 +1,45 @@ +package suso.shaderreload; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.*; +import net.minecraft.client.texture.TextureManager; +import net.minecraft.resource.DefaultResourcePack; +import net.minecraft.util.Identifier; + +import java.io.FileNotFoundException; +import java.util.Set; + +public class CustomShaderLoader extends ShaderLoader { + private final DefaultResourcePack defaultPack = MinecraftClient.getInstance().getDefaultResourcePack(); + + public CustomShaderLoader(TextureManager textureManager) { + super(textureManager, CustomShaderLoader::onShaderError); + } + + @Override + public PostEffectProcessor loadPostEffect(Identifier id, Set availableExternalTargets) { + PostEffectProcessor result = super.loadPostEffect(id, availableExternalTargets); + if(result != null) return result; + + try { + return loadDefaultPostEffect(id, availableExternalTargets); + } catch (Exception e) { + ShaderReload.printShaderException(e, true); + } + + return null; + } + + static void onShaderError(Exception e) { + ShaderReload.tripError(); + ShaderReload.printShaderException(e, false); + } + + private ShaderProgram loadDefaultProgram(ShaderProgram key) throws FileNotFoundException { + return null; + } + + private PostEffectProcessor loadDefaultPostEffect(Identifier id, Set availableExternalTargets) { + return null; + } +} \ No newline at end of file diff --git a/src/main/java/suso/shaderreload/ShaderReload.java b/src/main/java/suso/shaderreload/ShaderReload.java index 3631d62..9927a2b 100644 --- a/src/main/java/suso/shaderreload/ShaderReload.java +++ b/src/main/java/suso/shaderreload/ShaderReload.java @@ -1,15 +1,9 @@ package suso.shaderreload; -import com.google.gson.JsonSyntaxException; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gl.Framebuffer; -import net.minecraft.client.gl.PostEffectProcessor; -import net.minecraft.client.gl.ShaderProgram; -import net.minecraft.client.render.VertexFormat; -import net.minecraft.client.texture.TextureManager; import net.minecraft.resource.*; import net.minecraft.text.Text; import net.minecraft.util.*; @@ -18,21 +12,17 @@ import org.lwjgl.glfw.GLFW; import suso.shaderreload.mixin.KeyboardInvoker; -import java.io.IOException; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; -import static net.minecraft.resource.ResourceType.CLIENT_RESOURCES; - @Environment(EnvType.CLIENT) public class ShaderReload implements ClientModInitializer { public static final int GLFW_KEY = GLFW.GLFW_KEY_R; public static final Logger LOGGER = LogManager.getLogger("Shader Reload"); - private static final StopException STOP = new StopException(); private static boolean reloading = false; - private static boolean stopReloading = false; + private static boolean expectError = false; private static ResourceReloader shaderLoader; @Override @@ -41,95 +31,61 @@ public void onInitializeClient() { } public static void reloadShaders() { - if (reloading) return; - var client = MinecraftClient.getInstance(); + if(reloading) { + return; + } + reloading = true; - stopReloading = false; - SimpleResourceReload.start(client.getResourceManager(), List.of(shaderLoader, client.worldRenderer), - Util.getMainWorkerExecutor(), client, CompletableFuture.completedFuture(Unit.INSTANCE), false) - .whenComplete() - .whenComplete((result, throwable) -> { - reloading = false; - if (throwable == null) { - ((KeyboardInvoker) client.keyboard).invokeDebugLog("debug.reload_shaders.message"); - return; - } - if (throwable instanceof CompletionException ex && ex.getCause() != null) { - throwable = ex.getCause(); - } - if (!(throwable instanceof StopException)) { - ((KeyboardInvoker) client.keyboard).invokeDebugError("debug.reload_shaders.unknown_error"); - throwable.printStackTrace(); - } - }); - } + expectError = false; + MinecraftClient client = MinecraftClient.getInstance(); - // Print a shader exception in chat. - private static void printShaderException(Exception exception, boolean builtin) { - var client = MinecraftClient.getInstance(); - var throwable = (Throwable) exception; - while (!(throwable instanceof InvalidHierarchicalFileException)) { - var cause = throwable.getCause(); - if (cause != null) throwable = cause; - else { - var translationKey = "debug.reload_shaders.unknown_error" + (builtin ? ".builtin" : ""); - ((KeyboardInvoker) client.keyboard).invokeDebugError(translationKey); - throwable.printStackTrace(); + SimpleResourceReload.start(client.getResourceManager(), List.of(shaderLoader, client.worldRenderer), Util.getMainWorkerExecutor(), client, CompletableFuture.completedFuture(Unit.INSTANCE), false).whenComplete().whenComplete((result, throwable) -> { + reloading = false; + if(throwable == null) { + ((KeyboardInvoker) client.keyboard).debugLog(Text.translatable("debug.reload_shaders.message")); + expectError = false; return; } - } - var translationKey = "debug.reload_shaders.error" + (builtin ? ".builtin" : ""); - ((KeyboardInvoker) client.keyboard).invokeDebugError(translationKey); - client.inGameHud.getChatHud().addMessage(Text.literal(throwable.getMessage()).formatted(Formatting.GRAY)); - } - // Try loading a core shader; if it fails, stop shader reloading or try loading a built-in core shader. - public static ShaderProgram onLoadShaders$new(ResourceFactory factory, String name, VertexFormat format) throws IOException { - try { - return new ShaderProgram(factory, name, format); - } catch (IOException e) { - printShaderException(e, false); - if (reloading) throw STOP; - } - try { - var defaultPack = MinecraftClient.getInstance().getDefaultResourcePack(); - return new ShaderProgram(defaultPack.getFactory(), name, format); - } catch (IOException e) { - printShaderException(e, true); - throw e; - } + if(throwable instanceof CompletionException ex && ex.getCause() != null) { + throwable = ex.getCause(); + } + + if(!expectError) { + ((KeyboardInvoker) client.keyboard).debugLog(Text.translatable("debug.reload_shaders.unknown_error")); + LOGGER.error(throwable); + } + + expectError = false; + }); } - // Try loading a shader effect; if it fails, request stopping and try loading a built-in shader effect. - @SuppressWarnings("resource") - public static PostEffectProcessor onLoadShader$new(TextureManager textureManager, ResourceFactory resourceFactory, - Framebuffer framebuffer, Identifier location) throws IOException { - try { - return new PostEffectProcessor(textureManager, resourceFactory, framebuffer, location); - } catch (IOException | JsonSyntaxException e) { - printShaderException(e, false); - stopReloading = true; - } - try { - var defaultPack = MinecraftClient.getInstance().getDefaultResourcePack(); - resourceFactory = new LifecycledResourceManagerImpl(CLIENT_RESOURCES, List.of(defaultPack)); - return new PostEffectProcessor(textureManager, resourceFactory, framebuffer, location); - } catch (IOException | JsonSyntaxException e) { - printShaderException(e, true); - throw e; + // Print a shader exception in chat. + public static void printShaderException(Exception exception, boolean builtin) { + MinecraftClient client = MinecraftClient.getInstance(); + Throwable throwable = exception; + while (!(throwable instanceof InvalidHierarchicalFileException)) { + Throwable cause = throwable.getCause(); + if(cause != null) { + throwable = cause; + } else { + String translationKey = "debug.reload_shaders.unknown_error" + (builtin ? ".builtin" : ""); + ((KeyboardInvoker) client.keyboard).debugLog(Text.translatable(translationKey)); + LOGGER.error(throwable); + return; + } } - } - // Stop shader reloading if it's requested. - public static void onLoadShader$end() { - if (reloading && stopReloading) throw STOP; + String translationKey = "debug.reload_shaders.error" + (builtin ? ".builtin" : ""); + ((KeyboardInvoker) client.keyboard).debugLog(Text.translatable(translationKey)); + client.inGameHud.getChatHud().addMessage(Text.literal(throwable.getMessage()).formatted(Formatting.GRAY)); } public static void setShaderLoader(ResourceReloader value) { shaderLoader = value; } - private static class StopException extends RuntimeException { - private StopException() {} + public static void tripError() { + expectError = true; } } diff --git a/src/main/java/suso/shaderreload/mixin/GameRendererMixin.java b/src/main/java/suso/shaderreload/mixin/GameRendererMixin.java deleted file mode 100644 index 0084f0e..0000000 --- a/src/main/java/suso/shaderreload/mixin/GameRendererMixin.java +++ /dev/null @@ -1,57 +0,0 @@ -package suso.shaderreload.mixin; - -import net.minecraft.client.gl.Framebuffer; -import net.minecraft.client.gl.PostEffectProcessor; -import net.minecraft.client.gl.ShaderProgram; -import net.minecraft.client.render.GameRenderer; -import net.minecraft.client.render.VertexFormat; -import net.minecraft.client.texture.TextureManager; -import net.minecraft.resource.ResourceFactory; -import net.minecraft.resource.ResourceReloader; -import net.minecraft.util.Identifier; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import suso.shaderreload.ShaderReload; - -import java.io.IOException; - -@Mixin(GameRenderer.class) -public abstract class GameRendererMixin { - @Shadow private boolean postProcessorEnabled; - - @Redirect(method = "loadPrograms", at = @At(value = "NEW", target = "Lnet/minecraft/client/gl/ShaderProgram;")) - ShaderProgram onLoadPrograms$new(ResourceFactory factory, String name, VertexFormat format) throws IOException { - return ShaderReload.onLoadShaders$new(factory, name, format); - } - - @Redirect(method = "loadPostProcessor(Lnet/minecraft/util/Identifier;)V", at = @At(value = "NEW", - target = "Lnet/minecraft/client/gl/PostEffectProcessor;")) - PostEffectProcessor onLoadPostProcessor$new(TextureManager textureManager, ResourceFactory resourceFactory, - Framebuffer framebuffer, Identifier location) throws IOException { - return ShaderReload.onLoadShader$new(textureManager, resourceFactory, framebuffer, location); - } - - @Inject(method = "loadPostProcessor(Lnet/minecraft/util/Identifier;)V", at = @At(value = "INVOKE", - target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", - remap = false), cancellable = true) - void onLoadPostProcessor$error(Identifier id, CallbackInfo ci) { - postProcessorEnabled = false; - ShaderReload.onLoadShader$end(); - ci.cancel(); - } - - @Inject(method = "loadPostProcessor(Lnet/minecraft/util/Identifier;)V", at = @At("TAIL")) - void onLoadPostProcessor$success(Identifier id, CallbackInfo ci) { - ShaderReload.onLoadShader$end(); - } - - @Inject(method = "createProgramReloader", at = @At("RETURN")) - void onCreateProgramReloader(CallbackInfoReturnable cir) { - ShaderReload.setShaderLoader(cir.getReturnValue()); - } -} diff --git a/src/main/java/suso/shaderreload/mixin/GlProgramManagerMixin.java b/src/main/java/suso/shaderreload/mixin/GlProgramManagerMixin.java deleted file mode 100644 index 36839e5..0000000 --- a/src/main/java/suso/shaderreload/mixin/GlProgramManagerMixin.java +++ /dev/null @@ -1,20 +0,0 @@ -package suso.shaderreload.mixin; - -import com.mojang.blaze3d.platform.GlStateManager; -import net.minecraft.client.gl.GlProgramManager; -import net.minecraft.client.gl.ShaderProgramSetupView; -import net.minecraft.util.InvalidHierarchicalFileException; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(GlProgramManager.class) -public class GlProgramManagerMixin { - @Inject(method = "linkProgram", at = @At(value = "INVOKE", - target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", - remap = false)) - private static void onLinkProgram$error(ShaderProgramSetupView shader, CallbackInfo ci) throws InvalidHierarchicalFileException { - throw new InvalidHierarchicalFileException(GlStateManager.glGetProgramInfoLog(shader.getGlRef(), 32768)); - } -} diff --git a/src/main/java/suso/shaderreload/mixin/KeyboardInvoker.java b/src/main/java/suso/shaderreload/mixin/KeyboardInvoker.java index fe607f4..077ac74 100644 --- a/src/main/java/suso/shaderreload/mixin/KeyboardInvoker.java +++ b/src/main/java/suso/shaderreload/mixin/KeyboardInvoker.java @@ -1,14 +1,14 @@ package suso.shaderreload.mixin; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; import net.minecraft.client.Keyboard; +import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; -@Mixin(Keyboard.class) +@Mixin(Keyboard.class) @Environment(EnvType.CLIENT) public interface KeyboardInvoker { - @Invoker - void invokeDebugLog(String key, Object ... args); - - @Invoker - void invokeDebugError(String key, Object ... args); + @Invoker("debugLog") + void debugLog(Text text); } diff --git a/src/main/java/suso/shaderreload/mixin/KeyboardMixin.java b/src/main/java/suso/shaderreload/mixin/KeyboardMixin.java index 3c1410b..6eed9c5 100644 --- a/src/main/java/suso/shaderreload/mixin/KeyboardMixin.java +++ b/src/main/java/suso/shaderreload/mixin/KeyboardMixin.java @@ -1,5 +1,7 @@ package suso.shaderreload.mixin; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; import net.minecraft.client.Keyboard; import net.minecraft.client.MinecraftClient; import net.minecraft.client.util.InputUtil; @@ -14,39 +16,28 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import suso.shaderreload.ShaderReload; -@Mixin(Keyboard.class) +@Mixin(Keyboard.class) @Environment(EnvType.CLIENT) public abstract class KeyboardMixin { @Shadow @Final private MinecraftClient client; - @Inject(method = "processF3", at = @At(value = "INVOKE", - target = "Lnet/minecraft/client/gui/hud/ChatHud;addMessage(Lnet/minecraft/text/Text;)V", - ordinal = 10, shift = At.Shift.AFTER)) - private void onProcessF3$addHelp(int key, CallbackInfoReturnable cir) { - client.inGameHud.getChatHud().addMessage(Text.translatable("debug.reload_shaders.help")); - } - @Inject(method = "processF3", at = @At("RETURN"), cancellable = true) void onProcessF3(int key, CallbackInfoReturnable cir) { if (key == ShaderReload.GLFW_KEY) { - ShaderReload.reloadShaders(); cir.setReturnValue(true); + } else if (key == GLFW.GLFW_KEY_Q) { + client.inGameHud.getChatHud().addMessage(Text.translatable("debug.reload_shaders.help")); } } - @Inject(method = "onKey", at = @At(value = "INVOKE", - target = "Lnet/minecraft/client/gui/screen/Screen;wrapScreenError(Ljava/lang/Runnable;Ljava/lang/String;Ljava/lang/String;)V"), - cancellable = true) + @Inject(method = "onKey", at = @At(value = "HEAD")) void onOnKey(long window, int key, int scancode, int action, int modifiers, CallbackInfo ci) { if (!InputUtil.isKeyPressed(window, GLFW.GLFW_KEY_F3) || key != ShaderReload.GLFW_KEY) return; if (action != 0) { ShaderReload.reloadShaders(); } - ci.cancel(); } - @Inject(method = "onChar", at = @At(value = "INVOKE", - target = "Lnet/minecraft/client/gui/screen/Screen;wrapScreenError(Ljava/lang/Runnable;Ljava/lang/String;Ljava/lang/String;)V", - ordinal = 0), cancellable = true) + @Inject(method = "onChar", at = @At(value = "HEAD"), cancellable = true) void onOnChar(long window, int codePoint, int modifiers, CallbackInfo ci) { if (InputUtil.isKeyPressed(window, GLFW.GLFW_KEY_F3) && InputUtil.isKeyPressed(window, ShaderReload.GLFW_KEY)) { ci.cancel(); diff --git a/src/main/java/suso/shaderreload/mixin/MinecraftClientMixin.java b/src/main/java/suso/shaderreload/mixin/MinecraftClientMixin.java new file mode 100644 index 0000000..6e07173 --- /dev/null +++ b/src/main/java/suso/shaderreload/mixin/MinecraftClientMixin.java @@ -0,0 +1,24 @@ +package suso.shaderreload.mixin; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.ShaderLoader; +import net.minecraft.client.texture.TextureManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import suso.shaderreload.CustomShaderLoader; +import suso.shaderreload.ShaderReload; + +import java.util.function.Consumer; + +@Mixin(MinecraftClient.class) @Environment(EnvType.CLIENT) +public class MinecraftClientMixin { + @Redirect(method = "", at = @At(value = "NEW", target = "Lnet/minecraft/client/gl/ShaderLoader;")) + ShaderLoader replaceShaderLoader(TextureManager textureManager, Consumer onError) { + ShaderLoader loader = new CustomShaderLoader(textureManager); + ShaderReload.setShaderLoader(loader); + return loader; + } +} diff --git a/src/main/java/suso/shaderreload/mixin/WorldRendererMixin.java b/src/main/java/suso/shaderreload/mixin/WorldRendererMixin.java deleted file mode 100644 index 4166c93..0000000 --- a/src/main/java/suso/shaderreload/mixin/WorldRendererMixin.java +++ /dev/null @@ -1,70 +0,0 @@ -package suso.shaderreload.mixin; - -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gl.Framebuffer; -import net.minecraft.client.gl.PostEffectProcessor; -import net.minecraft.client.option.GraphicsMode; -import net.minecraft.client.render.WorldRenderer; -import net.minecraft.client.texture.TextureManager; -import net.minecraft.resource.ResourceFactory; -import net.minecraft.resource.ResourceManager; -import net.minecraft.util.Identifier; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import suso.shaderreload.ShaderReload; - -import java.io.IOException; - -@Mixin(WorldRenderer.class) -public abstract class WorldRendererMixin { - @Shadow @Final private MinecraftClient client; - @Shadow private PostEffectProcessor entityOutlinePostProcessor; - @Shadow private Framebuffer entityOutlinesFramebuffer; - - @Redirect(method = "loadTransparencyPostProcessor", at = @At(value = "NEW", target = "Lnet/minecraft/client/gl/PostEffectProcessor;")) - PostEffectProcessor onLoadTransparencyPostProcessor$new(TextureManager textureManager, ResourceFactory resourceFactory, - Framebuffer framebuffer, Identifier location) throws IOException { - return ShaderReload.onLoadShader$new(textureManager, resourceFactory, framebuffer, location); - } - - @Inject(method = "loadTransparencyPostProcessor", at = @At(value = "INVOKE", - target = "Lnet/minecraft/client/render/WorldRenderer$ProgramInitException;(Ljava/lang/String;Ljava/lang/Throwable;)V"), - cancellable = true) - void onLoadTransparencyShader$error(CallbackInfo ci) { - client.options.getGraphicsMode().setValue(GraphicsMode.FANCY); - client.options.write(); - ShaderReload.onLoadShader$end(); - ci.cancel(); - } - - @Inject(method = "loadTransparencyPostProcessor", at = @At("TAIL")) - void onLoadTransparencyPostProcessor$success(CallbackInfo ci) { - ShaderReload.onLoadShader$end(); - } - - @Redirect(method = "loadEntityOutlinePostProcessor", at = @At(value = "NEW", target = "Lnet/minecraft/client/gl/PostEffectProcessor;")) - PostEffectProcessor onLoadEntityOutlinePostProcessor$new(TextureManager textureManager, ResourceFactory resourceFactory, - Framebuffer framebuffer, Identifier location) throws IOException { - return ShaderReload.onLoadShader$new(textureManager, resourceFactory, framebuffer, location); - } - - @Inject(method = "loadEntityOutlinePostProcessor", at = @At(value = "INVOKE", - target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", - remap = false), cancellable = true) - void onLoadEntityOutlinePostProcessor$error(CallbackInfo ci) { - entityOutlinePostProcessor = null; - entityOutlinesFramebuffer = null; - ShaderReload.onLoadShader$end(); - ci.cancel(); - } - - @Inject(method = "loadEntityOutlinePostProcessor", at = @At("TAIL")) - void onLoadEntityOutlinePostProcessor$success(CallbackInfo ci) { - ShaderReload.onLoadShader$end(); - } -} diff --git a/src/main/resources/shaderreload.mixins.json b/src/main/resources/shaderreload.mixins.json index 2428554..3818f5a 100644 --- a/src/main/resources/shaderreload.mixins.json +++ b/src/main/resources/shaderreload.mixins.json @@ -4,11 +4,9 @@ "package": "suso.shaderreload.mixin", "compatibilityLevel": "JAVA_21", "client": [ - "GameRendererMixin", "KeyboardInvoker", "KeyboardMixin", - "GlProgramManagerMixin", - "WorldRendererMixin" + "MinecraftClientMixin" ], "injectors": { "defaultRequire": 1