diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 312e811..122ce28 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -22,10 +22,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 21 + - name: Set up JDK 25 uses: actions/setup-java@v4 with: - java-version: '21' + java-version: '25' distribution: 'temurin' # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies. @@ -55,10 +55,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 21 + - name: Set up JDK 25 uses: actions/setup-java@v4 with: - java-version: '21' + java-version: '25' distribution: 'temurin' # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies. diff --git a/README.MD b/README.MD index 7de239b..ecc765d 100644 --- a/README.MD +++ b/README.MD @@ -1,19 +1,38 @@ # ShaderAPI -A simple mod that allows you to download and apply shader packs from the internet. +A client-side Fabric mod that lets servers offer shader packs for download and apply them with Iris. -## Usage +## Requirements + +This branch targets **Minecraft 26.3** and **Java 25**. Install matching builds of: + +- [Fabric Loader](https://fabricmc.net/use/installer/) 0.20.1 or newer +- [Fabric API](https://modrinth.com/mod/fabric-api) for Minecraft 26.3 +- [Fabric Language Kotlin](https://modrinth.com/mod/fabric-language-kotlin) 1.14.2+kotlin.2.4.21 or newer +- [Iris Shaders](https://modrinth.com/mod/iris/version/vTN4NRGW) 1.11.7 for Fabric 26.3, together with its required [Sodium](https://modrinth.com/mod/sodium/version/bAZQdGpg) 0.9.2 for 26.3 -To use this mod, you need to have the latest [Iris Shaders](https://github.com/IrisShaders/Iris) build for your Minecraft version installed. +ShaderAPI disables its client integration when Iris is missing. + +## Usage -Once you have Iris installed, you can download and apply shader packs by sending a request to the server. -(The server will need to have an implementation of the `ShaderAPI` Serverside. An example for PaperMC can be found [here](https://github.com/Liams-Services/ShaderAPI-Paper)) +Connect to a server implementing the ShaderAPI protocol. The client requests the server's shader pack and asks for approval before downloading and applying it. Acceptance is remembered by server group. +A server-side implementation for Paper is available in [ShaderAPI-Paper](https://github.com/Liams-Services/ShaderAPI-Paper). Client support for 26.3 does not establish compatibility of that separate server plugin. ## Building -To build this mod, you need to have [Gradle](https://gradle.org/) installed. +Install JDK 25 and point `JAVA_HOME` at it, then run the included Gradle wrapper: + +```sh +./gradlew clean build +``` + +On Windows, use `gradlew.bat clean build`. No separate Gradle installation is needed. The build targets Minecraft 26.3 only; it no longer has a `chiseledBuild` task. The mod JAR and sources JAR are written to `build/libs/`. + +For an interactive development client, put the matching Iris and Sodium JARs in `run/mods/`, then run: + +```sh +./gradlew runClient +``` -1. Clone the repository -2. Open the project in your IDE -3. Run the `gradle chiseledBuild` task to build the mod for all versions +Before release, verify the approval/denial prompt, downloading and applying a valid pack, a cached pack, failed downloads, and shader reloads in a real client. A successful build does not verify GPU rendering or server-plugin compatibility. diff --git a/build.gradle.kts b/build.gradle.kts index c1336f6..5588031 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,11 +1,10 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.util.* plugins { - kotlin("jvm") version "2.3.20" - id("net.fabricmc.fabric-loom") version "1.15.5" + kotlin("jvm") version "2.4.21" + id("net.fabricmc.fabric-loom") version "1.17.21" id("maven-publish") } @@ -69,12 +68,17 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. implementation("net.fabricmc.fabric-api:fabric-api:${project.property("fabric_version")}") + testImplementation(platform("org.junit:junit-bom:5.13.4")) + testImplementation("org.junit.jupiter:junit-jupiter") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + } tasks.processResources { inputs.property("version", project.version) inputs.property("minecraft_version", project.property("minecraft_version")) inputs.property("loader_version", project.property("loader_version")) + inputs.property("kotlin_loader_version", project.property("kotlin_loader_version")) filteringCharset = "UTF-8" filesMatching("fabric.mod.json") { @@ -97,9 +101,11 @@ tasks.withType().configureEach { } tasks.withType().configureEach { - // Temporary: Kotlin JVM target 25 may lag; keep Java on 25 and downgrade Kotlin bytecode target. - compilerOptions.jvmTarget.set(JvmTarget.JVM_24) - jvmTargetValidationMode.set(JvmTargetValidationMode.WARNING) + compilerOptions.jvmTarget.set(JvmTarget.JVM_25) +} + +tasks.test { + useJUnitPlatform() } tasks.jar { diff --git a/gradle.properties b/gradle.properties index aa09925..3c1e4f5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,12 +2,12 @@ org.gradle.jvmargs=-Xmx1G # Fabric Properties # check these on https://modmuss50.me/fabric.html -minecraft_version=26.1.2 -loader_version=0.19.2 -kotlin_loader_version=1.13.10+kotlin.2.3.20 +minecraft_version=26.3 +loader_version=0.20.1 +kotlin_loader_version=1.14.2+kotlin.2.4.21 # Mod Properties maven_group=com.liamxsage archives_base_name=shaderapi # Dependencies # check this on https://modmuss50.me/fabric.html -fabric_version=0.146.1+26.1.2 +fabric_version=0.165.2+26.3 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1a70468..2d03569 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,8 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionSha256Sum=bbaeb2fef8710818cf0e261201dab964c572f92b942812df0c3620d62a529a01 diff --git a/src/client/kotlin/com/liamxsage/shaderapi/client/ShaderReceivePayloadHandler.kt b/src/client/kotlin/com/liamxsage/shaderapi/client/ShaderReceivePayloadHandler.kt index b92fe01..49eee00 100644 --- a/src/client/kotlin/com/liamxsage/shaderapi/client/ShaderReceivePayloadHandler.kt +++ b/src/client/kotlin/com/liamxsage/shaderapi/client/ShaderReceivePayloadHandler.kt @@ -29,15 +29,15 @@ class ShaderReceivePayloadHandler : ClientPlayNetworking.PlayPayloadHandler + client.gui.setScreen(ConfirmScreen({ accept: Boolean -> if (accept) { downloadAndApplyShaderPack(shaderUrl, shaderHash) - context.client().setScreen(null) + context.client().gui.setScreen(null) ConfigManager.addServerGroup(serverGroup, ShaderPackAcceptState.ACCEPT) return@ConfirmScreen } - context.client().setScreen(null) + context.client().gui.setScreen(null) ConfigManager.addServerGroup(serverGroup, ShaderPackAcceptState.DENY) sendShaderStatusResponse(ShaderStatusResponse.DENIED) diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 181a5b0..122d823 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -22,6 +22,7 @@ "fabricloader": ">=${loader_version}", "fabric-language-kotlin": ">=${kotlin_loader_version}", "fabric-api": "*", - "minecraft": "${minecraft_version}" + "minecraft": "${minecraft_version}", + "java": ">=25" } } diff --git a/src/test/java/com/liamxsage/shaderapi/PayloadCodecTest.java b/src/test/java/com/liamxsage/shaderapi/PayloadCodecTest.java new file mode 100644 index 0000000..f7e55f6 --- /dev/null +++ b/src/test/java/com/liamxsage/shaderapi/PayloadCodecTest.java @@ -0,0 +1,77 @@ +package com.liamxsage.shaderapi; + +import io.netty.buffer.Unpooled; +import net.minecraft.core.RegistryAccess; +import net.minecraft.network.RegistryFriendlyByteBuf; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class PayloadCodecTest { + private RegistryFriendlyByteBuf buffer() { + return new RegistryFriendlyByteBuf(Unpooled.buffer(), RegistryAccess.EMPTY); + } + + @Test + void requestKeepsTheSingleBooleanWireFormat() { + var buffer = buffer(); + try { + ShaderRequestPayload.CODEC.encode(buffer, new ShaderRequestPayload(true)); + assertEquals(1, buffer.readableBytes()); + assertEquals(1, buffer.getUnsignedByte(0)); + assertTrue(ShaderRequestPayload.CODEC.decode(buffer).alwaysTrue()); + assertEquals(0, buffer.readableBytes()); + } finally { + buffer.release(); + } + } + + @Test + void receiveKeepsUrlHashAndServerGroupInProtocolOrder() { + var buffer = buffer(); + try { + ShaderReceivePayload.CODEC.encode(buffer, + new ShaderReceivePayload("https://example.com/shader.zip", "abc123", "lobby")); + assertEquals("https://example.com/shader.zip", buffer.readUtf()); + assertEquals("abc123", buffer.readUtf()); + assertEquals("lobby", buffer.readUtf()); + assertEquals(0, buffer.readableBytes()); + buffer.readerIndex(0); + assertEquals(new ShaderReceivePayload("https://example.com/shader.zip", "abc123", "lobby"), + ShaderReceivePayload.CODEC.decode(buffer)); + } finally { + buffer.release(); + } + } + + @Test + void receiveDecodesEmptyGroupWithoutChangingServerData() { + var buffer = buffer(); + try { + buffer.writeUtf("https://example.com/shader.zip"); + buffer.writeUtf("abc123"); + buffer.writeUtf(""); + var payload = ShaderReceivePayload.CODEC.decode(buffer); + assertEquals("https://example.com/shader.zip", payload.shaderUrl()); + assertEquals("abc123", payload.hash()); + assertEquals("", payload.serverGroup()); + assertEquals(0, buffer.readableBytes()); + } finally { + buffer.release(); + } + } + + @Test + void statusResponseKeepsTheUtf8WireFormat() { + var buffer = buffer(); + try { + ShaderStatusResponsePayload.CODEC.encode(buffer, new ShaderStatusResponsePayload("APPLY_FAILED")); + assertEquals("APPLY_FAILED", buffer.readUtf()); + assertEquals(0, buffer.readableBytes()); + buffer.readerIndex(0); + assertEquals("APPLY_FAILED", ShaderStatusResponsePayload.CODEC.decode(buffer).statusResponse()); + } finally { + buffer.release(); + } + } +}