diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a5defad..42a53f2 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,30 +1,64 @@
-# 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]
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
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/
+ name: mod-jar
+ path: build/libs/*.jar
+
+ - name: Read gradle.properties Release Flags
+ id: release_props
+ if: github.event_name == 'push' && github.ref == 'refs/heads/master'
+ run: |
+ SHOULD_RELEASE=$(grep -Po '^shouldRelease=.*' gradle.properties | cut -d= -f2 | xargs)
+
+ if [ "$SHOULD_RELEASE" != "true" ]; then
+ echo "shouldRelease is false — skipping release"
+ exit 0
+ fi
+
+ 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: ${{ 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 }}
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
diff --git a/README.md b/README.md
index c76728b..64eabfe 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
Cobalt

- 
+ 

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/client/AddonList_CrashReportMixin.java b/src/main/java/org/cobalt/mixin/client/AddonList_CrashReportMixin.java
index d85bf41..a7fe4ce 100644
--- a/src/main/java/org/cobalt/mixin/client/AddonList_CrashReportMixin.java
+++ b/src/main/java/org/cobalt/mixin/client/AddonList_CrashReportMixin.java
@@ -14,7 +14,7 @@
@Mixin(CrashReport.class)
public abstract class AddonList_CrashReportMixin {
- @Inject(method = "getDetails*", at = @At("HEAD"))
+ @Inject(method = "getDetails(Ljava/lang/StringBuilder;)V", at = @At("HEAD"))
private void addAddonInfo(StringBuilder crashReportBuilder, CallbackInfo ci) {
String addons = AddonLoader.INSTANCE.getAddons().stream()
.map(info -> info.getFirst().getName() + " v" + info.getFirst().getVersion())
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/ChatUtils.kt b/src/main/kotlin/org/cobalt/api/util/ChatUtils.kt
index eabf1bf..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]"
*
@@ -20,10 +23,15 @@ 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
}
/**
@@ -33,6 +41,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"))
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/loader/AddonLoader.kt b/src/main/kotlin/org/cobalt/internal/loader/AddonLoader.kt
index 2503396..fc6603d 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()
}
@@ -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) {
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..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 = Color(42, 42, 42).rgb
+ colorMask = if (addonIcon == boxIcon) Color(42, 42, 42).rgb else 0
)
NVGRenderer.text(
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
}