Skip to content
Closed
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
34 changes: 34 additions & 0 deletions common/src/main/kotlin/com/lambda/config/Configurable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.google.gson.reflect.TypeToken
import com.lambda.Lambda
import com.lambda.Lambda.LOG
import com.lambda.config.settings.CharSetting
import com.lambda.config.settings.comparable.DurationSetting
import com.lambda.config.settings.FunctionSetting
import com.lambda.config.settings.StringSetting
import com.lambda.config.settings.collections.ListSetting
Expand All @@ -35,10 +36,13 @@ import com.lambda.config.settings.numeric.*
import com.lambda.util.Communication.logError
import com.lambda.util.KeyCode
import com.lambda.util.Nameable
import com.lambda.util.extension.highestUnit
import net.minecraft.block.Block
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Vec3d
import java.awt.Color
import kotlin.time.Duration
import kotlin.time.toDuration

/**
* Represents a set of [AbstractSetting]s that are associated with the [name] of the [Configurable].
Expand Down Expand Up @@ -354,6 +358,36 @@ abstract class Configurable(
visibility: () -> Boolean = { true },
) = LongSetting(name, defaultValue, range, step, description, visibility, unit).register()

/**
* Creates a [DurationSetting] with the provided parameters and adds it to the [settings].
*
* The value of the setting is coerced into the specified [range] and rounded to the nearest [step].
*
* The unit of the duration is inferred automatically by [Duration.highestUnit]
*
* Example:
* ```kotlin
* val duration by setting("Duration", 10.microseconds, 420.nanoseconds..80.minutes, 69420.microseconds)
* ```
*
* @param name The unique identifier for the setting.
* @param defaultValue The default [Duration] value of the setting.
* @param range The range within which the setting's value must fall.
* @param step The step to which the setting's value is rounded.
* @param description A brief explanation of the setting's purpose and behavior.
* @param visibility A lambda expression that determines the visibility status of the setting.
*
* @return The created [Duration].
*/
fun setting(
name: String,
defaultValue: Duration,
range: ClosedRange<Duration>,
step: Duration = 1.toDuration(defaultValue.highestUnit),
description: String = "",
visibility: () -> Boolean = { true },
) = DurationSetting(name, defaultValue, range, step, description, visibility).register()

/**
* Creates a [KeyBindSetting] with the provided parameters and adds it to the [settings].
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@ class BuildSettings(
override val placeConfirmation by c.setting("Place Confirmation", true, "Wait for block placement confirmation") { vis() && page == Page.Place }
override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() && page == Page.Place }

override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeConfirmation || page == Page.Break && breakConfirmation) }
override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1,"Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeConfirmation || page == Page.Break && breakConfirmation) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2025 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.comparable

import com.google.gson.reflect.TypeToken
import com.lambda.config.AbstractSetting
import kotlin.reflect.KProperty
import kotlin.time.Duration

class DurationSetting(
override val name: String,
defaultValue: Duration,
val range: ClosedRange<Duration>,
val step: Duration,
description: String,
visibility: () -> Boolean,
) : AbstractSetting<Duration>(
defaultValue,
TypeToken.get(defaultValue::class.java).type,
description,
visibility,
) {
override fun toString() = value.toString()

override operator fun setValue(thisRef: Any?, property: KProperty<*>, valueIn: Duration) {
value = valueIn.coerceIn(range)
}
}
14 changes: 14 additions & 0 deletions common/src/main/kotlin/com/lambda/gui/GuiManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package com.lambda.gui
import com.lambda.config.settings.comparable.BooleanSetting
import com.lambda.config.settings.comparable.EnumSetting
import com.lambda.config.settings.FunctionSetting
import com.lambda.config.settings.comparable.DurationSetting
import com.lambda.config.settings.complex.ColorSetting
import com.lambda.config.settings.complex.KeyBindSetting
import com.lambda.config.settings.numeric.DoubleSetting
Expand All @@ -31,6 +32,7 @@ import com.lambda.gui.component.core.UIBuilder
import com.lambda.gui.component.layout.Layout
import com.lambda.gui.impl.clickgui.module.setting.settings.BooleanButton.Companion.booleanSetting
import com.lambda.gui.impl.clickgui.module.setting.settings.ColorPicker.Companion.colorPicker
import com.lambda.gui.impl.clickgui.module.setting.settings.DurationSlider.Companion.durationSlider
import com.lambda.gui.impl.clickgui.module.setting.settings.EnumSlider.Companion.enumSetting
import com.lambda.gui.impl.clickgui.module.setting.settings.KeybindPicker.Companion.keybindSetting
import com.lambda.gui.impl.clickgui.module.setting.settings.NumberSlider.Companion.numberSlider
Expand Down Expand Up @@ -115,6 +117,18 @@ object GuiManager : Loadable {
}
}

typeAdapter<DurationSetting> { owner, ref ->
owner.durationSlider(
ref.name,
ref.range.start,
ref.range.endInclusive,
ref.step,
ref::value
).apply {
visibility { ref.visibility() }
}
}

return "Loaded ${typeMap.size} gui type adapters."
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright 2025 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.gui.impl.clickgui.module.setting.settings

import com.lambda.gui.component.core.UIBuilder
import com.lambda.gui.component.layout.Layout
import com.lambda.gui.impl.clickgui.module.setting.SettingSlider
import com.lambda.util.math.MathUtils.roundToStep
import com.lambda.util.math.lerp
import com.lambda.util.math.transform
import kotlin.reflect.KMutableProperty0
import kotlin.time.Duration
import com.lambda.config.settings.comparable.DurationSetting
import com.lambda.util.extension.highestUnit
import kotlin.time.toDuration

class DurationSlider(
owner: Layout,
name: String,
min: Duration,
max: Duration,
step: Duration,
field: KMutableProperty0<Duration>,
) : SettingSlider<Duration>(owner, name, field) {
override val settingValue: String
get() = settingDelegate.toString()

private val unit = step.highestUnit

private val minNanos = min.toDouble(unit)
private val maxNanos = max.toDouble(unit)
private val stepNanos = step.toDouble(unit)

init {
// Slider logic
slider.progress {
transform(
settingDelegate.toDouble(unit),
minNanos,
maxNanos,
0.0,
1.0,
)
}

slider.onSlide {
settingDelegate = lerp(it, minNanos, maxNanos)
.roundToStep(stepNanos)
.toDuration(unit)
.coerceIn(min..max)
}
}

companion object {
/**
* Creates an [DurationSlider] - visual representation of the [DurationSetting]
*/
@UIBuilder
fun Layout.durationSlider(
name: String,
min: Duration,
max: Duration,
step: Duration,
field: KMutableProperty0<Duration>
) = DurationSlider(this, name, min, max, step, field).apply(children::add)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,16 @@ import dev.cbyrne.kdiscordipc.KDiscordIPC
import dev.cbyrne.kdiscordipc.core.packet.inbound.impl.AuthenticatePacket
import dev.cbyrne.kdiscordipc.data.activity.*
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds

object Discord : Module(
name = "Discord",
description = "Discord Rich Presence configuration",
defaultTags = setOf(ModuleTag.CLIENT),
//enabledByDefault = true, // ToDo: Bring this back on beta release
) {
private val delay by setting("Update Delay", 5000L, 5000L..30000L, 100L, unit = "ms")
private val delay by setting("Update Delay", 5.seconds, 5.seconds..30.seconds, 100.milliseconds)
private val showTime by setting("Show Time", true, description = "Show how long you have been playing for.")
private val line1Left by setting("Line 1 Left", LineInfo.WORLD)
private val line1Right by setting("Line 1 Right", LineInfo.USERNAME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ import net.minecraft.util.math.*
import kotlin.concurrent.fixedRateTimer
import kotlin.math.max
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.Duration.Companion.seconds

object CrystalAura : Module(
name = "CrystalAura",
Expand All @@ -74,9 +76,9 @@ object CrystalAura : Module(
private val placeDelay by setting("Place Delay", 50L, 0L..1000L, 1L, "Delay between placement attempts", " ms") { page == Page.General }
private val explodeDelay by setting("Explode Delay", 10L, 0L..1000L, 1L, "Delay between explosion attempts", " ms") { page == Page.General }
private val updateMode by setting("Update Mode", UpdateMode.Async) { page == Page.General }
private val updateDelaySetting by setting("Update Delay", 25L, 5L..200L, 5L, unit = " ms") { page == Page.General && updateMode == UpdateMode.Async }
private val updateDelaySetting by setting("Update Delay", 25.milliseconds, 5.milliseconds..200.milliseconds, 5.milliseconds) { page == Page.General && updateMode == UpdateMode.Async }
private val maxUpdatesPerFrame by setting("Max Updates Per Frame", 5, 1..20, 1) { page == Page.General && updateMode == UpdateMode.Async }
private val updateDelay get() = if (updateMode == UpdateMode.Async) updateDelaySetting else 0L
private val updateDelay get() = if (updateMode == UpdateMode.Async) updateDelaySetting else 0.nanoseconds
private val debug by setting("Debug", false) { page == Page.General }

/* Placement */
Expand Down Expand Up @@ -119,7 +121,7 @@ object CrystalAura : Module(
private val predictionTimer = Timer()
private var lastEntityId = 0

private val decay = LimitedDecayQueue<Int>(10000, 3000L)
private val decay = LimitedDecayQueue<Int>(10000, 3.seconds)

private val collidingOffsets = mutableListOf<BlockPos>().apply {
for (x in -1..1) {
Expand Down Expand Up @@ -293,7 +295,7 @@ object CrystalAura : Module(
}

private fun SafeContext.updateBlueprint(target: LivingEntity) =
updateTimer.runIfPassed(updateDelay.milliseconds) {
updateTimer.runIfPassed(updateDelay) {
resetBlueprint()

// Build damage info
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ import com.lambda.util.ClientPacket
import com.lambda.util.PacketUtils.handlePacketSilently
import com.lambda.util.PacketUtils.sendPacketSilently
import com.lambda.util.ServerPacket
import com.lambda.util.Timer
import kotlinx.coroutines.delay
import net.minecraft.network.listener.ClientPacketListener
import net.minecraft.network.listener.ServerPacketListener
import net.minecraft.network.packet.Packet
import net.minecraft.network.packet.c2s.common.KeepAliveC2SPacket
import java.util.concurrent.ConcurrentLinkedDeque
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds

object PacketDelay : Module(
name = "PacketDelay",
Expand All @@ -44,19 +45,18 @@ object PacketDelay : Module(
private val mode by setting("Mode", Mode.STATIC)
private val networkScope by setting("Network Scope", Direction.BOTH)
private val packetScope by setting("Packet Scope", PacketType.ANY)
private val inboundDelay by setting("Inbound Delay", 250L, 0L..5000L, 10L, unit = "ms") { networkScope != Direction.OUTBOUND }
private val outboundDelay by setting("Outbound Delay", 250L, 0L..5000L, 10L, unit = "ms") { networkScope != Direction.INBOUND }
private val inboundDelay by setting("Inbound Delay", 250.milliseconds, 1.milliseconds..5.seconds, 10.milliseconds) { networkScope != Direction.OUTBOUND }
private val outboundDelay by setting("Outbound Delay", 250.milliseconds, 1.milliseconds..5.seconds, 10.milliseconds) { networkScope != Direction.INBOUND }

private var outboundPool = ConcurrentLinkedDeque<ClientPacket>()
private var inboundPool = ConcurrentLinkedDeque<ServerPacket>()
private var outboundLastUpdate = 0L
private var inboundLastUpdate = 0L
private var outboundLastUpdate = Timer()
private var inboundLastUpdate = Timer()

init {
listen<RenderEvent.World> {
if (mode != Mode.STATIC) return@listen

flushPools(System.currentTimeMillis())
flushPools()
}

listen<PacketEvent.Send.Pre>(Int.MIN_VALUE) { event ->
Expand Down Expand Up @@ -104,29 +104,25 @@ object PacketDelay : Module(
}

onDisable {
flushPools(System.currentTimeMillis())
flushPools()
}
}

private fun SafeContext.flushPools(time: Long) {
if (time - outboundLastUpdate >= outboundDelay) {
private fun SafeContext.flushPools() {
outboundLastUpdate.runIfPassed(outboundDelay) {
while (outboundPool.isNotEmpty()) {
outboundPool.poll().let { packet ->
connection.sendPacketSilently(packet)
}
}

outboundLastUpdate = time
}

if (time - inboundLastUpdate >= inboundDelay) {
inboundLastUpdate.runIfPassed(inboundDelay) {
while (inboundPool.isNotEmpty()) {
inboundPool.poll().let { packet ->
connection.handlePacketSilently(packet)
}
}

inboundLastUpdate = time
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,20 @@ import com.lambda.util.collections.LimitedDecayQueue
import net.minecraft.network.packet.c2s.common.CommonPongC2SPacket
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.*
import net.minecraft.network.packet.c2s.play.TeleportConfirmC2SPacket
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds

// ToDo: HUD info
object PacketLimiter : Module(
name = "PacketLimiter",
description = "Limits the amount of packets sent to the server",
defaultTags = setOf(ModuleTag.NETWORK)
) {
private var packetQueue = LimitedDecayQueue<PacketEvent.Send.Pre>(99, 1000)
private var packetQueue = LimitedDecayQueue<PacketEvent.Send.Pre>(99, 1.seconds)
private val limit by setting("Limit", 99, 1..100, 1, "The maximum amount of packets to send per given time interval", unit = " packets")
.onValueChange { _, to -> packetQueue.setSizeLimit(to) }

private val interval by setting("Duration", 4000L, 1L..10000L, 50L, "The interval / duration in milliseconds to limit packets for", unit = " ms")
private val interval by setting("Duration", 4.seconds, 1.milliseconds..10.seconds, 50.milliseconds, "The interval / duration in milliseconds to limit packets for")
.onValueChange { _, to -> packetQueue.setDecayTime(to) }

private val defaultIgnorePackets = setOf(
Expand Down
3 changes: 2 additions & 1 deletion common/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import com.lambda.util.Formatting.string
import com.lambda.util.collections.LimitedDecayQueue
import com.lambda.util.extension.Structure
import com.lambda.util.extension.inventorySlots
import com.lambda.util.extension.ticks
import com.lambda.util.item.ItemUtils.block
import com.lambda.util.player.SlotUtils.hotbarAndStorage
import net.minecraft.entity.ItemEntity
Expand All @@ -70,7 +71,7 @@ class BuildTask @Ta5kBuilder constructor(
override val name: String get() = "Building $blueprint with ${(breaks / (age / 20.0 + 0.001)).string} b/s ${(placements / (age / 20.0 + 0.001)).string} p/s"

private val pendingInteractions = LimitedDecayQueue<BuildContext>(
build.maxPendingInteractions, build.interactionTimeout * 50L
build.maxPendingInteractions, build.interactionTimeout.ticks
) { info("${it::class.simpleName} at ${it.expectedPos.toShortString()} timed out") }
private var currentInteraction: BuildContext? = null
private val instantBreaks = mutableSetOf<BreakContext>()
Expand Down
Loading