Skip to content

Commit 58c2e7f

Browse files
committed
rewrote HotbarManager to allow for multiple swaps per tick
- added swapDelay, and swapsPerTick settings
1 parent fbe2756 commit 58c2e7f

File tree

9 files changed

+240
-144
lines changed

9 files changed

+240
-144
lines changed

common/src/main/kotlin/com/lambda/config/groups/HotbarSettings.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,7 @@ class HotbarSettings(
2727
vis: () -> Boolean = { true }
2828
) : HotbarConfig(priority) {
2929
override val keepTicks by c.setting("Keep Ticks", 3, 0..20, 1, "The number of ticks to keep the current hotbar selection active", " ticks", vis)
30-
override var switchPause by c.setting("Switch Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks", vis)
30+
override val swapDelay by c.setting("Swap Delay", 0, 0..3, 1, "The number of ticks delay before allowing another hotbar selection swap", " ticks", vis)
31+
override val swapsPerTick by c.setting("Swaps Per Tick", 3, 1..10, 1, "The number of hotbar selection swaps that can take place each tick") { swapDelay <= 0 && vis() }
32+
override var swapPause by c.setting("Swap Pause", 0, 0..20, 1, "The delay in ticks to pause actions after switching to the slot", " ticks", vis)
3133
}

common/src/main/kotlin/com/lambda/interaction/request/RequestHandler.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import java.util.concurrent.ConcurrentHashMap
2929
*/
3030
abstract class RequestHandler<R : Request> {
3131

32-
private val requestMap = ConcurrentHashMap<RequestConfig<R>, R>()
32+
protected val requestMap = ConcurrentHashMap<RequestConfig<R>, R>()
3333

3434
/**
3535
* Represents if the handler performed any external actions within this tick

common/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt

Lines changed: 83 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import com.lambda.context.SafeContext
2323
import com.lambda.event.EventFlow.post
2424
import com.lambda.event.events.ConnectionEvent
2525
import com.lambda.event.events.EntityEvent
26-
import com.lambda.event.events.TickEvent
2726
import com.lambda.event.events.UpdateManagerEvent
2827
import com.lambda.event.events.WorldEvent
2928
import com.lambda.event.listener.SafeListener.Companion.listen
@@ -113,94 +112,100 @@ object BreakManager : RequestHandler<BreakRequest>(), PositionBlocking {
113112
}
114113

115114
init {
116-
listen<TickEvent.Pre>(priority = Int.MIN_VALUE + 1) {
115+
listen<UpdateManagerEvent.Hotbar.Pre> {
117116
preEvent()
118117

119118
pendingBreaks.cleanUp()
120119
updateRequest()
121-
if (isOnBreakCooldown()) {
122-
blockBreakingCooldown--
123-
} else if (currentRequest == null) {
124-
breakingInfos.forEach { it?.cancelBreak(player, world, interaction) }
125-
} else currentRequest?.let request@ { request ->
126-
val breakConfig = request.buildConfig.breakSettings
127-
128-
val validNewContexts = request.contexts
129-
.filter { ctx -> canAccept(ctx) }
130-
.sortedWith(
131-
compareByDescending<BreakContext> { it.instantBreak }
132-
.thenByDescending { it.hotbarIndex == HotbarManager.serverSlot }
133-
).toMutableList()
120+
val request = currentRequest ?: breakingInfos.filterNotNull().firstOrNull()?.request ?: return@listen
121+
122+
val hotbarRequest = HotbarRequest(request.hotbarConfig) {
123+
if (isOnBreakCooldown()) {
124+
blockBreakingCooldown--
125+
} else if (currentRequest == null) {
126+
breakingInfos.forEach { it?.cancelBreak(player, world, interaction) }
127+
} else currentRequest?.let request@ { currentRequest ->
128+
val breakConfig = currentRequest.buildConfig.breakSettings
129+
val validNewContexts = currentRequest.contexts
130+
.filter { ctx -> canAccept(ctx) }
131+
.sortedWith(
132+
compareByDescending<BreakContext> { it.instantBreak }
133+
.thenByDescending { it.hotbarIndex == HotbarManager.serverSlot }
134+
).toMutableList()
135+
136+
breakingInfos
137+
.filterNotNull()
138+
.forEach { info ->
139+
validNewContexts.find {
140+
ctx -> ctx.expectedPos == info.context.expectedPos
141+
}?.let { ctx ->
142+
info.updateInfo(ctx, currentRequest)
143+
validNewContexts.remove(ctx)
144+
return@forEach
145+
}
146+
147+
info.cancelBreak(player, world, interaction)
148+
}
149+
if (atMaxBreakingInfos(currentRequest.buildConfig.breakSettings)) return@request
150+
151+
val maxBreaksThisTick = breakConfig.maxPendingBreaks - (breakingInfos.count { it != null } + pendingBreaks.size)
152+
if (maxBreaksThisTick <= 0) return@request
153+
val instantBreaks = validNewContexts
154+
.take(breakConfig.instantBreaksPerTick.coerceAtMost(maxBreaksThisTick))
155+
.filter { it.instantBreak }
156+
157+
if (instantBreaks.isNotEmpty()) {
158+
instantBreaks.forEach { ctx ->
159+
if (ctx.hotbarIndex != HotbarManager.serverSlot) {
160+
if (!swapTo(ctx.hotbarIndex)) return@request
161+
}
162+
val breakInfo = handleRequestContext(ctx, currentRequest) ?: return@request
163+
currentRequest.onAccept?.invoke(ctx.expectedPos)
164+
updateBlockBreakingProgress(breakInfo)
165+
activeThisTick = true
166+
}
167+
if (instantBreaks.size == breakConfig.instantBreaksPerTick) return@request
168+
}
134169

135-
breakingInfos
136-
.filterNotNull()
137-
.forEach { info ->
138-
validNewContexts.find {
139-
ctx -> ctx.expectedPos == info.context.expectedPos
140-
}?.let { ctx ->
141-
info.updateInfo(ctx, request)
142-
validNewContexts.remove(ctx)
143-
return@forEach
170+
validNewContexts
171+
.filter { !it.instantBreak }
172+
.forEach { ctx ->
173+
handleRequestContext(ctx, currentRequest) ?: return@request
174+
currentRequest.onAccept?.invoke(ctx.expectedPos)
175+
if (atMaxBreakingInfos(currentRequest.buildConfig.breakSettings)) return@request
144176
}
177+
}
145178

146-
info.cancelBreak(player, world, interaction)
179+
breakingInfos
180+
.firstOrNull { it?.breakConfig?.rotateForBreak == true && !it.redundant }
181+
?.let { info ->
182+
// If the simulation cant find a valid rotation to the block,
183+
// the existing break context stays and the keep ticks deplete until rotations stop
184+
if (info.context.rotation.keepTicks <= 0) null
185+
else info.rotationConfig.request(info.context.rotation)
147186
}
148-
if (atMaxBreakingInfos(request.buildConfig.breakSettings)) return@request
149-
150-
val maxBreaksThisTick = breakConfig.maxPendingBreaks - (breakingInfos.count { it != null } + pendingBreaks.size)
151-
if (maxBreaksThisTick <= 0) return@request
152-
val instantBreaks = validNewContexts
153-
.take(breakConfig.instantBreaksPerTick.coerceAtMost(maxBreaksThisTick))
154-
.filter { it.instantBreak }
155-
156-
if (instantBreaks.isNotEmpty()) {
157-
instantBreaks.forEach { ctx ->
158-
if (ctx.hotbarIndex != HotbarManager.serverSlot) {
159-
if (!request.hotbarConfig.request(HotbarRequest(ctx.hotbarIndex)).done) return@request
187+
?.let { rot ->
188+
if (!rot.done) {
189+
return@HotbarRequest
160190
}
161-
val breakInfo = handleRequestContext(ctx, request) ?: return@request
162-
request.onAccept?.invoke(ctx.expectedPos)
163-
updateBlockBreakingProgress(breakInfo)
164-
activeThisTick = true
165191
}
166-
if (instantBreaks.size == breakConfig.instantBreaksPerTick) return@request
167-
}
168192

169-
validNewContexts
170-
.filter { !it.instantBreak }
171-
.forEach { ctx ->
172-
handleRequestContext(ctx, request) ?: return@request
173-
request.onAccept?.invoke(ctx.expectedPos)
174-
if (atMaxBreakingInfos(request.buildConfig.breakSettings)) return@request
193+
// Reversed so that the breaking order feels natural to the user as the primary break has to
194+
// be started after the secondary
195+
breakingInfos
196+
.filterNotNull()
197+
.reversed()
198+
.forEach { info ->
199+
if (!info.redundant && !swapTo(info.context.hotbarIndex)) return@HotbarRequest
200+
updateBlockBreakingProgress(info)
201+
if (!info.redundant) activeThisTick = true
175202
}
203+
done()
176204
}
205+
request.hotbarConfig.request(hotbarRequest)
206+
}
177207

178-
breakingInfos
179-
.firstOrNull { it?.breakConfig?.rotateForBreak == true && !it.redundant }
180-
?.let { info ->
181-
// If the simulation cant find a valid rotation to the block,
182-
// the existing break context stays and the keep ticks deplete until rotations stop
183-
if (info.context.rotation.keepTicks <= 0) null
184-
else info.rotationConfig.request(info.context.rotation)
185-
}
186-
?.let { rot ->
187-
if (!rot.done) {
188-
postEvent()
189-
return@listen
190-
}
191-
}
192-
193-
// Reversed so that the breaking order feels natural to the user as the primary break has to
194-
// be started after the secondary
195-
breakingInfos
196-
.filterNotNull()
197-
.reversed()
198-
.forEach { info ->
199-
if (!info.redundant && !info.requestHotbarSwap()) return@forEach
200-
updateBlockBreakingProgress(info)
201-
if (!info.redundant) activeThisTick = true
202-
}
203-
208+
listen<UpdateManagerEvent.Hotbar.Post> {
204209
postEvent()
205210
}
206211

@@ -345,7 +350,7 @@ object BreakManager : RequestHandler<BreakRequest>(), PositionBlocking {
345350
setBreakCooldown(info.breakConfig.breakDelay)
346351
interaction.sendSequencedPacket(world) { sequence ->
347352
onBlockBreak(info)
348-
PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence)
353+
PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence)
349354
}
350355
val swing = info.breakConfig.swing
351356
if (swing.isEnabled()) {
@@ -419,7 +424,7 @@ object BreakManager : RequestHandler<BreakRequest>(), PositionBlocking {
419424
if (info.type == BreakType.Primary) {
420425
interaction.sendSequencedPacket(world) { sequence ->
421426
onBlockBreak(info)
422-
PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence)
427+
PlayerActionC2SPacket(Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence)
423428
}
424429
} else {
425430
onBlockBreak(info)
@@ -442,7 +447,7 @@ object BreakManager : RequestHandler<BreakRequest>(), PositionBlocking {
442447
if (gamemode.isCreative) {
443448
interaction.sendSequencedPacket(world) { sequence: Int ->
444449
onBlockBreak(info)
445-
PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence)
450+
PlayerActionC2SPacket(Action.START_DESTROY_BLOCK, ctx.expectedPos, ctx.result.side, sequence)
446451
}
447452
setBreakCooldown(info.breakConfig.breakDelay)
448453
return true
@@ -591,9 +596,6 @@ object BreakManager : RequestHandler<BreakRequest>(), PositionBlocking {
591596
}
592597
}
593598

594-
fun requestHotbarSwap() =
595-
request.hotbarConfig.request(HotbarRequest(context.hotbarIndex)).done
596-
597599
fun setBreakingTextureStage(
598600
player: ClientPlayerEntity,
599601
world: ClientWorld,

common/src/main/kotlin/com/lambda/interaction/request/hotbar/HotbarConfig.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,24 @@ abstract class HotbarConfig(
3434
*/
3535
abstract val keepTicks: Int
3636

37+
/**
38+
* The delay, in ticks, between swapping hotbar selections
39+
*/
40+
abstract val swapDelay: Int
41+
42+
/**
43+
* The amount of hotbar selection swaps that can happen per tick
44+
*
45+
* Only makes a difference if swapDelay is set to 0
46+
*/
47+
abstract val swapsPerTick: Int
48+
3749
/**
3850
* The delay in ticks to pause actions after switching to the slot.
3951
*
4052
* Affects the validity state of the request
4153
*/
42-
abstract var switchPause: Int
54+
abstract var swapPause: Int
4355

4456
/**
4557
* Registers a hotbar request with the HotbarManager.

0 commit comments

Comments
 (0)