Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ea509c6
Version bumps for Minecraft 26.1
eclipseisoffline Mar 25, 2026
7fa6666
Initial pass at building errors
eclipseisoffline Mar 25, 2026
b65a41c
Fix imports in AnimationMapper
eclipseisoffline Mar 29, 2026
1bb6887
Try load textures from atlas in same order as vanilla's MaterialBaker
eclipseisoffline Mar 29, 2026
ddfd014
Fix remaining build errors in core module
eclipseisoffline Mar 29, 2026
1221abd
Fix build errors in client module
eclipseisoffline Mar 29, 2026
c30b581
Fix build errors in datagen module
eclipseisoffline Mar 29, 2026
e501792
Client mixin fixes
eclipseisoffline Mar 29, 2026
5336a0e
Remove core and datagen run configurations
eclipseisoffline Mar 29, 2026
f304710
Some mixin clean ups
eclipseisoffline Mar 29, 2026
4bff563
Null-mark the code
eclipseisoffline Mar 29, 2026
54fdfc8
Properly use nullable annotations, other small code clean ups
eclipseisoffline Mar 29, 2026
f74de22
Switch to ItemStackTemplate during mapping process to please the data…
eclipseisoffline Mar 30, 2026
ce39925
Start on adding support for transformations in item model definitions
eclipseisoffline Apr 4, 2026
5d51e6a
Implement transformation (translation+scale) mapping in GeometryMappe…
eclipseisoffline Apr 4, 2026
8ba06b0
Refactor: MappingContext#stack -> MappingContext#itemStack
eclipseisoffline Apr 4, 2026
8b4621e
Fix third_person animate condition in attachables
eclipseisoffline Apr 6, 2026
c254629
Update README + LICENSE
eclipseisoffline Apr 11, 2026
eff839a
Pack report improvements: instructions and statistics fixes
eclipseisoffline Apr 11, 2026
8eceab2
Fix incorrect texture/JSON count in report.txt, send feedback when st…
eclipseisoffline Apr 11, 2026
444df61
Fix fetching block/item textures safely with fallback to no atlas
eclipseisoffline Apr 11, 2026
ec0448a
Set up Modrinth publishing (hopefully), update Gradle wrapper, suppor…
eclipseisoffline Apr 11, 2026
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
16 changes: 8 additions & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Setup Gradle
uses: GeyserMC/actions/setup-gradle-composite@master
with:
setup-java_java-version: 21
setup-java_java-version: 25

- name: Build Rainbow
run: ./gradlew build
Expand Down Expand Up @@ -80,13 +80,13 @@ jobs:
client/build/libs/Rainbow.jar
changelog: ${{ steps.metadata.outputs.body }}

# - name: Publish to Modrinth
# if: ${{ success() && github.repository == 'GeyserMC/rainbow' && github.ref_name == 'master' }}
# env:
# CHANGELOG: ${{ steps.metadata.outputs.body }}
# BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
# MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
# run: ./gradlew modrinth
- name: Publish to Modrinth
if: ${{ success() && github.repository == 'GeyserMC/Rainbow' && github.ref_name == 'master' }}
env:
CHANGELOG: ${{ steps.metadata.outputs.body }}
BUILD_NUMBER: ${{ steps.release-info.outputs.curentRelease }}
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
run: ./gradlew modrinth

- name: Notify Discord
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Rainbow' }}
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2025 GeyserMC
Copyright (c) 2025-2026 GeyserMC

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
52 changes: 37 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,21 @@
[![Discord](https://img.shields.io/discord/613163671870242838.svg?color=%237289da&label=discord)](https://discord.gg/geysermc)

Rainbow is a client-side Minecraft mod for the Fabric modloader to generate Geyser item mappings and bedrock resourcepacks
for use with Geyser's [custom item API (v2)](https://github.com/geyserMC/geyser/pull/5189). Rainbow is available for Minecraft 1.21.9 and 1.21.10.
for use with Geyser's [custom item API (v2)](https://geysermc.org/wiki/geyser/custom-items). Rainbow is available for Minecraft 26.1.

Rainbow is currently experimental and capable of the following:

- Generating Geyser item mappings complete with data components and proper bedrock options, by detecting items with a custom `minecraft:item_model` component and analysing their components.
- Also includes generating mappings with predicates for more complicated Java item model definitions, such as checks for if an item is broken.
- Does not support range dispatch predicates yet.
- Also includes detecting if an item should be displayed handheld by looking at the item's model.
- Generating a simple bedrock resourcepack for simple 2D items, as well as:
- Simple custom armour items, by analysing an item's `minecraft:equippable` component and loaded equipment assets.
- 3D items (unlikely to work well as of now), by converting the Java model to a bedrock one, and generating an attachable and animations for it, as well as rendering a custom GUI icon.
Rainbow is currently experimental, and only capable of generating Geyser item mappings and bedrock resourcepacks for
somewhat simple 2D and 3D items. Features like animated textures are not yet supported. For a more descriptive list
of Rainbow's capabilities, see further below.

Rainbow works by detecting custom items in your inventory, or a container/inventory menu you have opened. It analyses
the components of detected items, and uses assets from loaded Java resourcepacks to gather information about item models, textures,
and more.

## Usage

You can download the latest version of Rainbow [here](https://download.geysermc.org/v2/projects/rainbow/versions/latest/builds/latest/downloads/rainbow).

To use Rainbow's generated item mappings, you must use a build of Geyser with support for the v2 item mappings format.
You can download those [here](https://github.com/geyserMC/geyser/pull/5189).
You can download the latest version of Rainbow [here](https://download.geysermc.org/v2/projects/rainbow/versions/latest/builds/latest/downloads/rainbow). Using Rainbow requires the [Fabric API](https://modrinth.com/mod/fabric-api) to be installed.

To use Rainbow itself, you must install it on your Minecraft client. Rainbow adds a few commands to the client. Generally,
To use Rainbow, you must install it on your Minecraft client. Rainbow adds a few commands to the client. Generally,
you use them as follows:

1. First, start a new pack by running `/rainbow create <name>`, replacing `<name>` with the name of your pack. Your resourcepack and item mappings will be exported in the `.minecraft/rainbow/<name>` folder. Anything in here can be overwritten!
Expand All @@ -48,3 +39,34 @@ Once you have taken these steps, restart your server. Bedrock players should the
and if everything went well, they should be able to see custom items!

If you have any questions or run into any problems, please do feel free to ask for support in the Geyser Discord!

## What Rainbow can do

Rainbow is currently capable of the following:

- Generating Geyser item mappings complete with data components and proper bedrock options, by detecting items with a custom `minecraft:item_model` component and analysing their components.
- Also includes generating mappings with predicates for more complicated Java item model definitions, such as checks for if an item is broken. The following definition types are currently supported by Rainbow:
- Plain item model definitions.
- Conditional item models, supported properties are:
- `broken`,
- `damaged`,
- `cutom_model_data`,
- `has_component`, and,
- `fishing_rod/cast`.
- Range dispatch item models, supported properties are:
- `bundle/fullness`,
- `count`,
- `custom_model_data`, and,
- `damage`.
- Select item models, supported properties are:
- `charge_type`,
- `trim_material`,
- `context_dimension`, and,
- `custom_model_data`.
- For the `display_context` property, the `gui` case is mapped, if present.
- Also includes detecting if an item should be displayed handheld by looking at the item's model.
- Also is able to detect and map items using the "legacy" `custom_model_data` range-dispatch style, and map them to Geyser's `legacy` item mappings.
- Generating a simple bedrock resourcepack for simple 2D items, as well as:
- Simple custom armour items, by analysing an item's `minecraft:equippable` component and loaded equipment assets.
- 3D items, by converting the Java model to a bedrock one, and generating an attachable and animations for it, as well as rendering a custom GUI icon.
- Is able to translate display transformations for the head, first-person and third-person item slots.
1 change: 1 addition & 0 deletions build-logic/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ dependencies {
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))

implementation(libs.fabric.loom)
implementation(libs.minotaur)
}
15 changes: 15 additions & 0 deletions build-logic/src/main/kotlin/extensions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import org.gradle.api.Project

// Nicely stolen from Geyser

fun buildNumber(): Int {
return System.getenv()["BUILD_NUMBER"]?.let {Integer.parseInt(it)} ?: -1
}

fun projectVersion(project: Project): String {
return project.version.toString().replace("SNAPSHOT", "b" + buildNumber())
}

fun versionName(project: Project): String {
return "Rainbow-${projectVersion(project)}"
}
36 changes: 8 additions & 28 deletions build-logic/src/main/kotlin/rainbow.base-conventions.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,60 +1,40 @@
plugins {
id("fabric-loom")
id("net.fabricmc.fabric-loom")
}

version = properties["mod_version"]!! as String
group = properties["maven_group"]!! as String

val archivesBaseName = properties["archives_base_name"]!! as String
val targetJavaVersion = 21
val targetJavaVersion = 25

val buildNumber = System.getenv()["BUILD_NUMBER"]?: "DEV"
val fmjVersion = "$version-$buildNumber"
val fmjVersion = projectVersion(project)

base {
archivesName = archivesBaseName
}

repositories {
maven {
name = "ParchmentMC"
url = uri("https://maven.parchmentmc.org")
}

maven {
name = "Jitpack"
url = uri("https://jitpack.io")
}

maven {
name = "Open Collaboration"
url = uri("https://repo.opencollab.dev/main")
}
}
repositories {}

dependencies {
minecraft(libs.minecraft)
mappings(loom.layered {
officialMojangMappings()
parchment(libs.parchment)
})

modImplementation(libs.fabric.loader)
modImplementation(libs.fabric.api)
implementation(libs.fabric.loader)
implementation(libs.fabric.api)
}

tasks {
processResources {
inputs.property("version", fmjVersion)
inputs.property("supported_versions", libs.versions.minecraft.supported.get())
inputs.property("supported_versions", libs.versions.minecraft.release.get())
inputs.property("loader_version", libs.versions.fabric.loader.get())
filteringCharset = "UTF-8"

filesMatching("fabric.mod.json") {
expand(
mapOf(
"version" to fmjVersion,
"supported_versions" to libs.versions.minecraft.supported.get(),
"minecraft_version" to libs.versions.minecraft.release.get(),
"loader_version" to libs.versions.fabric.loader.get()
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
plugins {
id("com.modrinth.minotaur")
}

modrinth {
token.set(System.getenv("MODRINTH_TOKEN") ?: "")
debugMode.set(System.getenv("MODRINTH_TOKEN") == null)
projectId.set("QD7c9rxP")
versionName.set(versionName(project))
versionNumber.set(projectVersion(project))
versionType.set("beta")
changelog.set(System.getenv("CHANGELOG") ?: "")
gameVersions.addAll(libs.versions.minecraft.supported.modrinth.get().split(","))
loaders.add("fabric")

dependencies {
required.project("P7dR8mSH") // Fabric API
}
}
20 changes: 10 additions & 10 deletions client/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
import net.fabricmc.loom.task.RemapJarTask

plugins {
id("rainbow.base-conventions")
id("rainbow.publish-conventions")
id("rainbow.modrinth-publish-conventions")
}

dependencies {
// Implement namedElements so IDEs can use it correctly, but include the remapped build
implementation(project(path = ":rainbow", configuration = "namedElements"))
implementation(project(":rainbow"))
include(project(":rainbow"))
}

tasks {
val copyJarTask = register<Copy>("copyRainbowClientJar") {
group = "build"

val remapJarTask = getByName<RemapJarTask>("remapJar")
dependsOn(remapJarTask)
val jarTask = getByName<Jar>("jar")
dependsOn(jarTask)

from(remapJarTask.archiveFile)
rename {
"Rainbow.jar"
}
from(jarTask.archiveFile)
rename {"Rainbow.jar"}
into(project.layout.buildDirectory.file("libs"))
}

named("build") {
dependsOn(copyJarTask)
}
}

modrinth {
uploadFile.set(tasks.jar)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,53 @@
import com.mojang.serialization.Codec;
import com.mojang.serialization.DynamicOps;
import com.mojang.serialization.JsonOps;
import net.minecraft.client.Minecraft;
import net.minecraft.core.HolderLookup;
import net.minecraft.resources.RegistryOps;
import net.minecraft.util.Util;
import org.geysermc.rainbow.CodecUtil;
import org.geysermc.rainbow.RainbowIO;
import org.geysermc.rainbow.mapping.PackSerializer;
import org.jspecify.annotations.Nullable;

import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;

public class MinecraftPackSerializer implements PackSerializer {
private final HolderLookup.Provider registries;
public class ClientPackSerializer implements PackSerializer {
private HolderLookup.@Nullable Provider registries = null;
private int jsonExported = 0;
private int texturesExported = 0;

public MinecraftPackSerializer(Minecraft minecraft) {
registries = Objects.requireNonNull(minecraft.level).registryAccess();
public void prepare(HolderLookup.Provider registries) {
this.registries = registries;
jsonExported = 0;
texturesExported = 0;
}

public int jsonExported() {
return jsonExported;
}

public int texturesExported() {
return texturesExported;
}

@Override
public <T> CompletableFuture<?> saveJson(Codec<T> codec, T object, Path path) {
if (registries == null) {
throw new IllegalStateException("saveJson called whilst registries was null");
}
DynamicOps<JsonElement> ops = RegistryOps.create(JsonOps.INSTANCE, registries);
return CompletableFuture.runAsync(() -> RainbowIO.safeIO(() -> CodecUtil.trySaveJson(codec, object, path, ops)),
Util.backgroundExecutor().forName("PackSerializer-saveJson"));
jsonExported++;
return CompletableFuture.runAsync(() -> RainbowIO.safeIO(() -> {
CodecUtil.trySaveJson(codec, object, path, ops);
}), Util.backgroundExecutor().forName("PackSerializer-saveJson"));
}

@Override
public CompletableFuture<?> saveTexture(byte[] texture, Path path) {
texturesExported++;
return CompletableFuture.runAsync(() -> RainbowIO.safeIO(() -> {
CodecUtil.ensureDirectoryExists(path.getParent());
try (OutputStream outputTexture = new FileOutputStream(path.toFile())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import net.minecraft.client.renderer.item.ClientItem;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.AtlasManager;
import net.minecraft.client.resources.model.EquipmentAssetManager;
import net.minecraft.client.resources.model.EquipmentClientInfo;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ResolvedModel;
import net.minecraft.client.resources.model.sprite.AtlasManager;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.packs.resources.ResourceManager;
Expand All @@ -21,6 +21,7 @@
import org.geysermc.rainbow.mapping.AssetResolver;
import org.geysermc.rainbow.mapping.texture.TextureResource;
import org.geysermc.rainbow.mixin.SpriteContentsAccessor;
import org.jspecify.annotations.Nullable;

import java.io.InputStream;
import java.util.Optional;
Expand Down Expand Up @@ -54,7 +55,7 @@ public Optional<EquipmentClientInfo> getEquipmentInfo(ResourceKey<EquipmentAsset
}

@Override
public Optional<TextureResource> getTexture(Identifier atlasId, Identifier identifier) {
public Optional<TextureResource> getTexture(@Nullable Identifier atlasId, Identifier identifier) {
if (atlasId == null) {
// Not in an atlas - so not animated, probably?
return RainbowIO.safeIO(() -> {
Expand Down
Loading
Loading