Skip to content
Open
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
72 changes: 72 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,30 @@ val lwjglVersion: String by project
val sodiumVersion: String by project
val litematicaVersion: String by project
val maLiLibVersion: String by project
val composeVersion: String by project
val skikoVersion: String by project

// Skiko publishes its natives as one artifact per platform, and none of them are pulled in
// transitively by Compose. Dev runs only need the host's, so resolve that here.
// TODO: production jars need every target bundled, or downloaded at runtime.
val skikoTarget = run {
val osName = System.getProperty("os.name").lowercase()
val archName = System.getProperty("os.arch").lowercase()

val os = when {
osName.startsWith("windows") -> "windows"
osName.startsWith("mac") || osName.contains("darwin") -> "macos"
osName.contains("linux") -> "linux"
else -> throw GradleException("No skiko natives for host OS '$osName'")
}
val arch = when (archName) {
"x86_64", "amd64" -> "x64"
"aarch64", "arm64" -> "arm64"
else -> throw GradleException("No skiko natives for host architecture '$archName'")
}

"$os-$arch"
}

val libs = file("libs")
val targets = listOf("fabric.mod.json")
Expand All @@ -46,6 +70,8 @@ val replacements = file("gradle.properties").inputStream().use { stream ->

plugins {
kotlin("jvm") version "2.3.0"
kotlin("plugin.compose") version "2.3.0"
id("org.jetbrains.compose") version "1.7.0"
id("org.jetbrains.dokka") version "2.1.0"
id("fabric-loom") version "1.16-SNAPSHOT"
id("com.gradleup.shadow") version "9.3.0"
Expand All @@ -63,6 +89,11 @@ configurations.all {
if (requested.group == "org.lwjgl") {
useVersion(lwjglVersion)
}
// The native artifact and the classes Compose pulls in transitively must agree,
// or skiko fails to find its library at runtime
if (requested.group == "org.jetbrains.skiko") {
useVersion(skikoVersion)
}
}
}

Expand All @@ -72,6 +103,8 @@ repositories {
maven("https://maven.2b2t.vc/releases") // Baritone
maven("https://jitpack.io") // KDiscordIPC
maven("https://api.modrinth.com/maven")
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") // Compose Multiplatform
google() // AndroidX dependencies (transitive from Compose)
mavenCentral()

// Allow the use of local libraries
Expand Down Expand Up @@ -111,6 +144,12 @@ loom {
property("org.lwjgl.util.DebugFunctions", "true")
property("mixin.debug.export", "true")

// Skia refuses to initialise on macOS without both of these
if (skikoTarget.startsWith("macos")) {
property("skiko.renderApi", "OPENGL")
property("skiko.macos.opengl.enabled", "true")
}

vmArgs("-XX:+HeapDumpOnOutOfMemoryError", "-XX:+CreateCoredumpOnCrash")
programArgs("--username", "Steve", "--uuid", "8667ba71b85a4004af54457a9734eed7", "--accessToken", "****")
}
Expand Down Expand Up @@ -187,6 +226,31 @@ dependencies {
modCompileOnly("maven.modrinth:malilib:$maLiLibVersion")
modCompileOnly("maven.modrinth:litematica:$litematicaVersion")

shadowLib("org.jetbrains.compose.runtime:runtime-desktop:$composeVersion") {
exclude(group = "org.jetbrains.kotlin")
exclude(group = "org.jetbrains.kotlinx")
exclude(group = "org.jetbrains.skiko")
}
shadowLib("org.jetbrains.compose.ui:ui-desktop:$composeVersion") {
exclude(group = "org.jetbrains.kotlin")
exclude(group = "org.jetbrains.kotlinx")
exclude(group = "org.jetbrains.skiko")
}
shadowLib("org.jetbrains.compose.foundation:foundation-desktop:$composeVersion") {
exclude(group = "org.jetbrains.kotlin")
exclude(group = "org.jetbrains.kotlinx")
exclude(group = "org.jetbrains.skiko")
}
shadowLib("org.jetbrains.compose.material3:material3-desktop:$composeVersion") {
exclude(group = "org.jetbrains.kotlin")
exclude(group = "org.jetbrains.kotlinx")
exclude(group = "org.jetbrains.skiko")
}

shadowLib("org.jetbrains.skiko:skiko-awt-runtime-$skikoTarget:$skikoVersion")

implementation(compose.components.resources)

// DevLogin
modRuntimeOnly("com.ptsmods:devlogin:3.5")

Expand All @@ -198,6 +262,14 @@ dependencies {
setupConfigurations()
}

compose.resources {
customDirectory(
"main",
provider { layout.projectDirectory.dir("src/main/resources/assets/lambda/compose/") }
)
packageOfResClass = "com.lambda.newui"
}

tasks {
test {
useJUnitPlatform()
Expand Down
5 changes: 4 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ modId=lambda
modName=Lambda
modVersion=0.1.0
modDescription=Minecraft utility mod for automation
modIcon=assets/lambda/textures/lambda.png
modIcon=assets/lambda/compose/drawable/lambda.png
mavenGroup=com.lambda
modAuthors=Constructor (Avanatiker), Blade, beanbag44, Emy

Expand All @@ -38,6 +38,9 @@ ktorVersion=3.3.3
jacksonVersion=3.1.1
mockkVersion=1.14.7
spairVersion=1.90.0
composeVersion=1.8.0
# Must match the skiko version Compose depends on transitively
skikoVersion=0.9.4

# Fabric https://fabricmc.net/develop/
fabricLoaderVersion=0.18.3
Expand Down
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pluginManagement {
repositories {
maven("https://maven.fabricmc.net/")
maven("https://jitpack.io")
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
mavenCentral()
gradlePluginPortal()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@

@Mixin(value = ConnectScreen.class)
public class ConnectScreenMixin {

@Inject(method = "connect(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/network/ServerAddress;Lnet/minecraft/client/network/ServerInfo;Lnet/minecraft/client/network/CookieStorage;)V", at= @At("HEAD"))
private void connectHead(MinecraftClient client, ServerAddress address, ServerInfo info, CookieStorage cookieStorage, CallbackInfo ci) {
AutoDisconnect.INSTANCE.setLastReconnectTarget(new MultiplayerReconnectTarget(address, info, cookieStorage));
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/lambda/mixin/render/SplashOverlayMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class SplashOverlayMixin {

@Inject(method = "<init>", at = @At("RETURN"))
private void onInit(CallbackInfo ci) {
LOGO = Identifier.of("lambda", "textures/lambda_banner.png");
LOGO = Identifier.of("lambda", "compose/drawable/lambda_banner.png");
}

@WrapOperation(method = "render", at = @At(value = "INVOKE", target = "Ljava/util/function/IntSupplier;getAsInt()I"))
Expand All @@ -61,7 +61,7 @@ public LogoTextureMixin(Identifier location) {

@WrapOperation(method = "loadContents", at = @At(value = "INVOKE", target = "Lnet/minecraft/resource/ResourceFactory;open(Lnet/minecraft/util/Identifier;)Ljava/io/InputStream;"))
InputStream wrapLoadTextureData(ResourceFactory instance, Identifier id, Operation<InputStream> original) {
return LambdaResourceKt.getStream("textures/lambda_banner.png");
return LambdaResourceKt.getStream("compose/drawable/lambda_banner.png");
}
}
}
14 changes: 12 additions & 2 deletions src/main/kotlin/com/lambda/config/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package com.lambda.config

import androidx.compose.ui.graphics.Color
import com.lambda.Lambda.typeFactory
import com.lambda.config.ConfigLoader.configs
import com.lambda.config.entries.ConfigEntryDsl
Expand All @@ -39,6 +40,7 @@ import com.lambda.config.settings.complex.Bind
import com.lambda.config.settings.complex.BlockPosSetting
import com.lambda.config.settings.complex.BlockSetting
import com.lambda.config.settings.complex.ColorSetting
import com.lambda.config.settings.complex.KColorSetting
import com.lambda.config.settings.complex.KeybindSetting
import com.lambda.config.settings.complex.Vec3dSetting
import com.lambda.config.settings.numeric.DoubleSetting
Expand All @@ -55,7 +57,7 @@ import net.minecraft.item.Item
import net.minecraft.registry.Registries
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d
import java.awt.Color
import java.awt.Color as JColor
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
import kotlin.reflect.full.declaredMemberProperties
Expand Down Expand Up @@ -347,11 +349,19 @@ abstract class Config(
@ConfigEntryDsl
fun setting(
name: String,
defaultValue: Color,
defaultValue: JColor,
description: String = "",
visibility: () -> Boolean = { true }
) = setting(name) { layer -> ColorSetting(name, description, this, layer, visibility, defaultValue) }

@ConfigEntryDsl
fun setting(
name: String,
defaultValue: Color,
description: String = "",
visibility: () -> Boolean = { true }
) = setting(name) { layer -> KColorSetting(name, description, this, layer, visibility, defaultValue) }

@ConfigEntryDsl
fun setting(
name: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2026 Lambda
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.lambda.config.settings.complex

import androidx.compose.ui.graphics.Color
import com.lambda.brigadier.argument.integer
import com.lambda.brigadier.argument.value
import com.lambda.brigadier.execute
import com.lambda.brigadier.optional
import com.lambda.brigadier.required
import com.lambda.config.Config
import com.lambda.config.entries.Setting
import com.lambda.config.entries.SettingEntryLayer
import com.lambda.util.extension.CommandBuilder
import net.minecraft.command.CommandRegistryAccess

class KColorSetting(
name: String,
description: String,
config: Config,
layer: SettingEntryLayer<KColorSetting, Color>,
visibility: () -> Boolean,
defaultValue: Color
) : Setting<Color>(name, description, defaultValue, layer, config, visibility) {
override fun CommandBuilder.buildCommand(registry: CommandRegistryAccess) {
required(integer("Red", 0, 255)) { red ->
required(integer("Green", 0, 255)) { green ->
required(integer("Blue", 0, 255)) { blue ->
optional(integer("Alpha", 0, 255)) { alpha ->
execute {
val alphaValue = alpha?.let { it().value() } ?: 255
trySetValue(Color(red().value(), green().value(), blue().value(), alphaValue))
}
}
}
}
}
}
}
6 changes: 3 additions & 3 deletions src/main/kotlin/com/lambda/graphics/text/FontHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ object FontHandler : Loadable, Config(
private val loadedAtlases = ConcurrentHashMap<String, SDFFontAtlas>()

private val baseFonts = listOf(
FontInfo("Minecraft Default", "fonts/MinecraftDefault-Regular.ttf"),
FontInfo("FiraSans Regular", "fonts/FiraSans-Regular.ttf"),
FontInfo("FiraSans Bold", "fonts/FiraSans-Bold.ttf")
FontInfo("Minecraft Default", "font/MinecraftDefault-Regular.ttf"),
FontInfo("FiraSans Regular", "font/FiraSans-Regular.ttf"),
FontInfo("FiraSans Bold", "font/FiraSans-Bold.ttf")
)

private val discoveredFonts = baseFonts.toMutableList()
Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/com/lambda/gui/DearImGui.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ object DearImGui : Loadable {
val size = BASE_FONT_SCALE * scale
with(io.fonts) {
clear()
addFontFromMemoryTTF("fonts/FiraSans-Regular.ttf".stream.readAllBytes(), size, fontConfig, glyphRanges)
addFontFromMemoryTTF("fonts/FiraSans-Bold.ttf".stream.readAllBytes(), size, fontConfig, glyphRanges)
addFontFromMemoryTTF("fonts/MinecraftDefault-Regular.ttf".stream.readAllBytes(), size, fontConfig, glyphRanges)
addFontFromMemoryTTF("font/FiraSans-Regular.ttf".stream.readAllBytes(), size, fontConfig, glyphRanges)
addFontFromMemoryTTF("font/FiraSans-Bold.ttf".stream.readAllBytes(), size, fontConfig, glyphRanges)
addFontFromMemoryTTF("font/MinecraftDefault-Regular.ttf".stream.readAllBytes(), size, fontConfig, glyphRanges)
build()
}
implGl3.createFontsTexture()
Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/com/lambda/gui/MenuBar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ import java.util.*
object MenuBar {
private var aboutRequested = false
var newConfigName = ""
val headerLogo = upload("textures/lambda_text_color.png")
val lambdaLogo = upload("textures/lambda.png")
val githubLogo = upload("textures/github_logo.png")
val headerLogo = upload("drawable/lambda_text_color.png")
val lambdaLogo = upload("drawable/lambda.png")
val githubLogo = upload("drawable/github_logo.png")

var height = 0f

Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/com/lambda/gui/components/ClickGuiLayout.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import com.lambda.config.settings.complex.KeybindSetting.Companion.onPressUnsafe
import com.lambda.core.Loadable
import com.lambda.event.events.GuiEvent
import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
import com.lambda.gui.DearImGui
import com.lambda.gui.LambdaScreen
import com.lambda.gui.MenuBar
import com.lambda.gui.MenuBar.buildMenuBar
Expand All @@ -49,6 +48,7 @@ import com.lambda.module.modules.client.Client
import com.lambda.module.modules.combat.autodisconnect.AutoDisconnectScreen
import com.lambda.module.tag.ModuleTag
import com.lambda.module.tag.ModuleTag.Companion.shownTags
import com.lambda.newui.ComposeClickGui
import com.lambda.sound.LambdaSound
import com.lambda.sound.SoundHandler.play
import com.lambda.util.Describable
Expand All @@ -75,11 +75,11 @@ object ClickGuiLayout : Loadable, Config(
var developerMode = false
// onPressUnsafe (not onPress) so the GUI can also be toggled from menu screens
// (title, multiplayer, world-select) where there is no SafeContext, not just in-game.
// Temporarily delegates to ComposeClickGui instead of the ImGui toggle.
val keybind by setting("Keybind", KeyCode.Y, screenCheck = false)
.onPressUnsafe {
if (DearImGui.io.wantTextInput) return@onPressUnsafe
if (!open && !canOpenOver(mc.currentScreen)) return@onPressUnsafe
toggle()
if (!ComposeClickGui.open && !canOpenOver(mc.currentScreen)) return@onPressUnsafe
ComposeClickGui.toggle()
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/lambda/module/hud/Watermark.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ object Watermark : HudModule(
tag = ModuleTag.HUD,
enabledByDefault = true,
) {
private val texture = upload("textures/lambda.png")
private val texture = upload("drawable/lambda.png")
private val scale by setting("Scale", 0.15f, 0.01f..1f, 0.01f)

override fun ImGuiBuilder.buildLayout() {
Expand Down
Loading