Skip to content

Commit 3b9c161

Browse files
committed
PlacedBlockHandler
1 parent 064b78f commit 3b9c161

File tree

3 files changed

+119
-68
lines changed

3 files changed

+119
-68
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.interaction.request.placing
19+
20+
import com.lambda.interaction.construction.context.BuildContext
21+
import com.lambda.interaction.construction.context.PlaceContext
22+
23+
data class PlaceInfo(
24+
val context: PlaceContext,
25+
val onPlace: () -> Unit,
26+
val pendingInteractionsList: MutableCollection<BuildContext>,
27+
val placeConfig: PlaceConfig
28+
)

common/src/main/kotlin/com/lambda/interaction/request/placing/PlaceManager.kt

Lines changed: 4 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,26 @@
1717

1818
package com.lambda.interaction.request.placing
1919

20-
import com.lambda.Lambda.mc
2120
import com.lambda.config.groups.TickStage
2221
import com.lambda.context.SafeContext
2322
import com.lambda.event.Event
2423
import com.lambda.event.EventFlow.post
25-
import com.lambda.event.events.ConnectionEvent
2624
import com.lambda.event.events.MovementEvent
2725
import com.lambda.event.events.TickEvent
2826
import com.lambda.event.events.UpdateManagerEvent
29-
import com.lambda.event.events.WorldEvent
3027
import com.lambda.event.listener.SafeListener.Companion.listen
31-
import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
32-
import com.lambda.interaction.construction.context.BuildContext
3328
import com.lambda.interaction.construction.context.PlaceContext
34-
import com.lambda.interaction.construction.verify.TargetState
3529
import com.lambda.interaction.request.PositionBlocking
3630
import com.lambda.interaction.request.Priority
3731
import com.lambda.interaction.request.RequestHandler
3832
import com.lambda.interaction.request.breaking.BreakManager
3933
import com.lambda.interaction.request.hotbar.HotbarManager
4034
import com.lambda.interaction.request.placing.PlaceManager.activeRequest
4135
import com.lambda.interaction.request.placing.PlaceManager.processRequest
42-
import com.lambda.module.modules.client.TaskFlowModule
36+
import com.lambda.interaction.request.placing.PlacedBlockHandler.addPendingPlace
37+
import com.lambda.interaction.request.placing.PlacedBlockHandler.pendingPlacements
4338
import com.lambda.util.BlockUtils.blockState
44-
import com.lambda.util.BlockUtils.item
45-
import com.lambda.util.Communication.info
4639
import com.lambda.util.Communication.warn
47-
import com.lambda.util.collections.LimitedDecayQueue
4840
import com.lambda.util.player.gamemode
4941
import com.lambda.util.player.isItemOnCooldown
5042
import com.lambda.util.player.swingHand
@@ -70,21 +62,12 @@ object PlaceManager : RequestHandler<PlaceRequest>(
7062
*TickStage.entries.toTypedArray(),
7163
onOpen = { activeRequest?.let { processRequest(it) } }
7264
), PositionBlocking {
73-
private val pendingPlacements = LimitedDecayQueue<PlaceInfo>(
74-
TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L
75-
) {
76-
info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out")
77-
mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState)
78-
it.pendingInteractionsList.remove(it.context)
79-
}
80-
8165
private var activeRequest: PlaceRequest? = null
66+
private var potentialPlacements = mutableListOf<PlaceContext>()
8267

8368
private var placementsThisTick = 0
8469
private var maxPlacementsThisTick = 0
8570

86-
private var potentialPlacements = mutableListOf<PlaceContext>()
87-
8871
private var shouldSneak = false
8972
private val validSneak: (player: ClientPlayerEntity) -> Boolean =
9073
{ player -> shouldSneak == player.isSneaking }
@@ -112,29 +95,6 @@ object PlaceManager : RequestHandler<PlaceRequest>(
11295
it.input.sneaking = true
11396
}
11497
}
115-
116-
listen<WorldEvent.BlockUpdate.Server>(priority = Int.MIN_VALUE) { event ->
117-
pendingPlacements
118-
.firstOrNull { it.context.expectedPos == event.pos }
119-
?.let { info ->
120-
removePendingPlace(info)
121-
122-
// return if the block wasn't placed
123-
if (!matchesTargetState(event.pos, info.context.targetState, event.newState))
124-
return@listen
125-
126-
if (info.placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace)
127-
with (info.context) {
128-
placeSound(expectedState.block.item as BlockItem, expectedState, expectedPos)
129-
}
130-
info.onPlace()
131-
return@listen
132-
}
133-
}
134-
135-
listenUnsafe<ConnectionEvent.Connect.Pre> {
136-
pendingPlacements.clear()
137-
}
13898
}
13999

140100
override fun SafeContext.handleRequest(request: PlaceRequest) {
@@ -198,13 +158,6 @@ object PlaceManager : RequestHandler<PlaceRequest>(
198158
pending.context.expectedPos == placeContext.expectedPos
199159
}
200160

201-
private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) =
202-
if (targetState.matches(newState, pos, world)) true
203-
else {
204-
this@PlaceManager.warn("Place at ${pos.toShortString()} was rejected with $newState instead of $targetState")
205-
false
206-
}
207-
208161
private fun SafeContext.placeBlock(placeContext: PlaceContext, request: PlaceRequest, hand: Hand): ActionResult {
209162
interaction.syncSelectedSlot()
210163
val hitResult = placeContext.result
@@ -342,7 +295,7 @@ object PlaceManager : RequestHandler<PlaceRequest>(
342295
PlayerInteractBlockC2SPacket(hand, hitResult, sequence)
343296
}
344297

345-
private fun SafeContext.placeSound(item: BlockItem, state: BlockState, pos: BlockPos) {
298+
fun SafeContext.placeSound(item: BlockItem, state: BlockState, pos: BlockPos) {
346299
val blockSoundGroup = state.soundGroup
347300
world.playSound(
348301
player,
@@ -364,22 +317,5 @@ object PlaceManager : RequestHandler<PlaceRequest>(
364317
)
365318
}
366319

367-
private fun addPendingPlace(info: PlaceInfo) {
368-
pendingPlacements.add(info)
369-
info.pendingInteractionsList.add(info.context)
370-
}
371-
372-
private fun removePendingPlace(info: PlaceInfo) {
373-
pendingPlacements.remove(info)
374-
info.pendingInteractionsList.remove(info.context)
375-
}
376-
377-
private data class PlaceInfo(
378-
val context: PlaceContext,
379-
val onPlace: () -> Unit,
380-
val pendingInteractionsList: MutableCollection<BuildContext>,
381-
val placeConfig: PlaceConfig
382-
)
383-
384320
override fun preEvent(): Event = UpdateManagerEvent.Place().post()
385321
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.interaction.request.placing
19+
20+
import com.lambda.Lambda.mc
21+
import com.lambda.context.SafeContext
22+
import com.lambda.event.events.ConnectionEvent
23+
import com.lambda.event.events.WorldEvent
24+
import com.lambda.event.listener.SafeListener.Companion.listen
25+
import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
26+
import com.lambda.interaction.construction.verify.TargetState
27+
import com.lambda.interaction.request.placing.PlaceManager.placeSound
28+
import com.lambda.module.modules.client.TaskFlowModule
29+
import com.lambda.util.BlockUtils.item
30+
import com.lambda.util.Communication.info
31+
import com.lambda.util.Communication.warn
32+
import com.lambda.util.collections.LimitedDecayQueue
33+
import net.minecraft.block.BlockState
34+
import net.minecraft.item.BlockItem
35+
import net.minecraft.util.math.BlockPos
36+
37+
object PlacedBlockHandler {
38+
val pendingPlacements = LimitedDecayQueue<PlaceInfo>(
39+
TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L
40+
) {
41+
info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out")
42+
mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState)
43+
it.pendingInteractionsList.remove(it.context)
44+
}
45+
46+
init {
47+
listen<WorldEvent.BlockUpdate.Server>(priority = Int.MIN_VALUE) { event ->
48+
pendingPlacements
49+
.firstOrNull { it.context.expectedPos == event.pos }
50+
?.let { info ->
51+
removePendingPlace(info)
52+
53+
// return if the block wasn't placed
54+
if (!matchesTargetState(event.pos, info.context.targetState, event.newState))
55+
return@listen
56+
57+
if (info.placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace)
58+
with (info.context) {
59+
placeSound(expectedState.block.item as BlockItem, expectedState, expectedPos)
60+
}
61+
info.onPlace()
62+
return@listen
63+
}
64+
}
65+
66+
listenUnsafe<ConnectionEvent.Connect.Pre> {
67+
pendingPlacements.clear()
68+
}
69+
}
70+
71+
fun addPendingPlace(info: PlaceInfo) {
72+
pendingPlacements.add(info)
73+
info.pendingInteractionsList.add(info.context)
74+
}
75+
76+
private fun removePendingPlace(info: PlaceInfo) {
77+
pendingPlacements.remove(info)
78+
info.pendingInteractionsList.remove(info.context)
79+
}
80+
81+
private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) =
82+
if (targetState.matches(newState, pos, world)) true
83+
else {
84+
this@PlacedBlockHandler.warn("Place at ${pos.toShortString()} was rejected with $newState instead of $targetState")
85+
false
86+
}
87+
}

0 commit comments

Comments
 (0)