From d042ea57b54b77f4d361fd4fd9dbdab6daab1aa2 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 19 Jan 2026 10:10:54 +0100 Subject: [PATCH 01/20] feat: Automatic releases --- .github/workflows/build.yml | 75 ++++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5defad..e51380b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,8 +1,3 @@ -# Automatically build the project and run any configured tests for every push -# and submitted pull request. This can help catch issues that only occur on -# certain platforms or Java versions, and provides a first line of defence -# against bad commits. - name: build on: [pull_request, push] @@ -12,19 +7,89 @@ jobs: steps: - name: checkout repository uses: actions/checkout@v4 + - name: validate gradle wrapper uses: gradle/actions/wrapper-validation@v4 + - name: setup jdk uses: actions/setup-java@v4 with: java-version: '21' distribution: 'microsoft' + - name: make gradle wrapper executable run: chmod +x ./gradlew + - name: build run: ./gradlew build + - name: capture build artifacts uses: actions/upload-artifact@v4 with: name: Artifacts path: build/libs/ + + release: + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + runs-on: ubuntu-24.04 + needs: build + permissions: + contents: write + steps: + - name: checkout repository + uses: actions/checkout@v4 + + - name: read modVersion from gradle.properties + id: props + uses: BrycensRanch/read-properties-action@v1 + with: + file: gradle.properties + property: modVersion + + - name: download build artifacts from build job + uses: actions/download-artifact@v4 + with: + name: Artifacts + path: dist + + - name: decide release type + tag + id: rel + env: + GH_TOKEN: ${{ github.token }} + MOD_VERSION: ${{ steps.props.outputs.value }} + REPO: ${{ github.repository }} + run: | + set -euo pipefail + + version_tag="v${MOD_VERSION}" + + # Get latest release tag (empty if none) + latest_tag="$(curl -sS -H "Authorization: Bearer $GH_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/${REPO}/releases/latest" \ + | jq -r '.tag_name // empty' || true)" + + prerelease=false + tag="$version_tag" + name="$version_tag" + + # If latest release already matches this version, make a beta pre-release + if [ "$latest_tag" = "$version_tag" ]; then + prerelease=true + tag="${version_tag}-beta.${GITHUB_RUN_NUMBER}" + name="${version_tag} beta ${GITHUB_RUN_NUMBER}" + fi + + echo "tag=$tag" >> "$GITHUB_OUTPUT" + echo "name=$name" >> "$GITHUB_OUTPUT" + echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT" + + - name: create GitHub release + upload jars + uses: ncipollo/release-action@v1 + with: + tag: ${{ steps.rel.outputs.tag }} + name: ${{ steps.rel.outputs.name }} + prerelease: ${{ steps.rel.outputs.prerelease }} + artifacts: "dist/*" + allowUpdates: true + replacesArtifacts: true From 3741b820e7157bef8e715f0aaeed31211c20c636 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 19 Jan 2026 10:31:21 +0100 Subject: [PATCH 02/20] fix: improved counting logic for workflow --- .github/workflows/build.yml | 42 ++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e51380b..ec0ae8a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,7 +52,7 @@ jobs: name: Artifacts path: dist - - name: decide release type + tag + - name: decide release type + tag (version-scoped beta counter) id: rel env: GH_TOKEN: ${{ github.token }} @@ -63,27 +63,39 @@ jobs: version_tag="v${MOD_VERSION}" - # Get latest release tag (empty if none) - latest_tag="$(curl -sS -H "Authorization: Bearer $GH_TOKEN" \ + # List releases and extract tag_name values + tags="$(curl -sS -H "Authorization: Bearer $GH_TOKEN" \ -H "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/${REPO}/releases/latest" \ - | jq -r '.tag_name // empty' || true)" - - prerelease=false - tag="$version_tag" - name="$version_tag" - - # If latest release already matches this version, make a beta pre-release - if [ "$latest_tag" = "$version_tag" ]; then + "https://api.github.com/repos/${REPO}/releases?per_page=100" \ + | jq -r '.[].tag_name' || true)" + + # If a full release with vX.Y.Z already exists -> create next beta for that version + if echo "$tags" | grep -qx "$version_tag"; then + # Count existing betas for this exact version: vX.Y.Z-beta.N + n="$(echo "$tags" \ + | grep -E "^${version_tag}-beta\.[0-9]+$" \ + | sed -E "s/^${version_tag}-beta\.//" \ + | sort -n \ + | tail -n 1 || true)" + + if [ -z "${n:-}" ]; then + next=1 + else + next=$((n + 1)) + fi + + tag="${version_tag}-beta.${next}" + name="${version_tag}-beta.${next}" prerelease=true - tag="${version_tag}-beta.${GITHUB_RUN_NUMBER}" - name="${version_tag} beta ${GITHUB_RUN_NUMBER}" + else + tag="$version_tag" + name="$version_tag" + prerelease=false fi echo "tag=$tag" >> "$GITHUB_OUTPUT" echo "name=$name" >> "$GITHUB_OUTPUT" echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT" - - name: create GitHub release + upload jars uses: ncipollo/release-action@v1 with: From a673c727aa2bd0d5af4edd077cb13db8e39bfe10 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 19 Jan 2026 10:38:22 +0100 Subject: [PATCH 03/20] fix: made it so pr's no longer build twice (once for push, once for pr) --- .github/workflows/build.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ec0ae8a..60bda07 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,5 +1,11 @@ name: build -on: [pull_request, push] +on: + push: + branches: + - master + pull_request: + branches: + - master jobs: build: From 8f04ab0cebf7e88084282bde05e9e0ede367cbab Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Mon, 19 Jan 2026 08:13:18 -0500 Subject: [PATCH 04/20] chore: README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c76728b..64eabfe 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

Cobalt

![mod-loader](https://img.shields.io/badge/modloader-Fabric-4682b4?style=for-the-badge&logoColor=white) - ![minecraft-version](https://img.shields.io/badge/Minecraft-1.20.10-6BAA57?style=for-the-badge&logoColor=white) + ![minecraft-version](https://img.shields.io/badge/Minecraft-1.21.10-6BAA57?style=for-the-badge&logoColor=white) ![language](https://img.shields.io/badge/version-1.0.0-FF474C?style=for-the-badge&logoColor=white) From fe4e85bd8a22a3f7d98efe68a1128aa72d6f68f4 Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Mon, 19 Jan 2026 08:47:06 -0500 Subject: [PATCH 05/20] chore: smol cleanup --- .../org/cobalt/internal/ui/panel/panels/UISidebar.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/org/cobalt/internal/ui/panel/panels/UISidebar.kt b/src/main/kotlin/org/cobalt/internal/ui/panel/panels/UISidebar.kt index deffd4a..6c3a2bc 100644 --- a/src/main/kotlin/org/cobalt/internal/ui/panel/panels/UISidebar.kt +++ b/src/main/kotlin/org/cobalt/internal/ui/panel/panels/UISidebar.kt @@ -23,13 +23,11 @@ internal class UISidebar : UIPanel( } private val steveIcon = NVGRenderer.createImage("/assets/cobalt/steve.png") - private val userIcon = Minecraft.getInstance().user.profileId?.let { - try { - NVGRenderer.createImage("https://mc-heads.net/avatar/${Minecraft.getInstance().user.profileId}/100/face.png") - } catch (_: Exception) { - steveIcon - } - } ?: steveIcon + private val userIcon = try { + NVGRenderer.createImage("https://mc-heads.net/avatar/${Minecraft.getInstance().user.profileId ?: "a"}/100/face.png") + } catch (_: Exception) { + steveIcon + } private val userIconTooltip = UITooltip( content = { UITextTooltip("Hello, ${Minecraft.getInstance().user.name}!") }, From 364f6470a118dd330a92e45327365dee2c93238c Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Mon, 19 Jan 2026 09:26:13 -0500 Subject: [PATCH 06/20] chore: update property name --- build.gradle.kts | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index ec7de09..b5f3641 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,7 +39,7 @@ dependencies { modImplementation("net.fabricmc:fabric-loader:${property("loader_version")}") modImplementation("net.fabricmc:fabric-language-kotlin:${property("fabric_kotlin_version")}") - modImplementation("net.fabricmc.fabric-api:fabric-api:${property("fabric_version")}") + modImplementation("net.fabricmc.fabric-api:fabric-api:${property("fabric_api_version")}") modRuntimeOnly("me.djtheredstoner:DevAuth-fabric:1.2.1") diff --git a/gradle.properties b/gradle.properties index e76626e..9ee9e0e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,5 +14,5 @@ loader_version=0.17.3 loom_version=1.13-SNAPSHOT # Dependency Versions -fabric_version=0.138.3+1.21.10 +fabric_api_version=0.138.3+1.21.10 fabric_kotlin_version=1.13.7+kotlin.2.2.21 From 37db970b1984f44536b70b0ab64d1751d8247ad7 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 19 Jan 2026 15:30:34 +0100 Subject: [PATCH 07/20] fix: improved validation on Chatutils.sendDebug and Chatutils.sendMessage --- src/main/kotlin/org/cobalt/api/util/ChatUtils.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/kotlin/org/cobalt/api/util/ChatUtils.kt b/src/main/kotlin/org/cobalt/api/util/ChatUtils.kt index eabf1bf..f618ca3 100644 --- a/src/main/kotlin/org/cobalt/api/util/ChatUtils.kt +++ b/src/main/kotlin/org/cobalt/api/util/ChatUtils.kt @@ -20,6 +20,8 @@ object ChatUtils { */ @JvmStatic fun sendDebug(message: String) { + if (mc.player == null || mc.level == null) return + mc.gui.chat.addMessage( Component.empty().append(debugPrefix) .append(Component.literal("${ChatFormatting.RESET}$message")) @@ -33,6 +35,8 @@ object ChatUtils { */ @JvmStatic fun sendMessage(message: String) { + if (mc.player == null || mc.level == null) return + mc.gui.chat.addMessage( Component.empty().append(prefix) .append(Component.literal("${ChatFormatting.RESET}$message")) From 417d33e71d41dae1a55b5755ff9df382e8dbe2d8 Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 19 Jan 2026 15:44:16 +0100 Subject: [PATCH 08/20] fix: should? fix a crash when getDetails() is called. --- .../org/cobalt/mixin/client/AddonList_CrashReportMixin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cobalt/mixin/client/AddonList_CrashReportMixin.java b/src/main/java/org/cobalt/mixin/client/AddonList_CrashReportMixin.java index d85bf41..8cfede7 100644 --- a/src/main/java/org/cobalt/mixin/client/AddonList_CrashReportMixin.java +++ b/src/main/java/org/cobalt/mixin/client/AddonList_CrashReportMixin.java @@ -14,8 +14,8 @@ @Mixin(CrashReport.class) public abstract class AddonList_CrashReportMixin { - @Inject(method = "getDetails*", at = @At("HEAD")) - private void addAddonInfo(StringBuilder crashReportBuilder, CallbackInfo ci) { + @Inject(method = "getDetails(Ljava/lang/StringBuilder;)V", at = @At("HEAD")) + private void cobalt$addAddonInfo(StringBuilder crashReportBuilder, CallbackInfo ci) { String addons = AddonLoader.INSTANCE.getAddons().stream() .map(info -> info.getFirst().getName() + " v" + info.getFirst().getVersion()) .collect(Collectors.joining(", ")); From f6c8cccccc1974ce7c2d113870ef3b30ae7f538f Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Mon, 19 Jan 2026 09:49:32 -0500 Subject: [PATCH 09/20] fix: add check to prevent resending same message with sendDebug --- src/main/kotlin/org/cobalt/api/util/ChatUtils.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/kotlin/org/cobalt/api/util/ChatUtils.kt b/src/main/kotlin/org/cobalt/api/util/ChatUtils.kt index f618ca3..089f817 100644 --- a/src/main/kotlin/org/cobalt/api/util/ChatUtils.kt +++ b/src/main/kotlin/org/cobalt/api/util/ChatUtils.kt @@ -13,6 +13,9 @@ object ChatUtils { private val mc: Minecraft = Minecraft.getInstance() + // used for sendDebug to prevent resending same message multiple times in a row + private var lastMessage: String = "" + /** * Function to display a message in Minecraft chat with the prefix "[Cobalt Debug]" * @@ -21,11 +24,14 @@ object ChatUtils { @JvmStatic fun sendDebug(message: String) { if (mc.player == null || mc.level == null) return + if (message == lastMessage) return mc.gui.chat.addMessage( Component.empty().append(debugPrefix) .append(Component.literal("${ChatFormatting.RESET}$message")) ) + + lastMessage = message } /** From 8798aa3d9f82dcc86ff0c1192bd60abc4beb5f9b Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Mon, 19 Jan 2026 09:50:20 -0500 Subject: [PATCH 10/20] chore: cleanup (its not needed) --- .../org/cobalt/mixin/client/AddonList_CrashReportMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/cobalt/mixin/client/AddonList_CrashReportMixin.java b/src/main/java/org/cobalt/mixin/client/AddonList_CrashReportMixin.java index 8cfede7..a7fe4ce 100644 --- a/src/main/java/org/cobalt/mixin/client/AddonList_CrashReportMixin.java +++ b/src/main/java/org/cobalt/mixin/client/AddonList_CrashReportMixin.java @@ -15,7 +15,7 @@ public abstract class AddonList_CrashReportMixin { @Inject(method = "getDetails(Ljava/lang/StringBuilder;)V", at = @At("HEAD")) - private void cobalt$addAddonInfo(StringBuilder crashReportBuilder, CallbackInfo ci) { + private void addAddonInfo(StringBuilder crashReportBuilder, CallbackInfo ci) { String addons = AddonLoader.INSTANCE.getAddons().stream() .map(info -> info.getFirst().getName() + " v" + info.getFirst().getVersion()) .collect(Collectors.joining(", ")); From a99ac272859a2598452b88fdd6b38d5f7afa9bec Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Fri, 23 Jan 2026 13:41:30 -0500 Subject: [PATCH 11/20] feat: better github release --- .github/workflows/build.yml | 94 +++++++++---------------------------- 1 file changed, 22 insertions(+), 72 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 60bda07..d230683 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,81 +33,31 @@ jobs: uses: actions/upload-artifact@v4 with: name: Artifacts - path: build/libs/ - - release: - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - runs-on: ubuntu-24.04 - needs: build - permissions: - contents: write - steps: - - name: checkout repository - uses: actions/checkout@v4 - - - name: read modVersion from gradle.properties - id: props - uses: BrycensRanch/read-properties-action@v1 - with: - file: gradle.properties - property: modVersion - - - name: download build artifacts from build job - uses: actions/download-artifact@v4 - with: - name: Artifacts - path: dist - - - name: decide release type + tag (version-scoped beta counter) - id: rel - env: - GH_TOKEN: ${{ github.token }} - MOD_VERSION: ${{ steps.props.outputs.value }} - REPO: ${{ github.repository }} + path: build/libs/*.jar + + - name: read gradle.properties release flags + id: release_props + if: github.event_name == 'push' && github.ref == 'refs/heads/master' run: | - set -euo pipefail - - version_tag="v${MOD_VERSION}" + SHOULD_RELEASE=$(grep -Po '^shouldRelease=.*' gradle.properties | cut -d= -f2 | xargs) - # List releases and extract tag_name values - tags="$(curl -sS -H "Authorization: Bearer $GH_TOKEN" \ - -H "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/${REPO}/releases?per_page=100" \ - | jq -r '.[].tag_name' || true)" - - # If a full release with vX.Y.Z already exists -> create next beta for that version - if echo "$tags" | grep -qx "$version_tag"; then - # Count existing betas for this exact version: vX.Y.Z-beta.N - n="$(echo "$tags" \ - | grep -E "^${version_tag}-beta\.[0-9]+$" \ - | sed -E "s/^${version_tag}-beta\.//" \ - | sort -n \ - | tail -n 1 || true)" + if [ "$SHOULD_RELEASE" != "true" ]; then + echo "shouldRelease is false — skipping release" + exit 0 + fi - if [ -z "${n:-}" ]; then - next=1 - else - next=$((n + 1)) - fi + MOD_VERSION=$(grep -Po '^modVersion=.*' gradle.properties | cut -d= -f2 | xargs) - tag="${version_tag}-beta.${next}" - name="${version_tag}-beta.${next}" - prerelease=true - else - tag="$version_tag" - name="$version_tag" - prerelease=false - fi + echo "SHOULD_RELEASE=$SHOULD_RELEASE" >> $GITHUB_OUTPUT + echo "MOD_VERSION=$MOD_VERSION" >> $GITHUB_OUTPUT - echo "tag=$tag" >> "$GITHUB_OUTPUT" - echo "name=$name" >> "$GITHUB_OUTPUT" - echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT" - - name: create GitHub release + upload jars - uses: ncipollo/release-action@v1 + - name: create GitHub release + if: steps.release_props.outputs.SHOULD_RELEASE == 'true' + uses: softprops/action-gh-release@v2 with: - tag: ${{ steps.rel.outputs.tag }} - name: ${{ steps.rel.outputs.name }} - prerelease: ${{ steps.rel.outputs.prerelease }} - artifacts: "dist/*" - allowUpdates: true - replacesArtifacts: true + tag_name: v${{ steps.release_props.outputs.MOD_VERSION }} + name: Cobalt ${{ steps.release_props.outputs.MOD_VERSION }} + files: build/libs/*.jar + body: ${{ github.event.head_commit.message }} + env: + GITHUB_TOKEN: ${{ github.token }} From c39e19f3f1c53603f7c3ebb80cabffb33bb5136a Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Fri, 23 Jan 2026 13:52:50 -0500 Subject: [PATCH 12/20] chore: cleanup --- .github/workflows/build.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d230683..b39b558 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,31 +11,31 @@ jobs: build: runs-on: ubuntu-24.04 steps: - - name: checkout repository + - name: Checkout Repository uses: actions/checkout@v4 - - name: validate gradle wrapper + - name: Validate Gradle Wrapper uses: gradle/actions/wrapper-validation@v4 - - name: setup jdk + - name: Setup JDK uses: actions/setup-java@v4 with: java-version: '21' distribution: 'microsoft' - - name: make gradle wrapper executable + - name: Make Gradle Wrapper Executable run: chmod +x ./gradlew - - name: build + - name: Build Jar run: ./gradlew build - - name: capture build artifacts + - name: Capture Build Artifacts uses: actions/upload-artifact@v4 with: name: Artifacts path: build/libs/*.jar - - name: read gradle.properties release flags + - name: Read gradle.properties Release Flags id: release_props if: github.event_name == 'push' && github.ref == 'refs/heads/master' run: | @@ -51,7 +51,7 @@ jobs: echo "SHOULD_RELEASE=$SHOULD_RELEASE" >> $GITHUB_OUTPUT echo "MOD_VERSION=$MOD_VERSION" >> $GITHUB_OUTPUT - - name: create GitHub release + - name: Create GitHub Release if: steps.release_props.outputs.SHOULD_RELEASE == 'true' uses: softprops/action-gh-release@v2 with: From 0dd6b9e64f56aefc317a5dc9f30530befd48da6f Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Fri, 23 Jan 2026 13:58:42 -0500 Subject: [PATCH 13/20] chore: cleanup 2 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b39b558..35f180c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,7 +56,7 @@ jobs: uses: softprops/action-gh-release@v2 with: tag_name: v${{ steps.release_props.outputs.MOD_VERSION }} - name: Cobalt ${{ steps.release_props.outputs.MOD_VERSION }} + name: Cobalt v${{ steps.release_props.outputs.MOD_VERSION }} files: build/libs/*.jar body: ${{ github.event.head_commit.message }} env: From 60e6cdbfb0f4f48fd7853819ff4020b2c400b759 Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Fri, 23 Jan 2026 14:01:01 -0500 Subject: [PATCH 14/20] fix: update tag name --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 35f180c..2b97b25 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,7 +55,7 @@ jobs: if: steps.release_props.outputs.SHOULD_RELEASE == 'true' uses: softprops/action-gh-release@v2 with: - tag_name: v${{ steps.release_props.outputs.MOD_VERSION }} + tag_name: release name: Cobalt v${{ steps.release_props.outputs.MOD_VERSION }} files: build/libs/*.jar body: ${{ github.event.head_commit.message }} From 19b246dc945af13c03a1216059c472b9d9e88b5e Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Fri, 23 Jan 2026 14:41:03 -0500 Subject: [PATCH 15/20] fix: better tag name --- .github/workflows/build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2b97b25..42a53f2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,7 @@ jobs: - name: Capture Build Artifacts uses: actions/upload-artifact@v4 with: - name: Artifacts + name: mod-jar path: build/libs/*.jar - name: Read gradle.properties Release Flags @@ -49,13 +49,14 @@ jobs: MOD_VERSION=$(grep -Po '^modVersion=.*' gradle.properties | cut -d= -f2 | xargs) echo "SHOULD_RELEASE=$SHOULD_RELEASE" >> $GITHUB_OUTPUT + echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT echo "MOD_VERSION=$MOD_VERSION" >> $GITHUB_OUTPUT - name: Create GitHub Release if: steps.release_props.outputs.SHOULD_RELEASE == 'true' uses: softprops/action-gh-release@v2 with: - tag_name: release + tag_name: ${{ steps.release_props.outputs.MOD_VERSION }}.${{ steps.release_props.outputs.SHA_SHORT }} name: Cobalt v${{ steps.release_props.outputs.MOD_VERSION }} files: build/libs/*.jar body: ${{ github.event.head_commit.message }} From 1e4dbc99dd6509d139bc147ba20a0294c3c7243b Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:15:26 -0500 Subject: [PATCH 16/20] fix: addon loader issues --- src/main/kotlin/org/cobalt/internal/loader/AddonLoader.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/cobalt/internal/loader/AddonLoader.kt b/src/main/kotlin/org/cobalt/internal/loader/AddonLoader.kt index 2503396..4811855 100644 --- a/src/main/kotlin/org/cobalt/internal/loader/AddonLoader.kt +++ b/src/main/kotlin/org/cobalt/internal/loader/AddonLoader.kt @@ -52,8 +52,8 @@ object AddonLoader { Files.newDirectoryStream(addonsDir, "*.jar").use { stream -> for (jarPath in stream) { try { - loadAddon(jarPath) FabricLauncherBase.getLauncher().addToClassPath(jarPath) + loadAddon(jarPath) } catch (e: Exception) { e.printStackTrace() } From e33eb148bd7b7c7ded1e56f43aff11dac7d70b73 Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:29:29 -0500 Subject: [PATCH 17/20] fix: addon loader --- src/main/kotlin/org/cobalt/internal/loader/AddonLoader.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/cobalt/internal/loader/AddonLoader.kt b/src/main/kotlin/org/cobalt/internal/loader/AddonLoader.kt index 4811855..fc6603d 100644 --- a/src/main/kotlin/org/cobalt/internal/loader/AddonLoader.kt +++ b/src/main/kotlin/org/cobalt/internal/loader/AddonLoader.kt @@ -92,7 +92,7 @@ object AddonLoader { ) } - val instance = Class.forName(entrypoint, true, jarPath.javaClass.classLoader).let { + val instance = Class.forName(entrypoint).let { try { it.getField("INSTANCE").get(null) } catch (_: NoSuchFieldException) { From 70ee37504b6224d53cb2f57f88ff31c76571b892 Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:37:44 -0500 Subject: [PATCH 18/20] fix: addon entry rendering icons with tint --- .../kotlin/org/cobalt/internal/ui/components/UIAddonEntry.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/cobalt/internal/ui/components/UIAddonEntry.kt b/src/main/kotlin/org/cobalt/internal/ui/components/UIAddonEntry.kt index ee67f21..a4c4b00 100644 --- a/src/main/kotlin/org/cobalt/internal/ui/components/UIAddonEntry.kt +++ b/src/main/kotlin/org/cobalt/internal/ui/components/UIAddonEntry.kt @@ -37,7 +37,7 @@ internal class UIAddonEntry( NVGRenderer.image( addonIcon, x + 20F, y + height / 2F - 15F, 30F, 30F, - colorMask = Color(42, 42, 42).rgb + colorMask = if (addonIcon != boxIcon) Color(42, 42, 42).rgb else 0 ) NVGRenderer.text( From 2582fc7eadf34ef6787f63d167c2b3243372b2bd Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:48:07 -0500 Subject: [PATCH 19/20] fix: really dumb bug --- .../kotlin/org/cobalt/internal/ui/components/UIAddonEntry.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/cobalt/internal/ui/components/UIAddonEntry.kt b/src/main/kotlin/org/cobalt/internal/ui/components/UIAddonEntry.kt index a4c4b00..e0f1b78 100644 --- a/src/main/kotlin/org/cobalt/internal/ui/components/UIAddonEntry.kt +++ b/src/main/kotlin/org/cobalt/internal/ui/components/UIAddonEntry.kt @@ -37,7 +37,7 @@ internal class UIAddonEntry( NVGRenderer.image( addonIcon, x + 20F, y + height / 2F - 15F, 30F, 30F, - colorMask = if (addonIcon != boxIcon) Color(42, 42, 42).rgb else 0 + colorMask = if (addonIcon == boxIcon) Color(42, 42, 42).rgb else 0 ) NVGRenderer.text( From 74a372d1661765fb766898e4115f96cc79f338e9 Mon Sep 17 00:00:00 2001 From: Nathan <209938737+quiteboring@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:43:36 -0500 Subject: [PATCH 20/20] Revert "Merge branch '1.21.11' into master" This reverts commit 391a2459d8f266f6ec024cd305ddd08ab2ec87ad, reversing changes made to 2582fc7eadf34ef6787f63d167c2b3243372b2bd. --- build.gradle.kts | 1 - gradle.properties | 10 +-- .../WorldContext_LevelRendererMixin.java | 10 +-- src/main/kotlin/org/cobalt/Cobalt.kt | 2 +- .../org/cobalt/api/command/CommandManager.kt | 8 +- .../org/cobalt/api/util/render/Render3D.kt | 76 ++++++++++++++----- .../cobalt/internal/helper/RenderLayers.kt | 39 ++++++++++ .../cobalt/internal/helper/RenderPipelines.kt | 57 ++++++++++++++ .../internal/ui/panel/panels/UISidebar.kt | 2 +- 9 files changed, 168 insertions(+), 37 deletions(-) create mode 100644 src/main/kotlin/org/cobalt/internal/helper/RenderLayers.kt create mode 100644 src/main/kotlin/org/cobalt/internal/helper/RenderPipelines.kt diff --git a/build.gradle.kts b/build.gradle.kts index 284563f..b5f3641 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,7 +42,6 @@ dependencies { modImplementation("net.fabricmc.fabric-api:fabric-api:${property("fabric_api_version")}") modRuntimeOnly("me.djtheredstoner:DevAuth-fabric:1.2.1") - runtimeOnly("org.apache.httpcomponents:httpclient:4.5.14") modImplementation("org.lwjgl:lwjgl-nanovg:${lwjglVersion}") include("org.lwjgl:lwjgl-nanovg:${lwjglVersion}") diff --git a/gradle.properties b/gradle.properties index f1f733e..9ee9e0e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,10 +9,10 @@ shouldRelease=true lwjglVersion=3.3.3 # https://fabricmc.net/develop/ -minecraft_version=1.21.11 -loader_version=0.18.4 +minecraft_version=1.21.10 +loader_version=0.17.3 loom_version=1.13-SNAPSHOT -# Fabric API -fabric_api_version=0.141.1+1.21.11 -fabric_kotlin_version=1.13.8+kotlin.2.3.0 +# Dependency Versions +fabric_api_version=0.138.3+1.21.10 +fabric_kotlin_version=1.13.7+kotlin.2.2.21 diff --git a/src/main/java/org/cobalt/mixin/render/WorldContext_LevelRendererMixin.java b/src/main/java/org/cobalt/mixin/render/WorldContext_LevelRendererMixin.java index 8dea518..925635f 100644 --- a/src/main/java/org/cobalt/mixin/render/WorldContext_LevelRendererMixin.java +++ b/src/main/java/org/cobalt/mixin/render/WorldContext_LevelRendererMixin.java @@ -29,7 +29,6 @@ public class WorldContext_LevelRendererMixin { @Unique private final WorldRenderContext ctx = new WorldRenderContext(); - @Shadow @Final private RenderBuffers renderBuffers; @@ -42,14 +41,9 @@ private void render(GraphicsResourceAllocator allocator, DeltaTracker tickCounte } - @ModifyExpressionValue(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/LevelRenderer;prepareCullFrustum(Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;Lnet/minecraft/world/phys/Vec3;)Lnet/minecraft/client/renderer/culling/Frustum;")) - private Frustum onSetupFrustum(Frustum frustum) { - ctx.setFrustum(frustum); - return frustum; - } - @Inject(method = "method_62214", at = @At("RETURN")) - private void postRender(GpuBufferSlice gpuBufferSlice, LevelRenderState levelRenderState, ProfilerFiller profilerFiller, Matrix4f matrix4f, ResourceHandle resourceHandle, ResourceHandle resourceHandle2, boolean bl, ResourceHandle resourceHandle3, ResourceHandle resourceHandle4, CallbackInfo ci) { + private void postRender(GpuBufferSlice gpuBufferSlice, LevelRenderState worldRenderState, ProfilerFiller profiler, Matrix4f matrix4f, ResourceHandle handle, ResourceHandle handle2, boolean bl, Frustum frustum, ResourceHandle handle3, ResourceHandle handle4, CallbackInfo ci) { + ctx.setFrustum(frustum); new WorldRenderEvent.Last(ctx).post(); } diff --git a/src/main/kotlin/org/cobalt/Cobalt.kt b/src/main/kotlin/org/cobalt/Cobalt.kt index 487ec71..fde274b 100644 --- a/src/main/kotlin/org/cobalt/Cobalt.kt +++ b/src/main/kotlin/org/cobalt/Cobalt.kt @@ -25,7 +25,7 @@ object Cobalt : ClientModInitializer { listOf( TickScheduler, MainCommand, NotificationManager, - RotationExecutor + RotationExecutor, ).forEach { EventBus.register(it) } Config.loadModulesConfig() diff --git a/src/main/kotlin/org/cobalt/api/command/CommandManager.kt b/src/main/kotlin/org/cobalt/api/command/CommandManager.kt index 8852c40..24fa915 100644 --- a/src/main/kotlin/org/cobalt/api/command/CommandManager.kt +++ b/src/main/kotlin/org/cobalt/api/command/CommandManager.kt @@ -22,11 +22,11 @@ import org.cobalt.api.command.annotation.SubCommand object CommandManager { - private val commandsList = mutableListOf() + private val commands = mutableListOf() @JvmStatic - fun register(vararg commands: Command) { - commands.forEach(commandsList::add) + fun register(command: Command) { + commands.add(command) } internal fun dispatchAll() { @@ -34,7 +34,7 @@ object CommandManager { } private fun dispatchAll(dispatcher: CommandDispatcher, access: CommandBuildContext) { - commandsList.forEach { command -> + commands.forEach { command -> val rootNames = listOf(command.name) + command.aliases rootNames.forEach { rootName -> diff --git a/src/main/kotlin/org/cobalt/api/util/render/Render3D.kt b/src/main/kotlin/org/cobalt/api/util/render/Render3D.kt index 6a1fca5..298cd15 100644 --- a/src/main/kotlin/org/cobalt/api/util/render/Render3D.kt +++ b/src/main/kotlin/org/cobalt/api/util/render/Render3D.kt @@ -1,12 +1,16 @@ package org.cobalt.api.util.render +import com.mojang.blaze3d.systems.RenderSystem import java.awt.Color -import net.minecraft.gizmos.Gizmos -import net.minecraft.gizmos.GizmoStyle -import net.minecraft.util.ARGB +import kotlin.math.max +import kotlin.math.min +import net.minecraft.client.renderer.MultiBufferSource +import net.minecraft.client.renderer.ShapeRenderer import net.minecraft.world.phys.AABB import net.minecraft.world.phys.Vec3 import org.cobalt.api.event.impl.render.WorldRenderContext +import org.cobalt.internal.helper.RenderLayers +import org.joml.Vector3f object Render3D { @@ -16,15 +20,38 @@ object Render3D { return } - val strokeColor = ARGB.color(color.alpha, color.red, color.green, color.blue) - val fillColor = ARGB.color(150, color.red, color.green, color.blue) + val matrix = context.matrixStack ?: return + val bufferSource = context.consumers as? MultiBufferSource.BufferSource ?: return - val style = GizmoStyle.strokeAndFill(strokeColor, 2.5f, fillColor) - val props = Gizmos.cuboid(box, style) + val r = color.red / 255f + val g = color.green / 255f + val b = color.blue / 255f - if (esp) { - props.setAlwaysOnTop() - } + val fillLayer = if (esp) RenderLayers.TRIANGLE_STRIP_ESP else RenderLayers.TRIANGLE_STRIP + val lineLayer = if (esp) RenderLayers.LINE_LIST_ESP else RenderLayers.LINE_LIST + + matrix.pushPose() + with(context.camera.position) { matrix.translate(-x, -y, -z) } + + ShapeRenderer.addChainedFilledBoxVertices( + matrix, + bufferSource.getBuffer(fillLayer), + box.minX, box.minY, box.minZ, + box.maxX, box.maxY, box.maxZ, + r, g, b, 150 / 255F + ) + + ShapeRenderer.renderLineBox( + matrix.last(), + bufferSource.getBuffer(lineLayer), + box.minX, box.minY, box.minZ, + box.maxX, box.maxY, box.maxZ, + r, g, b, 1f + ) + + matrix.popPose() + bufferSource.endBatch(fillLayer) + bufferSource.endBatch(lineLayer) } @JvmStatic @@ -38,17 +65,32 @@ object Render3D { ) { if (!FrustumUtils.isVisible( context.frustum, - minOf(start.x, end.x), minOf(start.y, end.y), minOf(start.z, end.z), - maxOf(start.x, end.x), maxOf(start.y, end.y), maxOf(start.z, end.z) + min(start.x, end.x), min(start.y, end.y), min(start.z, end.z), + max(start.x, end.x), max(start.y, end.y), max(start.z, end.z) ) ) return - val argbColor = ARGB.color(color.alpha, color.red, color.green, color.blue) - val props = Gizmos.line(start, end, argbColor, thickness) + val matrix = context.matrixStack ?: return + val bufferSource = context.consumers as? MultiBufferSource.BufferSource ?: return + val layer = if (esp) RenderLayers.LINE_LIST_ESP else RenderLayers.LINE_LIST + RenderSystem.lineWidth(thickness) - if (esp) { - props.setAlwaysOnTop() - } + matrix.pushPose() + with(context.camera.position) { matrix.translate(-x, -y, -z) } + + val startOffset = Vector3f(start.x.toFloat(), start.y.toFloat(), start.z.toFloat()) + val direction = end.subtract(start) + + ShapeRenderer.renderVector( + matrix, + bufferSource.getBuffer(layer), + startOffset, + direction, + color.rgb + ) + + matrix.popPose() + bufferSource.endBatch(layer) } } diff --git a/src/main/kotlin/org/cobalt/internal/helper/RenderLayers.kt b/src/main/kotlin/org/cobalt/internal/helper/RenderLayers.kt new file mode 100644 index 0000000..7c403cb --- /dev/null +++ b/src/main/kotlin/org/cobalt/internal/helper/RenderLayers.kt @@ -0,0 +1,39 @@ +package org.cobalt.internal.helper + +import net.minecraft.client.renderer.RenderStateShard +import net.minecraft.client.renderer.RenderType +import net.minecraft.client.renderer.RenderType.CompositeRenderType + +internal object RenderLayers { + + val LINE_LIST: RenderType = RenderType.create( + "line-list", RenderType.TRANSIENT_BUFFER_SIZE, + RenderPipelines.LINE_LIST, + RenderType.CompositeState.builder() + .setLayeringState(RenderStateShard.VIEW_OFFSET_Z_LAYERING) + .createCompositeState(false) + ) + + val LINE_LIST_ESP: RenderType = RenderType.create( + "line-list-esp", RenderType.TRANSIENT_BUFFER_SIZE, + RenderPipelines.LINE_LIST_ESP, + RenderType.CompositeState.builder().createCompositeState(false) + ) + + val TRIANGLE_STRIP: CompositeRenderType = RenderType.create( + "triangle_strip", RenderType.TRANSIENT_BUFFER_SIZE, + false, true, + RenderPipelines.TRIANGLE_STRIP, + RenderType.CompositeState.builder() + .setLayeringState(RenderStateShard.VIEW_OFFSET_Z_LAYERING) + .createCompositeState(false) + ) + + val TRIANGLE_STRIP_ESP: CompositeRenderType = RenderType.create( + "triangle_strip_esp", RenderType.TRANSIENT_BUFFER_SIZE, + false, true, + RenderPipelines.TRIANGLE_STRIP_ESP, + RenderType.CompositeState.builder().createCompositeState(false) + ) + +} diff --git a/src/main/kotlin/org/cobalt/internal/helper/RenderPipelines.kt b/src/main/kotlin/org/cobalt/internal/helper/RenderPipelines.kt new file mode 100644 index 0000000..0da3ce9 --- /dev/null +++ b/src/main/kotlin/org/cobalt/internal/helper/RenderPipelines.kt @@ -0,0 +1,57 @@ +package org.cobalt.internal.helper + +import com.mojang.blaze3d.pipeline.BlendFunction +import com.mojang.blaze3d.pipeline.RenderPipeline +import com.mojang.blaze3d.platform.DepthTestFunction +import com.mojang.blaze3d.vertex.DefaultVertexFormat +import com.mojang.blaze3d.vertex.VertexFormat.Mode +import net.minecraft.client.renderer.RenderPipelines + +internal object RenderPipelines { + + val LINE_LIST: RenderPipeline = RenderPipelines.register( + RenderPipeline.builder(*arrayOf(RenderPipelines.LINES_SNIPPET)) + .withLocation("pipeline/lines") + .withVertexFormat(DefaultVertexFormat.POSITION_COLOR_NORMAL, Mode.LINES) + .withCull(false) + .withBlend(BlendFunction.TRANSLUCENT) + .withDepthWrite(true) + .withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST) + .build() + ) + + val LINE_LIST_ESP: RenderPipeline = RenderPipelines.register( + RenderPipeline.builder(*arrayOf(RenderPipelines.LINES_SNIPPET)) + .withLocation("pipeline/lines") + .withShaderDefine("shad") + .withVertexFormat(DefaultVertexFormat.POSITION_COLOR_NORMAL, Mode.LINES) + .withCull(false) + .withBlend(BlendFunction.TRANSLUCENT) + .withDepthWrite(false) + .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) + .build() + ) + + val TRIANGLE_STRIP: RenderPipeline = RenderPipelines.register( + RenderPipeline.builder(*arrayOf(RenderPipelines.DEBUG_FILLED_SNIPPET)) + .withLocation("pipeline/debug_filled_box") + .withCull(false) + .withVertexFormat(DefaultVertexFormat.POSITION_COLOR, Mode.TRIANGLE_STRIP) + .withDepthWrite(true) + .withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST) + .withBlend(BlendFunction.TRANSLUCENT) + .build() + ) + + val TRIANGLE_STRIP_ESP: RenderPipeline = RenderPipelines.register( + RenderPipeline.builder(*arrayOf(RenderPipelines.DEBUG_FILLED_SNIPPET)) + .withLocation("pipeline/debug_filled_box") + .withCull(false) + .withVertexFormat(DefaultVertexFormat.POSITION_COLOR, Mode.TRIANGLE_STRIP) + .withDepthWrite(false) + .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) + .withBlend(BlendFunction.TRANSLUCENT) + .build() + ) + +} diff --git a/src/main/kotlin/org/cobalt/internal/ui/panel/panels/UISidebar.kt b/src/main/kotlin/org/cobalt/internal/ui/panel/panels/UISidebar.kt index f9784d4..6c3a2bc 100644 --- a/src/main/kotlin/org/cobalt/internal/ui/panel/panels/UISidebar.kt +++ b/src/main/kotlin/org/cobalt/internal/ui/panel/panels/UISidebar.kt @@ -24,7 +24,7 @@ internal class UISidebar : UIPanel( private val steveIcon = NVGRenderer.createImage("/assets/cobalt/steve.png") private val userIcon = try { - NVGRenderer.createImage("https://mc-heads.net/avatar/${Minecraft.getInstance().user.profileId}/100/face.png") + NVGRenderer.createImage("https://mc-heads.net/avatar/${Minecraft.getInstance().user.profileId ?: "a"}/100/face.png") } catch (_: Exception) { steveIcon }