diff --git a/src/main/kotlin/com/lambda/gui/dsl/ImGuiBuilder.kt b/src/main/kotlin/com/lambda/gui/dsl/ImGuiBuilder.kt index 143bd2fb9..8c0df75f1 100644 --- a/src/main/kotlin/com/lambda/gui/dsl/ImGuiBuilder.kt +++ b/src/main/kotlin/com/lambda/gui/dsl/ImGuiBuilder.kt @@ -39,15 +39,147 @@ package com.lambda.gui.dsl import com.lambda.gui.components.ClickGuiLayout import com.lambda.gui.dsl.ImGuiBuilder.text import com.lambda.util.math.Vec2d -import imgui.* -import imgui.ImGui.* -import imgui.flag.* -import imgui.type.* +import imgui.ImDrawList +import imgui.ImFont +import imgui.ImGui +import imgui.ImGui.begin +import imgui.ImGui.beginChild +import imgui.ImGui.beginCombo +import imgui.ImGui.beginDragDropSource +import imgui.ImGui.beginDragDropTarget +import imgui.ImGui.beginGroup +import imgui.ImGui.beginMainMenuBar +import imgui.ImGui.beginMenu +import imgui.ImGui.beginMenuBar +import imgui.ImGui.beginPopup +import imgui.ImGui.beginPopupContextItem +import imgui.ImGui.beginPopupContextVoid +import imgui.ImGui.beginPopupContextWindow +import imgui.ImGui.beginPopupModal +import imgui.ImGui.beginTabBar +import imgui.ImGui.beginTabItem +import imgui.ImGui.beginTooltip +import imgui.ImGui.calcTextSize +import imgui.ImGui.collapsingHeader +import imgui.ImGui.colorButton +import imgui.ImGui.colorEdit4 +import imgui.ImGui.colorPicker4 +import imgui.ImGui.dragFloat +import imgui.ImGui.dragInt +import imgui.ImGui.end +import imgui.ImGui.endChild +import imgui.ImGui.endCombo +import imgui.ImGui.endDragDropSource +import imgui.ImGui.endDragDropTarget +import imgui.ImGui.endGroup +import imgui.ImGui.endMainMenuBar +import imgui.ImGui.endMenu +import imgui.ImGui.endMenuBar +import imgui.ImGui.endPopup +import imgui.ImGui.endTabBar +import imgui.ImGui.endTabItem +import imgui.ImGui.endTooltip +import imgui.ImGui.getBackgroundDrawList +import imgui.ImGui.getColorU32 +import imgui.ImGui.getContentRegionAvail +import imgui.ImGui.getCursorPosX +import imgui.ImGui.getCursorPosY +import imgui.ImGui.getFont +import imgui.ImGui.getFontSize +import imgui.ImGui.getForegroundDrawList +import imgui.ImGui.getFrameHeight +import imgui.ImGui.getFrameHeightWithSpacing +import imgui.ImGui.getIO +import imgui.ImGui.getItemID +import imgui.ImGui.getItemRectMaxX +import imgui.ImGui.getItemRectMaxY +import imgui.ImGui.getItemRectMinX +import imgui.ImGui.getItemRectMinY +import imgui.ImGui.getStyle +import imgui.ImGui.getVersion +import imgui.ImGui.getWindowContentRegionMaxX +import imgui.ImGui.getWindowContentRegionMaxY +import imgui.ImGui.getWindowDrawList +import imgui.ImGui.getWindowHeight +import imgui.ImGui.getWindowPos +import imgui.ImGui.getWindowPosX +import imgui.ImGui.getWindowPosY +import imgui.ImGui.getWindowSize +import imgui.ImGui.getWindowViewport +import imgui.ImGui.getWindowWidth +import imgui.ImGui.image +import imgui.ImGui.inputDouble +import imgui.ImGui.inputFloat +import imgui.ImGui.inputFloat2 +import imgui.ImGui.inputFloat3 +import imgui.ImGui.inputFloat4 +import imgui.ImGui.isAnyItemActive +import imgui.ImGui.isAnyItemFocused +import imgui.ImGui.isAnyItemHovered +import imgui.ImGui.isItemActivated +import imgui.ImGui.isItemActive +import imgui.ImGui.isItemClicked +import imgui.ImGui.isItemDeactivated +import imgui.ImGui.isItemDeactivatedAfterEdit +import imgui.ImGui.isItemEdited +import imgui.ImGui.isItemFocused +import imgui.ImGui.isItemHovered +import imgui.ImGui.isItemToggledOpen +import imgui.ImGui.isMouseClicked +import imgui.ImGui.isWindowAppearing +import imgui.ImGui.isWindowCollapsed +import imgui.ImGui.isWindowHovered +import imgui.ImGui.plotHistogram +import imgui.ImGui.popFont +import imgui.ImGui.popID +import imgui.ImGui.popItemWidth +import imgui.ImGui.popStyleColor +import imgui.ImGui.popStyleVar +import imgui.ImGui.popTextWrapPos +import imgui.ImGui.pushFont +import imgui.ImGui.pushID +import imgui.ImGui.pushItemWidth +import imgui.ImGui.pushStyleColor +import imgui.ImGui.pushStyleVar +import imgui.ImGui.pushTextWrapPos +import imgui.ImGui.setClipboardText +import imgui.ImGui.setCursorPosX +import imgui.ImGui.setCursorPosY +import imgui.ImGui.sliderFloat +import imgui.ImGui.sliderInt +import imgui.ImGui.textColored +import imgui.ImGui.textUnformatted +import imgui.ImGui.treeNode +import imgui.ImGui.treePop +import imgui.ImGuiIO +import imgui.ImGuiStyle +import imgui.ImGuiTextFilter +import imgui.ImGuiViewport +import imgui.ImVec2 +import imgui.flag.ImDrawListFlags +import imgui.flag.ImGuiCol +import imgui.flag.ImGuiColorEditFlags +import imgui.flag.ImGuiComboFlags +import imgui.flag.ImGuiDir +import imgui.flag.ImGuiHoveredFlags +import imgui.flag.ImGuiInputTextFlags +import imgui.flag.ImGuiMouseButton +import imgui.flag.ImGuiPopupFlags +import imgui.flag.ImGuiSelectableFlags +import imgui.flag.ImGuiTabBarFlags +import imgui.flag.ImGuiTreeNodeFlags +import imgui.flag.ImGuiWindowFlags +import imgui.type.ImBoolean +import imgui.type.ImDouble +import imgui.type.ImFloat +import imgui.type.ImInt +import imgui.type.ImString import net.minecraft.util.math.Vec2f import net.minecraft.util.math.Vec3d import net.minecraft.util.math.Vec3i import java.awt.Color import kotlin.reflect.KMutableProperty0 +import imgui.ImGui.plotLines typealias ProcedureBlock = ImGuiBuilder.() -> Unit typealias WrappedBlock = ImGuiBuilder.(In) -> Out @@ -312,6 +444,18 @@ object ImGuiBuilder { @ImGuiDsl fun text(text: String) = textUnformatted(text) + /** + * Text with coloring + * + * @param text The text to display + */ + @ImGuiDsl + fun textColored(text: String, color: Color) { + val floats = floatArrayOf(0f, 0f, 0f, 0f) + val (r, g, b, a) = color.getColorComponents(floats) + textColored(r, g, b, a, text) + } + /** * Text with disabled coloring. * @@ -1171,7 +1315,7 @@ object ImGuiBuilder { */ @ImGuiDsl inline fun treeNode(label: String, id: String, block: ProcedureBlock) { - if (treeNode(label, id)) { + if (treeNode(id, label)) { block() treePop() } diff --git a/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt b/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt index 9a449a891..7c4e32e2f 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/context/BreakContext.kt @@ -21,10 +21,11 @@ import com.lambda.graphics.renderer.esp.DirectionMask import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.graphics.renderer.esp.ShapeBuilder import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.request.LogContext +import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder +import com.lambda.interaction.request.LogContext.Companion.getLogContextBuilder import com.lambda.interaction.request.breaking.BreakConfig -import com.lambda.interaction.request.breaking.BreakRequest import com.lambda.interaction.request.hotbar.HotbarManager -import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.rotating.RotationRequest import com.lambda.util.BlockUtils.emptyState import net.minecraft.block.BlockState @@ -42,7 +43,7 @@ data class BreakContext( var instantBreak: Boolean, override var cachedState: BlockState, val sortMode: BreakConfig.SortMode -) : BuildContext() { +) : BuildContext(), LogContext { private val baseColor = Color(222, 0, 0, 25) private val sideColor = Color(222, 0, 0, 100) @@ -76,10 +77,16 @@ data class BreakContext( box(blockPos, cachedState, baseColor, sideColor, DirectionMask.ALL.exclude(result.side)) } - fun requestSwap(breakRequest: BreakRequest, minKeepTicks: Int = 0): Boolean = - HotbarRequest( - hotbarIndex, - breakRequest.hotbar, - breakRequest.hotbar.keepTicks.coerceAtLeast(minKeepTicks) - ).submit(false).done -} + override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("Break Context") { + text(blockPos.getLogContextBuilder()) + text(result.getLogContextBuilder()) + text(rotation.getLogContextBuilder()) + value("Hotbar Index", hotbarIndex) + value("Instant Break", instantBreak) + value("Cached State", cachedState) + value("Expected State", expectedState) + value("Sort Mode", sortMode) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt b/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt index e61351a32..cce1f76a3 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/context/InteractionContext.kt @@ -19,6 +19,9 @@ package com.lambda.interaction.construction.context import com.lambda.graphics.renderer.esp.DirectionMask.mask import com.lambda.graphics.renderer.esp.ShapeBuilder +import com.lambda.interaction.request.LogContext +import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder +import com.lambda.interaction.request.LogContext.Companion.getLogContextBuilder import com.lambda.interaction.request.Request.Companion.submit import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest @@ -36,7 +39,7 @@ class InteractionContext( override var hotbarIndex: Int, override var cachedState: BlockState, override val expectedState: BlockState, -) : BuildContext() { +) : BuildContext(), LogContext { private val baseColor = Color(35, 254, 79, 25) private val sideColor = Color(35, 254, 79, 100) @@ -68,4 +71,15 @@ class InteractionContext( val validRotation = if (request.rotate) submit(rotation, false).done else true return hotbarRequest.done && validRotation } + + override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("Interaction Context") { + text(blockPos.getLogContextBuilder()) + text(result.getLogContextBuilder()) + text(rotation.getLogContextBuilder()) + value("Hotbar Index", hotbarIndex) + value("Cached State", cachedState) + value("Expected State", expectedState) + } + } } diff --git a/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt b/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt index 76689dab2..93a2bc185 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/context/PlaceContext.kt @@ -18,18 +18,17 @@ package com.lambda.interaction.construction.context import com.lambda.Lambda.mc -import com.lambda.context.SafeContext -import com.lambda.graphics.renderer.esp.DirectionMask -import com.lambda.graphics.renderer.esp.DirectionMask.exclude import com.lambda.graphics.renderer.esp.DirectionMask.mask import com.lambda.graphics.renderer.esp.ShapeBuilder +import com.lambda.interaction.request.LogContext +import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder +import com.lambda.interaction.request.LogContext.Companion.getLogContextBuilder import com.lambda.interaction.request.Request.Companion.submit import com.lambda.interaction.request.hotbar.HotbarManager import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.placing.PlaceRequest import com.lambda.interaction.request.rotating.RotationRequest import com.lambda.util.BlockUtils -import com.lambda.util.BlockUtils.blockState import net.minecraft.block.BlockState import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos @@ -45,7 +44,7 @@ data class PlaceContext( val sneak: Boolean, val insideBlock: Boolean, val currentDirIsValid: Boolean = false -) : BuildContext() { +) : BuildContext(), LogContext { private val baseColor = Color(35, 188, 254, 25) private val sideColor = Color(35, 188, 254, 100) @@ -83,4 +82,18 @@ data class PlaceContext( } else true return hotbarRequest.done && validRotation } -} + + override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("Place Context") { + text(blockPos.getLogContextBuilder()) + text(result.getLogContextBuilder()) + text(rotation.getLogContextBuilder()) + value("Hotbar Index", hotbarIndex) + value("Cached State", cachedState) + value("Expected State", expectedState) + value("Sneak", sneak) + value("Inside Block", insideBlock) + value("Current Dir Is Invalid", currentDirIsValid) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/request/DebugLogger.kt b/src/main/kotlin/com/lambda/interaction/request/DebugLogger.kt new file mode 100644 index 000000000..a745822a2 --- /dev/null +++ b/src/main/kotlin/com/lambda/interaction/request/DebugLogger.kt @@ -0,0 +1,140 @@ +/* + * 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 . + */ + +package com.lambda.interaction.request + +import com.lambda.gui.dsl.ImGuiBuilder +import com.lambda.interaction.request.LogContext.Companion.buildLogContext +import com.lambda.module.hud.ManagerDebugLoggers.autoScroll +import com.lambda.module.hud.ManagerDebugLoggers.maxLogEntries +import com.lambda.module.hud.ManagerDebugLoggers.showDebug +import com.lambda.module.hud.ManagerDebugLoggers.showError +import com.lambda.module.hud.ManagerDebugLoggers.showSuccess +import com.lambda.module.hud.ManagerDebugLoggers.showSystem +import com.lambda.module.hud.ManagerDebugLoggers.showWarning +import com.lambda.module.hud.ManagerDebugLoggers.wrapText +import com.lambda.util.math.a +import imgui.ImGui +import imgui.flag.ImGuiCol +import imgui.flag.ImGuiWindowFlags +import java.awt.Color +import java.util.* + +class DebugLogger( + val name: String +) { + val logs = LinkedList() + + private fun log(message: String, logColor: LogType, extraContext: List) { + if (logs.size + 1 > maxLogEntries) { + logs.removeFirst() + } + logs.add(LogEntry(message, logColor, extraContext.filterNotNull())) + } + + fun debug(message: String) = log(message, LogType.Debug, emptyList()) + fun debug(message: String, vararg extraContext: String?) = log(message, LogType.Debug, extraContext.toList()) + fun debug(message: String, vararg extraContext: LogContext?) = + log(message, LogType.Debug, extraContext.filterNotNull().map { buildLogContext(builder = it.getLogContextBuilder()) }) + fun success(message: String) = log(message, LogType.Success, emptyList()) + fun success(message: String, vararg extraContext: String?) = log(message, LogType.Success, extraContext.toList()) + fun success(message: String, vararg extraContext: LogContext?) = + log(message, LogType.Success, extraContext.filterNotNull().map { buildLogContext(builder = it.getLogContextBuilder()) }) + fun warning(message: String) = log(message, LogType.Warning, emptyList()) + fun warning(message: String, vararg extraContext: String?) = log(message, LogType.Warning, extraContext.toList()) + fun warning(message: String, vararg extraContext: LogContext?) = + log(message, LogType.Warning, extraContext.filterNotNull().map { buildLogContext(builder = it.getLogContextBuilder()) }) + fun error(message: String) = log(message, LogType.Error, emptyList()) + fun error(message: String, vararg extraContext: String?) = log(message, LogType.Error, extraContext.toList()) + fun error(message: String, vararg extraContext: LogContext?) = + log(message, LogType.Error, extraContext.filterNotNull().map { buildLogContext(builder = it.getLogContextBuilder()) }) + fun system(message: String) = log(message, LogType.System, emptyList()) + fun system(message: String, vararg extraContext: String?) = log(message, LogType.System, extraContext.toList()) + fun system(message: String, vararg extraContext: LogContext?) = + log(message, LogType.System, extraContext.filterNotNull().map { buildLogContext(builder = it.getLogContextBuilder()) }) + + fun ImGuiBuilder.buildLayout() { + ImGui.setNextWindowSizeConstraints(300f, 400f, windowViewport.workSizeX, windowViewport.workSizeY) + var flags = if (autoScroll) ImGuiWindowFlags.NoScrollbar or ImGuiWindowFlags.NoScrollWithMouse else 0 + flags = flags or ImGuiWindowFlags.NoBackground + child("Log Content", extraFlags = flags) { + if (wrapText) ImGui.pushTextWrapPos() + + logs.forEach { logEntry -> + if (shouldDisplay(logEntry)) { + val type = logEntry.type + val (logTypeStr, color) = when (type) { + LogType.Debug -> Pair("[DEBUG]", type.color) + LogType.Success -> Pair("[SUCCESS]", type.color) + LogType.Warning -> Pair("[WARNING]", type.color) + LogType.Error -> Pair("[ERROR]", type.color) + LogType.System -> Pair("[SYSTEM]", type.color) + } + + val floats = floatArrayOf(0f, 0f, 0f) + val (r, g, b) = color.getColorComponents(floats) + ImGui.pushStyleColor(ImGuiCol.Text, r, g, b, color.a.toFloat()) + if (logEntry.type == LogType.System) { + text("$logTypeStr ${logEntry.message}") + } else { + treeNode("$logTypeStr ${logEntry.message}", logEntry.uuid) { + logEntry.extraContext + .filterNotNull() + .forEach { + text(it) + } + } + } + ImGui.popStyleColor() + } + } + + if (wrapText) ImGui.popTextWrapPos() + + if (autoScroll) { + ImGui.setScrollHereY(1f) + } + } + } + + fun shouldDisplay(logEntry: LogEntry) = + when (logEntry.type) { + LogType.Debug -> showDebug + LogType.Success -> showSuccess + LogType.Warning -> showWarning + LogType.Error -> showError + LogType.System -> showSystem + } + + fun clear() = logs.clear() + + class LogEntry( + val message: String, + val type: LogType, + val extraContext: Collection + ) { + val uuid = UUID.randomUUID().toString() + } + + enum class LogType(val color: Color) { + Debug(Color(1.0f, 1.0f, 1.0f, 1f)), + Success(Color(0.28f, 1.0f, 0.28f, 1f)), + Warning(Color(1.0f, 1.0f, 0.28f, 1f)), + Error(Color(1.0f, 0.28f, 0.28f, 1f)), + System(Color(0.28f, 0.28f, 1.0f, 1f)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/request/LogContext.kt b/src/main/kotlin/com/lambda/interaction/request/LogContext.kt new file mode 100644 index 000000000..8e16e0d0a --- /dev/null +++ b/src/main/kotlin/com/lambda/interaction/request/LogContext.kt @@ -0,0 +1,104 @@ +/* + * 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 . + */ + +package com.lambda.interaction.request + +import net.minecraft.util.hit.BlockHitResult +import net.minecraft.util.math.BlockPos + +interface LogContext { + fun getLogContextBuilder(): LogContextBuilder.() -> Unit + + companion object { + @DslMarker + private annotation class LogContextDsl + + fun BlockPos.getLogContextBuilder(): LogContextBuilder.() -> Unit { + val pos = if (this is BlockPos.Mutable) toImmutable() else this + return { value("Block Pos", pos.toShortString()) } + } + + fun BlockHitResult.getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("Block Hit Result") { + value("Side", side) + text(blockPos.getLogContextBuilder()) + value("Pos", pos) + } + } + + @LogContextDsl + fun buildLogContext(tabMin: Int = 0, builder: LogContextBuilder.() -> Unit): String = + LogContextBuilder(tabMin).apply(builder).build() + + private fun LogContextBuilder.build() = logContext + + class LogContextBuilder(val tabMin: Int = 0) { + var logContext = "" + + private var tabs = tabMin + + @LogContextDsl + fun sameLine() { + val length = logContext.length + val first = logContext[length - 2] + val second = logContext[length - 1] + if (first != '\\' || second != 'n') throw IllegalStateException("String does not end in a new line") + logContext = logContext.removeRange(length - 2, length - 1) + } + + @LogContextDsl + fun text(text: String) { + repeat(tabs) { + logContext += "----" + } + logContext += "$text\n" + } + + fun text(builder: LogContextBuilder.() -> Unit) { + logContext += LogContextBuilder(tabs).apply(builder).build() + } + + @LogContextDsl + fun value(name: String, value: Any) { + text("$name: $value") + } + + @LogContextDsl + fun value(name: String, value: String) { + text("$name: $value") + } + + @LogContextDsl + fun group(name: String, builder: LogContextBuilder.() -> Unit) { + text("$name {") + logContext += LogContextBuilder(tabs + 1).apply(builder).build() + text("}") + } + + @LogContextDsl + fun pushTab() { + tabs++ + } + + @LogContextDsl + fun popTab() { + tabs-- + if (tabs < tabMin) throw IllegalStateException("Cannot reduce tabs beneath the minimum tab count") + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/request/Logger.kt b/src/main/kotlin/com/lambda/interaction/request/Logger.kt new file mode 100644 index 000000000..768595363 --- /dev/null +++ b/src/main/kotlin/com/lambda/interaction/request/Logger.kt @@ -0,0 +1,22 @@ +/* + * 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 . + */ + +package com.lambda.interaction.request + +interface Logger { + val logger: DebugLogger +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt b/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt index 805f18e91..a66048e28 100644 --- a/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt +++ b/src/main/kotlin/com/lambda/interaction/request/ManagerUtils.kt @@ -17,6 +17,7 @@ package com.lambda.interaction.request +import com.lambda.event.Event import com.lambda.util.reflections.getInstances import net.minecraft.util.math.BlockPos @@ -25,6 +26,14 @@ object ManagerUtils { val accumulatedManagerPriority = managers.map { it.stagePriority }.reduce { acc, priority -> acc + priority } val positionBlockingManagers = getInstances() + fun DebugLogger.newTick() = + system("------------- New Tick -------------") + + fun DebugLogger.newStage(tickStage: Event?) = + system("Tick stage ${tickStage?.run { this.toLogContext() }}") + + fun Event.toLogContext() = this::class.qualifiedName?.substringAfter("com.lambda.event.events.") + fun isPosBlocked(pos: BlockPos) = positionBlockingManagers.any { manager -> manager.blockedPositions.any { blocked -> blocked == pos } } } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/request/Request.kt b/src/main/kotlin/com/lambda/interaction/request/Request.kt index d2c64d42e..e3998af79 100644 --- a/src/main/kotlin/com/lambda/interaction/request/Request.kt +++ b/src/main/kotlin/com/lambda/interaction/request/Request.kt @@ -18,6 +18,7 @@ package com.lambda.interaction.request abstract class Request { + abstract val requestID: Int abstract val config: RequestConfig var fresh = true diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt index 14198b7eb..edd719096 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -19,6 +19,8 @@ package com.lambda.interaction.request.breaking import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.request.ActionInfo +import com.lambda.interaction.request.LogContext +import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Primary import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Rebreak import com.lambda.interaction.request.breaking.BreakInfo.BreakType.RedundantSecondary @@ -39,7 +41,7 @@ data class BreakInfo( override var context: BreakContext, var type: BreakType, var request: BreakRequest -) : ActionInfo { +) : ActionInfo, LogContext { // Delegates val breakConfig get() = request.build.breaking override val pendingInteractionsList get() = request.pendingInteractions @@ -147,4 +149,28 @@ data class BreakInfo( sequence ) } + + override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("Break Info") { + value("Type", type) + text(context.getLogContextBuilder()) + group("Details") { + value("Should Progress", shouldProgress) + value("Rebreak Potential", rebreakPotential) + text(swapInfo.getLogContextBuilder()) + value("Swap Stack", swapStack) + value("Updated This Tick", updatedThisTick) + value("Updated Pre-Processing This Tick", updatedPreProcessingThisTick) + value("Progressed This Tick", progressedThisTick) + value("Breaking", breaking) + value("Abandoned", abandoned) + value("Breaking Ticks", breakingTicks) + value("Sounds Cooldown", soundsCooldown) + value("Vanilla Instant Breakable", vanillaInstantBreakable) + value("Rebreakable", rebreakable) + } + } + } + + override fun toString() = "$type, ${context.cachedState}, ${context.blockPos}" } diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 66e2a796e..d48949213 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -36,7 +36,10 @@ import com.lambda.interaction.construction.simulation.BuildSimulator.simulate import com.lambda.interaction.construction.verify.TargetState import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.StackSelection.Companion.select +import com.lambda.interaction.request.Logger import com.lambda.interaction.request.ManagerUtils.isPosBlocked +import com.lambda.interaction.request.ManagerUtils.newStage +import com.lambda.interaction.request.ManagerUtils.newTick import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode @@ -45,6 +48,7 @@ import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Primary import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Rebreak import com.lambda.interaction.request.breaking.BreakInfo.BreakType.RedundantSecondary import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Secondary +import com.lambda.interaction.request.breaking.BreakManager.activeInfos import com.lambda.interaction.request.breaking.BreakManager.activeRequest import com.lambda.interaction.request.breaking.BreakManager.breakInfos import com.lambda.interaction.request.breaking.BreakManager.breaks @@ -65,6 +69,7 @@ import com.lambda.interaction.request.hotbar.HotbarRequest import com.lambda.interaction.request.interacting.InteractionManager import com.lambda.interaction.request.placing.PlaceManager import com.lambda.interaction.request.rotating.RotationRequest +import com.lambda.module.hud.ManagerDebugLoggers.breakManagerLogger import com.lambda.threading.runSafe import com.lambda.util.BlockUtils.blockState import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta @@ -95,9 +100,14 @@ object BreakManager : RequestHandler( TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post, - onOpen = { processRequest(activeRequest); simulateAbandoned() }, + onOpen = { + if (activeInfos.isNotEmpty() || breaks.isNotEmpty()) + BreakManager.logger.newStage(BreakManager.tickStage) + processRequest(activeRequest) + simulateAbandoned() + }, onClose = { checkForCancels() } -), PositionBlocking { +), PositionBlocking, Logger { private val breakInfos = arrayOfNulls(2) private val activeInfos @@ -156,9 +166,16 @@ object BreakManager : RequestHandler( field = value } + override val logger = breakManagerLogger + override fun load(): String { super.load() + listen(priority = Int.MAX_VALUE) { + if (activeInfos.isEmpty() && breaks.isEmpty()) return@listen + logger.newTick() + } + listen(priority = Int.MIN_VALUE) { if (!swappedThisTick) { currentStack = player.mainHandStack @@ -300,6 +317,7 @@ object BreakManager : RequestHandler( */ private fun SafeContext.processRequest(breakRequest: BreakRequest?) { breakRequest?.let { request -> + logger.debug("Processing request", breakRequest) if (request.fresh) populateFrom(request) } @@ -332,8 +350,10 @@ object BreakManager : RequestHandler( if (noNew && noProgression) break } - if (breaks.isEmpty()) activeRequest = null - + if (breaks.isEmpty()) { + if (activeRequest != null) logger.debug("Clearing active request", activeRequest) + activeRequest = null + } if (breaksThisTick > 0 || activeInfos.isNotEmpty()) { activeThisTick = true } @@ -348,6 +368,8 @@ object BreakManager : RequestHandler( * @see canAccept */ private fun SafeContext.populateFrom(request: BreakRequest) { + logger.debug("Populating from request", request) + // Sanitize the new breaks val newBreaks = request.contexts .distinctBy { it.blockPos } @@ -359,6 +381,7 @@ object BreakManager : RequestHandler( .forEach { info -> newBreaks.find { ctx -> ctx.blockPos == info.context.blockPos && canAccept(ctx) }?.let { ctx -> if ((!info.updatedThisTick || info.type == RedundantSecondary) || info.abandoned) { + logger.debug("Updating info", info, ctx) if (info.type == RedundantSecondary) info.request.onStart?.invoke(info.context.blockPos) else if (info.abandoned) { @@ -378,6 +401,8 @@ object BreakManager : RequestHandler( .sortedByDescending { it.instantBreak } .toMutableList() + logger.debug("${breaks.size} unprocessed breaks") + val breakConfig = request.config val pendingLimit = (breakConfig.maxPendingBreaks - pendingBreakCount).coerceAtLeast(0) maxBreaksThisTick = breakConfig.breaksPerTick.coerceAtMost(pendingLimit) @@ -405,6 +430,7 @@ object BreakManager : RequestHandler( rotationRequest = infos.firstOrNull { info -> info.breakConfig.rotateForBreak } ?.let { info -> val rotation = info.context.rotation + logger.debug("Requesting rotation", rotation) rotation.submit(false) } @@ -422,7 +448,9 @@ object BreakManager : RequestHandler( request.hotbar.keepTicks.coerceAtLeast(minSwapTicks) ).submit(false) } + logger.debug("Submitted hotbar request", hotbarRequest) if (!hotbarRequest.done) { + logger.warning("Hotbar request failed", hotbarRequest) return false } if (minSwapTicks > 0) { @@ -479,6 +507,7 @@ object BreakManager : RequestHandler( if (!primaryInfo.breaking) return null + logger.debug("Transforming primary to secondary", primaryInfo) secondaryBreak = primaryInfo.apply { type = Secondary } secondaryBreak?.stopBreakPacket(world, interaction) return@let @@ -486,6 +515,7 @@ object BreakManager : RequestHandler( primaryBreak = breakInfo setPendingConfigs(request.build) + logger.success("Initialized break info", breakInfo) return primaryBreak } @@ -569,6 +599,8 @@ object BreakManager : RequestHandler( } private fun BreakInfo.updatePreProcessing(player: ClientPlayerEntity, world: BlockView) { + logger.debug("Updating pre-processing", this) + shouldProgress = !progressedThisTick && tickStage in breakConfig.breakStageMask && (rotated || type != Primary) @@ -596,6 +628,7 @@ object BreakManager : RequestHandler( if (type == RedundantSecondary || abandoned) return@runSafe when (type) { Primary -> { + logger.warning("Cancelling break", this@cancelBreak) nullify() setBreakingTextureStage(player, world, -1) abortBreakPacket(world, interaction) @@ -603,10 +636,12 @@ object BreakManager : RequestHandler( } Secondary -> { if (breakConfig.unsafeCancels) { + logger.warning("Making break redundant", this@cancelBreak) type = RedundantSecondary setBreakingTextureStage(player, world, -1) request.onCancel?.invoke(context.blockPos) } else { + logger.warning("Abandoning break", this@cancelBreak) abandoned = true } } @@ -666,12 +701,14 @@ object BreakManager : RequestHandler( if (blockState.isEmpty) { info.nullify() info.request.onCancel?.invoke(ctx.blockPos) + logger.warning("Block state was unexpectedly empty", info) return false } info.breakingTicks++ val breakDelta = blockState.calcBreakDelta(player, world, ctx.blockPos, config) val progress = breakDelta * (info.breakingTicks - config.fudgeFactor) + logger.debug("${info.type} progress: $progress, breaking ticks: ${info.breakingTicks}", info) if (config.sounds) { if (info.soundsCooldown % 4.0f == 0.0f) { @@ -700,6 +737,7 @@ object BreakManager : RequestHandler( val swing = config.swing if (progress >= info.getBreakThreshold() && info.swapInfo.validSwap) { + logger.success("Breaking", info) if (info.type == Primary) { onBlockBreak(info) info.stopBreakPacket(world, interaction) @@ -726,8 +764,10 @@ object BreakManager : RequestHandler( val ctx = info.context if (info.rebreakPotential.isPossible()) { + logger.debug("Handling potential rebreak") when (val rebreakResult = RebreakHandler.handleUpdate(info.context, info.request)) { is RebreakResult.StillBreaking -> { + logger.debug("Rebreak not complete", info) primaryBreak = rebreakResult.breakInfo.apply { type = Primary RebreakHandler.clearRebreak() @@ -741,6 +781,7 @@ object BreakManager : RequestHandler( return true } is RebreakResult.Rebroke -> { + logger.debug("Rebroke", info) info.type = Rebreak info.nullify() info.request.onReBreak?.invoke(ctx.blockPos) @@ -777,9 +818,11 @@ object BreakManager : RequestHandler( info.vanillaInstantBreakable = progress >= 1 && info.swapInfo.validSwap if (instantBreakable) { + logger.success("Instant breaking", info) onBlockBreak(info) if (!info.vanillaInstantBreakable) breakCooldown = info.breakConfig.breakDelay } else { + logger.debug("Starting break", info) info.apply { breaking = true breakingTicks = 1 diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt index 3fe4abfe9..4c07843c6 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakRequest.kt @@ -21,6 +21,8 @@ import com.lambda.config.groups.BuildConfig import com.lambda.config.groups.InteractionConfig import com.lambda.interaction.construction.context.BreakContext import com.lambda.interaction.construction.context.BuildContext +import com.lambda.interaction.request.LogContext +import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder import com.lambda.interaction.request.Request import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.inventory.InventoryConfig @@ -40,7 +42,9 @@ data class BreakRequest( val rotation: RotationConfig = TaskFlowModule.rotation, val inventory: InventoryConfig = TaskFlowModule.inventory, val interact: InteractionConfig = TaskFlowModule.interaction -) : Request() { +) : Request(), LogContext { + override val requestID = ++requestCount + override val config = build.breaking var onStart: ((BlockPos) -> Unit)? = null var onUpdate: ((BlockPos) -> Unit)? = null @@ -56,6 +60,22 @@ data class BreakRequest( override fun submit(queueIfClosed: Boolean) = BreakManager.request(this, queueIfClosed) + override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("Break Request") { + value("Request ID", requestID) + value("Contexts", contexts.size) + group("Callbacks") { + value("onStart", onStart != null) + value("onUpdate", onUpdate != null) + value("onStop", onStop != null) + value("onCancel", onCancel != null) + value("onItemDrop", onItemDrop != null) + value("onReBreakStart", onReBreakStart != null) + value("onReBreak", onReBreak != null) + } + } + } + @DslMarker annotation class BreakRequestBuilder @@ -111,6 +131,8 @@ data class BreakRequest( } companion object { + var requestCount = 0 + @BreakRequestBuilder fun breakRequest( contexts: Collection, diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt index 36f3fdefb..859195d6b 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BrokenBlockHandler.kt @@ -58,9 +58,13 @@ object BrokenBlockHandler : PostActionHandler() { if (!loaded) return@runSafe if (!info.broken) { - warn("${info.type} ${info::class.simpleName} at ${info.context.blockPos.toShortString()} timed out with cached state ${info.context.cachedState}") + val message = "${info.type} ${info::class.simpleName} at ${info.context.blockPos.toShortString()} timed out with cached state ${info.context.cachedState}" + BreakManager.logger.error(message) + warn(message) } else if (!TaskFlowModule.ignoreItemDropWarnings) { - warn("${info.type} ${info::class.simpleName}'s item drop at ${info.context.blockPos.toShortString()} timed out") + val message = "${info.type} ${info::class.simpleName}'s item drop at ${info.context.blockPos.toShortString()} timed out" + BreakManager.logger.warn(message) + warn(message) } if (!info.broken && info.breakConfig.breakConfirmation != BreakConfirmationMode.AwaitThenBreak) { @@ -90,7 +94,9 @@ object BrokenBlockHandler : PostActionHandler() { if (pending.type == BreakInfo.BreakType.Rebreak) { pending.context.cachedState = event.newState } else { - this@BrokenBlockHandler.warn("Broken block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.cachedState.emptyState}") + val message = "Broken block at ${event.pos.toShortString()} was rejected with ${event.newState} instead of ${pending.context.cachedState.emptyState}" + BreakManager.logger.error(message) + this@BrokenBlockHandler.warn(message) pending.stopPending() } return@listen diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt index 3c686d47b..5a3236fa7 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/SwapInfo.kt @@ -18,6 +18,8 @@ package com.lambda.interaction.request.breaking import com.lambda.Lambda.mc +import com.lambda.interaction.request.LogContext +import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Primary import com.lambda.interaction.request.breaking.BreakInfo.BreakType.Rebreak import com.lambda.interaction.request.breaking.BreakManager.calcBreakDelta @@ -32,7 +34,7 @@ data class SwapInfo( private val breakConfig: BreakConfig = TaskFlowModule.build.breaking, val swap: Boolean = false, val minKeepTicks: Int = 0, -) { +) : LogContext { val validSwap get() = run { val serverSwapTicks = if (type == Primary || type == Rebreak) breakConfig.serverSwapTicks @@ -41,6 +43,15 @@ data class SwapInfo( (mc.player?.mainHandStack?.heldTicks ?: return false) >= serverSwapTicks } + override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("Swap Info") { + value("Type", type) + value("Swap", swap) + value("Min Keep Ticks", minKeepTicks) + value("Valid Swap", validSwap) + } + } + companion object { val EMPTY = SwapInfo(Primary) diff --git a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt index d27f73bd5..b1b14446e 100644 --- a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarManager.kt @@ -23,8 +23,12 @@ import com.lambda.event.EventFlow.post import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.request.Logger +import com.lambda.interaction.request.ManagerUtils.newStage +import com.lambda.interaction.request.ManagerUtils.newTick import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.hotbar.HotbarManager.checkResetSwap +import com.lambda.module.hud.ManagerDebugLoggers.hotbarManagerLogger import com.lambda.threading.runSafe object HotbarManager : RequestHandler( @@ -33,8 +37,9 @@ object HotbarManager : RequestHandler( TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post, + onOpen = { if (HotbarManager.activeRequest != null) HotbarManager.logger.newStage(HotbarManager.tickStage) }, onClose = { checkResetSwap() } -) { +), Logger { val serverSlot get() = runSafe { interaction.lastSelectedSlot } ?: 0 @@ -45,9 +50,16 @@ object HotbarManager : RequestHandler( var activeRequest: HotbarRequest? = null + override val logger = hotbarManagerLogger + override fun load(): String { super.load() + listen(priority = Int.MAX_VALUE) { + if (activeRequest != null) + logger.newTick() + } + listen(priority = Int.MIN_VALUE) { swapsThisTick = 0 if (swapDelay > 0) swapDelay-- @@ -62,6 +74,8 @@ object HotbarManager : RequestHandler( } override fun SafeContext.handleRequest(request: HotbarRequest) { + logger.debug("Handling request:", request) + maxSwapsThisTick = request.swapsPerTick swapDelay = swapDelay.coerceAtMost(request.swapDelay) @@ -73,6 +87,7 @@ object HotbarManager : RequestHandler( if (sameButLonger) activeRequest?.let { current -> request.swapPauseAge = current.swapPauseAge + logger.debug("Request is the same as current, but longer or the same keep time", request) } else run swap@{ if (request.slot != activeRequest?.slot) { if (swapsThisTick + 1 > maxSwapsThisTick || swapDelay > 0) return @@ -93,6 +108,7 @@ object HotbarManager : RequestHandler( } activeRequest = request + logger.success("Set active request", request) interaction.syncSelectedSlot() return } @@ -101,6 +117,7 @@ object HotbarManager : RequestHandler( activeRequest?.let { active -> val canStopSwap = swapsThisTick < maxSwapsThisTick if (active.keepTicks <= 0 && tickStage in active.sequenceStageMask && canStopSwap) { + logger.debug("Clearing request and syncing slot", activeRequest) activeRequest = null interaction.syncSelectedSlot() } diff --git a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt index 6ef88613a..e17cc6e7f 100644 --- a/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarRequest.kt @@ -17,6 +17,8 @@ package com.lambda.interaction.request.hotbar +import com.lambda.interaction.request.LogContext +import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder import com.lambda.interaction.request.Request class HotbarRequest( @@ -24,7 +26,9 @@ class HotbarRequest( override val config: HotbarConfig, override var keepTicks: Int = config.keepTicks, override var swapPause: Int = config.swapPause -) : Request(), HotbarConfig by config { +) : Request(), HotbarConfig by config, LogContext { + override val requestID = ++requestCount + var activeRequestAge = 0 var swapPauseAge = 0 @@ -37,4 +41,19 @@ class HotbarRequest( override fun submit(queueIfClosed: Boolean) = HotbarManager.request(this, queueIfClosed) + + override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("Hotbar Request") { + value("Request ID", requestID) + value("Slot", slot) + value("Keep Ticks", keepTicks) + value("Swap Pause", swapPause) + value("Swap Pause Age", swapPauseAge) + value("Active Request Age", activeRequestAge) + } + } + + companion object { + var requestCount = 0 + } } diff --git a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractRequest.kt b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractRequest.kt index d544c1aa9..cf8b6796f 100644 --- a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractRequest.kt @@ -21,6 +21,8 @@ import com.lambda.Lambda.mc import com.lambda.config.groups.BuildConfig import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.context.InteractionContext +import com.lambda.interaction.request.LogContext +import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder import com.lambda.interaction.request.Request import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.rotating.RotationConfig @@ -35,10 +37,23 @@ data class InteractRequest( val build: BuildConfig, val hotbar: HotbarConfig, val rotation: RotationConfig -) : Request(), InteractConfig by config { +) : Request(), InteractConfig by config, LogContext { + override val requestID = ++requestCount + override val done: Boolean get() = contexts.all { mc.world?.getBlockState(it.blockPos)?.matches(it.expectedState) == true } override fun submit(queueIfClosed: Boolean) = InteractionManager.request(this, queueIfClosed) + + override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("Interact Request") { + value("Request ID", requestID) + value("Contexts", contexts.size) + } + } + + companion object { + var requestCount = 0 + } } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt index da14884a7..9c84fae7b 100644 --- a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionInfo.kt @@ -20,9 +20,17 @@ package com.lambda.interaction.request.interacting import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.context.InteractionContext import com.lambda.interaction.request.ActionInfo +import com.lambda.interaction.request.LogContext +import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder data class InteractionInfo( override val context: InteractionContext, override val pendingInteractionsList: MutableCollection, private val config: InteractConfig -) : ActionInfo, InteractConfig by config \ No newline at end of file +) : ActionInfo, InteractConfig by config, LogContext { + override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("Interaction Info") { + text(context.getLogContextBuilder()) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt index c83fc99e5..973623569 100644 --- a/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/interacting/InteractionManager.kt @@ -25,16 +25,19 @@ import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.InteractionContext +import com.lambda.interaction.request.Logger import com.lambda.interaction.request.ManagerUtils.isPosBlocked +import com.lambda.interaction.request.ManagerUtils.newStage +import com.lambda.interaction.request.ManagerUtils.newTick import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager import com.lambda.interaction.request.interacting.InteractedBlockHandler.pendingActions import com.lambda.interaction.request.interacting.InteractedBlockHandler.setPendingConfigs import com.lambda.interaction.request.interacting.InteractedBlockHandler.startPending -import com.lambda.interaction.request.interacting.InteractionManager.activeRequest import com.lambda.interaction.request.interacting.InteractionManager.processRequest import com.lambda.interaction.request.placing.PlaceManager +import com.lambda.module.hud.ManagerDebugLoggers.interactionManagerLogger import com.lambda.util.player.MovementUtils.sneaking import com.lambda.util.player.swingHand import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket @@ -46,8 +49,12 @@ object InteractionManager : RequestHandler( TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post, - onOpen = { activeRequest?.let { processRequest(it) } } -), PositionBlocking { + onOpen = { + if (InteractionManager.potentialInteractions.isNotEmpty()) + InteractionManager.logger.newStage(InteractionManager.tickStage) + InteractionManager.activeRequest?.let { processRequest(it) } + } +), PositionBlocking, Logger { private var activeRequest: InteractRequest? = null private var potentialInteractions = mutableListOf() @@ -57,9 +64,16 @@ object InteractionManager : RequestHandler( override val blockedPositions get() = pendingActions.map { it.context.blockPos } + override val logger = interactionManagerLogger + override fun load(): String { super.load() + listen(priority = Int.MAX_VALUE) { + if (potentialInteractions.isNotEmpty()) + logger.newTick() + } + listen(priority = Int.MIN_VALUE) { activeRequest = null interactionsThisTick = 0 @@ -84,6 +98,8 @@ object InteractionManager : RequestHandler( } fun SafeContext.processRequest(request: InteractRequest) { + logger.debug("Processing request", request) + if (request.fresh) populateFrom(request) if (player.isSneaking) return @@ -93,7 +109,10 @@ object InteractionManager : RequestHandler( if (interactionsThisTick + 1 > maxInteractionsThisTick) break val ctx = iterator.next() - if (!ctx.requestDependencies(request)) return + if (!ctx.requestDependencies(request)) { + logger.warning("Dependencies failed for interaction", ctx, request) + return + } if (request.interactConfirmationMode != InteractionConfig.InteractConfirmationMode.None) { InteractionInfo(ctx, request.pendingInteractionsList, request).startPending() @@ -111,15 +130,19 @@ object InteractionManager : RequestHandler( request.onInteract?.invoke(ctx.blockPos) interactionsThisTick++ iterator.remove() + logger.success("interacted with ${ctx.cachedState} at ${ctx.blockPos}, changing to ${ctx.expectedState}", ctx, request) } } private fun populateFrom(request: InteractRequest) { + logger.debug("Populating from request", request) setPendingConfigs(request.build) potentialInteractions = request.contexts .filter { !isPosBlocked(it.blockPos) } .toMutableList() + logger.debug("${potentialInteractions.size} potential interactions") + val pendingLimit = (request.build.maxPendingInteractions - pendingActions.size).coerceAtLeast(0) maxInteractionsThisTick = (request.build.interactionsPerTick.coerceAtMost(pendingLimit)) } diff --git a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt index 4c86403e9..ba2e21133 100644 --- a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryManager.kt @@ -21,10 +21,12 @@ import com.lambda.context.SafeContext import com.lambda.event.EventFlow.post import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent +import com.lambda.interaction.request.Logger import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.inventory.InventoryManager.activeRequest import com.lambda.interaction.request.inventory.InventoryManager.processRequest +import com.lambda.module.hud.ManagerDebugLoggers.inventoryManagerLogger object InventoryManager : RequestHandler( 1, @@ -33,9 +35,11 @@ object InventoryManager : RequestHandler( TickEvent.Input.Post, TickEvent.Player.Post, onOpen = { activeRequest?.let { processRequest(it) } } -) { +), Logger { var activeRequest: InventoryRequest? = null + override val logger = inventoryManagerLogger + override fun load(): String { super.load() diff --git a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt index 9b073b726..a8589b1e2 100644 --- a/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/inventory/InventoryRequest.kt @@ -17,14 +17,28 @@ package com.lambda.interaction.request.inventory +import com.lambda.interaction.request.LogContext +import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder import com.lambda.interaction.request.Request class InventoryRequest( override val config: InventoryConfig -) : Request(), InventoryConfig by config { +) : Request(), InventoryConfig by config, LogContext { + override val requestID = ++requestCount + override val done: Boolean get() = TODO("Not yet implemented") override fun submit(queueIfClosed: Boolean) = InventoryManager.request(this, queueIfClosed) + + override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("Inventory Request") { + value("Request ID", requestID) + } + } + + companion object { + var requestCount = 0 + } } diff --git a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt index dbb51800e..1335e2a59 100644 --- a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceInfo.kt @@ -20,6 +20,8 @@ package com.lambda.interaction.request.placing import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.context.PlaceContext import com.lambda.interaction.request.ActionInfo +import com.lambda.interaction.request.LogContext +import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder import net.minecraft.util.math.BlockPos data class PlaceInfo( @@ -27,4 +29,13 @@ data class PlaceInfo( override val pendingInteractionsList: MutableCollection, val onPlace: ((BlockPos) -> Unit)?, val placeConfig: PlaceConfig -) : ActionInfo \ No newline at end of file +) : ActionInfo, LogContext { + override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("Place Info") { + text(context.getLogContextBuilder()) + group("Callbacks") { + value("onPlace", onPlace != null) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt index 50d123bfa..967439b8f 100644 --- a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt @@ -25,7 +25,10 @@ import com.lambda.event.events.TickEvent import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.interaction.construction.context.PlaceContext +import com.lambda.interaction.request.Logger import com.lambda.interaction.request.ManagerUtils.isPosBlocked +import com.lambda.interaction.request.ManagerUtils.newStage +import com.lambda.interaction.request.ManagerUtils.newTick import com.lambda.interaction.request.PositionBlocking import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.breaking.BreakManager @@ -39,6 +42,7 @@ import com.lambda.interaction.request.placing.PlaceManager.processRequest import com.lambda.interaction.request.placing.PlacedBlockHandler.pendingActions import com.lambda.interaction.request.placing.PlacedBlockHandler.setPendingConfigs import com.lambda.interaction.request.placing.PlacedBlockHandler.startPending +import com.lambda.module.hud.ManagerDebugLoggers.placeManagerLogger import com.lambda.util.BlockUtils.blockState import com.lambda.util.Communication.warn import com.lambda.util.player.MovementUtils.sneaking @@ -68,8 +72,12 @@ object PlaceManager : RequestHandler( TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post, - onOpen = { activeRequest?.let { processRequest(it) } } -), PositionBlocking { + onOpen = { + if (potentialPlacements.isNotEmpty()) + PlaceManager.logger.newStage(PlaceManager.tickStage) + activeRequest?.let { processRequest(it) } + } +), PositionBlocking, Logger { private var activeRequest: PlaceRequest? = null private var potentialPlacements = mutableListOf() @@ -83,9 +91,16 @@ object PlaceManager : RequestHandler( override val blockedPositions get() = pendingActions.map { it.context.blockPos } + override val logger = placeManagerLogger + override fun load(): String { super.load() + listen(priority = Int.MAX_VALUE) { + if (potentialPlacements.isNotEmpty()) + logger.newTick() + } + listen(priority = Int.MIN_VALUE) { activeRequest = null placementsThisTick = 0 @@ -127,6 +142,8 @@ object PlaceManager : RequestHandler( * @see placeBlock */ fun SafeContext.processRequest(request: PlaceRequest) { + logger.debug("Processing request", request) + if (request.fresh) populateFrom(request) val iterator = potentialPlacements.iterator() @@ -135,15 +152,27 @@ object PlaceManager : RequestHandler( val ctx = iterator.next() if (ctx.sneak) shouldSneak = true - if (!ctx.requestDependencies(request) || !validSneak(player)) return -// if (tickStage !in request.build.placing.placeStageMask) return + if (!ctx.requestDependencies(request)) { + logger.warning("Dependencies failed for context", ctx, request) + return + } + if (!validSneak(player)) return + // if (tickStage !in request.build.placing.placeStageMask) return val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND) - if (!actionResult.isAccepted) warn("Placement interaction failed with $actionResult") + if (!actionResult.isAccepted) { + logger.warning("Placement interaction failed with $actionResult", ctx, request) + warn("Placement interaction failed with $actionResult") + } placementsThisTick++ iterator.remove() } - if (potentialPlacements.isEmpty()) activeRequest = null + if (potentialPlacements.isEmpty()) { + if (activeRequest != null) { + logger.debug("Clearing active request", activeRequest) + activeRequest = null + } + } } /** @@ -153,10 +182,12 @@ object PlaceManager : RequestHandler( * @see isPosBlocked */ private fun populateFrom(request: PlaceRequest) { + logger.debug("Populating from request", request) setPendingConfigs(request.build) potentialPlacements = request.contexts .filter { !isPosBlocked(it.blockPos) } .toMutableList() + logger.debug("${potentialPlacements.size} potential placements") val pendingLimit = (request.maxPendingPlacements - pendingActions.size).coerceAtLeast(0) maxPlacementsThisTick = (request.placementsPerTick.coerceAtMost(pendingLimit)) @@ -170,8 +201,14 @@ object PlaceManager : RequestHandler( private fun SafeContext.placeBlock(placeContext: PlaceContext, request: PlaceRequest, hand: Hand): ActionResult { interaction.syncSelectedSlot() val hitResult = placeContext.result - if (!world.worldBorder.contains(hitResult.blockPos)) return ActionResult.FAIL - if (gamemode == GameMode.SPECTATOR) return ActionResult.PASS + if (!world.worldBorder.contains(hitResult.blockPos)) { + logger.error("Placement position outside the world border", placeContext, request) + return ActionResult.FAIL + } + if (gamemode == GameMode.SPECTATOR) { + logger.error("Player is in spectator mode", placeContext, request) + return ActionResult.PASS + } return interactBlockInternal(placeContext, request, request.build.placing, hand, hitResult) } @@ -192,11 +229,13 @@ object PlaceManager : RequestHandler( if (!cantInteract) { val blockState = blockState(hitResult.blockPos) if (!connection.hasFeature(blockState.block.requiredFeatures)) { + logger.error("Required features not met for $blockState", placeContext, request) return ActionResult.FAIL } val actionResult = blockState.onUse(world, player, hitResult) if (actionResult.isAccepted) { + logger.error("Block state ($blockState) onUse not accepted", placeContext, request) return actionResult } } @@ -235,9 +274,15 @@ object PlaceManager : RequestHandler( val cantModifyWorld = !player.abilities.allowModifyWorld val cantPlaceOn = !itemStack.canPlaceOn(cachedBlockPosition) - if (cantModifyWorld && cantPlaceOn) return ActionResult.PASS + if (cantModifyWorld && cantPlaceOn) { + logger.error("Cannot modify world", placeContext, request) + return ActionResult.PASS + } - val item = (itemStack.item as? BlockItem) ?: return ActionResult.PASS + val item = (itemStack.item as? BlockItem) ?: run { + logger.error("Item ${itemStack.item.name} is not a block item", placeContext, request) + return ActionResult.PASS + } return place(placeContext, request, hand, hitResult, placeConfig, item, ItemPlacementContext(context)) } @@ -256,11 +301,23 @@ object PlaceManager : RequestHandler( item: BlockItem, context: ItemPlacementContext ): ActionResult { - if (!item.block.isEnabled(world.enabledFeatures)) return ActionResult.FAIL - if (!context.canPlace()) return ActionResult.FAIL + if (!item.block.isEnabled(world.enabledFeatures)) { + logger.error("Block ${item.block.name} is not enabled", placeContext, request) + return ActionResult.FAIL + } + if (!context.canPlace()) { + logger.error("Cannot place at ${placeContext.blockPos} with current state ${placeContext.cachedState}", placeContext, request) + return ActionResult.FAIL + } - val itemPlacementContext = item.getPlacementContext(context) ?: return ActionResult.FAIL - val blockState = item.getPlacementState(itemPlacementContext) ?: return ActionResult.FAIL + val itemPlacementContext = item.getPlacementContext(context) ?: run { + logger.error("Could not retrieve item placement context", placeContext, request) + return ActionResult.FAIL + } + val blockState = item.getPlacementState(itemPlacementContext) ?: run { + logger.error("Could not retrieve placement state", placeContext, request) + return ActionResult.FAIL + } val stackInHand = player.getStackInHand(hand) val stackCountPre = stackInHand.count @@ -294,7 +351,10 @@ object PlaceManager : RequestHandler( // TODO: Implement restriction checks (e.g., world height) to prevent unnecessary server requests when the // "AwaitThenPlace" confirmation setting is enabled, as the block state setting methods that validate these // rules are not called. - if (!item.place(itemPlacementContext, blockState)) return ActionResult.FAIL + if (!item.place(itemPlacementContext, blockState)) { + logger.error("Could not place block client side at ${placeContext.blockPos} with placement state ${placeContext.expectedState}", placeContext, request) + return ActionResult.FAIL + } val blockPos = itemPlacementContext.blockPos var state = world.getBlockState(blockPos) @@ -310,6 +370,8 @@ object PlaceManager : RequestHandler( request.onPlace?.invoke(placeContext.blockPos) } + logger.success("Placed ${placeContext.expectedState} at ${placeContext.blockPos}", placeContext, request) + return ActionResult.SUCCESS } diff --git a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt index 552dc6eda..f38ecfcf3 100644 --- a/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/placing/PlaceRequest.kt @@ -20,6 +20,8 @@ package com.lambda.interaction.request.placing import com.lambda.config.groups.BuildConfig import com.lambda.interaction.construction.context.BuildContext import com.lambda.interaction.construction.context.PlaceContext +import com.lambda.interaction.request.LogContext +import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder import com.lambda.interaction.request.Request import com.lambda.interaction.request.hotbar.HotbarConfig import com.lambda.interaction.request.rotating.RotationConfig @@ -35,7 +37,9 @@ data class PlaceRequest( val hotbar: HotbarConfig, val rotation: RotationConfig, val onPlace: ((BlockPos) -> Unit)? = null -) : Request(), PlaceConfig by build.placing { +) : Request(), PlaceConfig by build.placing, LogContext { + override val requestID = ++requestCount + override val config = build.placing override val done: Boolean @@ -45,4 +49,15 @@ data class PlaceRequest( override fun submit(queueIfClosed: Boolean) = PlaceManager.request(this, queueIfClosed) + + override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("PlaceRequest") { + value("Request ID", requestID) + value("Contexts", contexts.size) + } + } + + companion object { + var requestCount = 0 + } } diff --git a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt index 8baffa828..a695e4133 100644 --- a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationManager.kt @@ -29,9 +29,11 @@ import com.lambda.event.events.UpdateManagerEvent import com.lambda.event.listener.SafeListener.Companion.listen import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe import com.lambda.interaction.BaritoneManager +import com.lambda.interaction.request.Logger import com.lambda.interaction.request.RequestHandler import com.lambda.interaction.request.rotating.Rotation.Companion.slerp import com.lambda.interaction.request.rotating.visibilty.lookAt +import com.lambda.module.hud.ManagerDebugLoggers.rotationManagerLogger import com.lambda.threading.runGameScheduled import com.lambda.threading.runSafe import com.lambda.util.extension.partialTicks @@ -56,7 +58,7 @@ object RotationManager : RequestHandler( TickEvent.Input.Pre, TickEvent.Input.Post, TickEvent.Player.Post, -) { +), Logger { var activeRotation = Rotation.ZERO var serverRotation = Rotation.ZERO @JvmStatic @@ -66,12 +68,15 @@ object RotationManager : RequestHandler( var activeRequest: RotationRequest? = null private var changedThisTick = false + override val logger = rotationManagerLogger + override fun load(): String { super.load() listen(priority = Int.MAX_VALUE) { activeRequest?.let { if (it.keepTicks <= 0 && it.decayTicks <= 0) { + logger.debug("Clearing active request", it) activeRequest = null } } @@ -135,6 +140,7 @@ object RotationManager : RequestHandler( override fun SafeContext.handleRequest(request: RotationRequest) { activeRequest?.let { if (it.age <= 0) return } if (request.target.targetRotation.value != null) { + logger.debug("Accepting request", request) activeRequest = request updateActiveRotation() changedThisTick = true @@ -269,9 +275,12 @@ object RotationManager : RequestHandler( // Important: do NOT wrap the result yaw; keep it continuous to match vanilla packets serverRotation.slerp(rotationTo, turnSpeed) } ?: player.rotation + + logger.debug("Active rotation set to $activeRotation", activeRequest) } private fun reset(rotation: Rotation) { + logger.debug("Resetting values with rotation $rotation") prevServerRotation = rotation serverRotation = rotation activeRotation = rotation diff --git a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt index 9e9579843..c38bfcd11 100644 --- a/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt +++ b/src/main/kotlin/com/lambda/interaction/request/rotating/RotationRequest.kt @@ -17,6 +17,8 @@ package com.lambda.interaction.request.rotating +import com.lambda.interaction.request.LogContext +import com.lambda.interaction.request.LogContext.Companion.LogContextBuilder import com.lambda.interaction.request.Request import com.lambda.interaction.request.rotating.visibilty.RotationTarget import com.lambda.threading.runSafe @@ -29,7 +31,9 @@ data class RotationRequest( override var keepTicks: Int = config.keepTicks, override var decayTicks: Int = config.decayTicks, val speedMultiplier: Double = 1.0 -) : Request(), RotationConfig by config { +) : Request(), RotationConfig by config, LogContext { + override val requestID = ++requestCount + var age = 0 override val done: Boolean get() = @@ -37,4 +41,20 @@ data class RotationRequest( override fun submit(queueIfClosed: Boolean): RotationRequest = RotationManager.request(this, queueIfClosed) + + override fun getLogContextBuilder(): LogContextBuilder.() -> Unit = { + group("Rotation Request") { + value("Request ID", requestID) + value("Rotation Mode", rotationMode) + value("Turn Speed", turnSpeed) + value("Keep Ticks", keepTicks) + value("Decay Ticks", decayTicks) + value("Speed Multiplier", speedMultiplier) + value("Age", age) + } + } + + companion object { + var requestCount = 0 + } } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/module/HudModule.kt b/src/main/kotlin/com/lambda/module/HudModule.kt index 1b705d9a4..c84b379b8 100644 --- a/src/main/kotlin/com/lambda/module/HudModule.kt +++ b/src/main/kotlin/com/lambda/module/HudModule.kt @@ -26,6 +26,7 @@ abstract class HudModule( name: String, description: String = "", tag: ModuleTag, + val customWindow: Boolean = false, alwaysListening: Boolean = false, enabledByDefault: Boolean = false, defaultKeybind: KeyCode = KeyCode.UNBOUND, diff --git a/src/main/kotlin/com/lambda/module/hud/ManagerDebugLoggers.kt b/src/main/kotlin/com/lambda/module/hud/ManagerDebugLoggers.kt new file mode 100644 index 000000000..70173c704 --- /dev/null +++ b/src/main/kotlin/com/lambda/module/hud/ManagerDebugLoggers.kt @@ -0,0 +1,91 @@ +/* + * 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 . + */ + +package com.lambda.module.hud + +import com.lambda.gui.dsl.ImGuiBuilder +import com.lambda.interaction.request.DebugLogger +import com.lambda.module.HudModule +import com.lambda.module.tag.ModuleTag +import com.lambda.util.NamedEnum + +@Suppress("Unused") +object ManagerDebugLoggers : HudModule( + "Manager Debug Loggers", + "debug loggers for all action managers in lambda", + ModuleTag.HUD, + customWindow = true +) { + enum class Group(override val displayName: String) : NamedEnum { + General("General"), + Break("Break"), + Place("Place"), + Interact("Interact"), + Rotation("Rotation"), + Hotbar("Hotbar"), + Inventory("Inventory") + } + + private val loggers = mutableMapOf<() -> Boolean, DebugLogger>() + + val autoScroll by setting("Auto-Scroll", true, "Locks the page at the bottom of the logs").group(Group.General) + val wrapText by setting("Wrap Text", true, "Wraps the logs to new lines if they go off the panel").group(Group.General) + val showDebug by setting("Show Debug", true, "Shows debug logs").group(Group.General) + val showSuccess by setting("Show Success", true, "Shows success logs").group(Group.General) + val showWarning by setting("Show Warning", true, "Shows warning logs").group(Group.General) + val showError by setting("Show Error", true, "Shows error logs").group(Group.General) + val showSystem by setting("Show System", true, "Shows system logs").group(Group.General) + val maxLogEntries by setting("Max Log Entries", 100, 1..1000, 1, "Maximum amount of entries in the log").group(Group.General) + .onValueChange { from, to -> + if (to < from) { + loggers.values.forEach { logger -> + while(logger.logs.size > to) { + logger.logs.removeFirst() + } + } + } + } + + private val showBreakManager by setting("Show Break Manager Logger", false).group(Group.Break) + val breakManagerLogger = DebugLogger("Break Manager Logger").store { showBreakManager } + + private val showPlaceManager by setting("Show Place Manager Logger", false).group(Group.Place) + val placeManagerLogger = DebugLogger("Place Manager Logger").store { showPlaceManager } + + private val showInteractionManager by setting("Show Interaction Manager Logger", false).group(Group.Interact) + val interactionManagerLogger = DebugLogger("Interaction Manager Logger").store { showInteractionManager } + + private val showRotationManager by setting("Show Rotation Manager Logger", false).group(Group.Rotation) + val rotationManagerLogger = DebugLogger("Rotation Manager Logger").store { showRotationManager } + + private val showHotbarManager by setting("Show Hotbar Manager Logger", false).group(Group.Hotbar) + val hotbarManagerLogger = DebugLogger("Hotbar Manager Logger").store { showHotbarManager } + + private val showInventoryManager by setting("Show Inventory Manager Logger", false).group(Group.Inventory) + val inventoryManagerLogger = DebugLogger("Inventory Manager Logger").store { showInventoryManager } + + private fun DebugLogger.store(show: () -> Boolean) = + also { loggers.put(show, this) } + + override fun ImGuiBuilder.buildLayout() { + loggers.entries.forEach { entry -> + if (entry.key()) with(entry.value) { + buildLayout() + } + } + } +} \ No newline at end of file