Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
37 changes: 28 additions & 9 deletions README.MD
Original file line number Diff line number Diff line change
@@ -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.
18 changes: 12 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -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")
}

Expand Down Expand Up @@ -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") {
Expand All @@ -97,9 +101,11 @@ tasks.withType<JavaCompile>().configureEach {
}

tasks.withType<KotlinCompile>().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 {
Expand Down
8 changes: 4 additions & 4 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 2 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ class ShaderReceivePayloadHandler : ClientPlayNetworking.PlayPayloadHandler<Shad

val client = context.client()
client.execute {
client.setScreen(ConfirmScreen({ accept: Boolean ->
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)

Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"fabricloader": ">=${loader_version}",
"fabric-language-kotlin": ">=${kotlin_loader_version}",
"fabric-api": "*",
"minecraft": "${minecraft_version}"
"minecraft": "${minecraft_version}",
"java": ">=25"
}
}
77 changes: 77 additions & 0 deletions src/test/java/com/liamxsage/shaderapi/PayloadCodecTest.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Loading