Skip to content

Commit 216b031

Browse files
committed
place manager checks server block updates
for pending placement confirmations
1 parent 21705b1 commit 216b031

File tree

4 files changed

+81
-48
lines changed

4 files changed

+81
-48
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,5 @@ class BuildSettings(
4343
// Placing
4444
override val placeSettings = PlaceSettings(c) { page == Page.Place && vis() }
4545

46-
override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeSettings.placeConfirmation != PlaceConfig.PlaceConfirmation.None || page == Page.Break && breakSettings.breakConfirmation != BreakConfirmationMode.None) }
46+
override val interactionTimeout by c.setting("Interaction Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks") { vis() && (page == Page.Place && placeSettings.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None || page == Page.Break && breakSettings.breakConfirmation != BreakConfirmationMode.None) }
4747
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class PlaceSettings(
2727
vis: () -> Boolean = { true }
2828
) : PlaceConfig(priority) {
2929
override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() }
30-
override val placeConfirmation by c.setting("Place Confirmation", PlaceConfirmation.PlaceThenAwait, "Wait for block placement confirmation") { vis() }
30+
override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() }
3131
override val maxPendingPlacements by c.setting("Max Pending Placements", 2, 0..5, 1, "The maximum amount of pending placements") { vis() }
3232
override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() }
3333
override val swing by c.setting("Swing", true, "Swings the players hand when placing") { vis() }

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ abstract class PlaceConfig(
2525
priority: Priority
2626
) : RequestConfig<PlaceRequest>(priority) {
2727
abstract val rotateForPlace: Boolean
28-
abstract val placeConfirmation: PlaceConfirmation
28+
abstract val placeConfirmationMode: PlaceConfirmationMode
2929
abstract val maxPendingPlacements: Int
3030
abstract val placementsPerTick: Int
3131
abstract val swing: Boolean
@@ -36,7 +36,7 @@ abstract class PlaceConfig(
3636
PlaceManager.registerRequest(this, request)
3737
}
3838

39-
enum class PlaceConfirmation {
39+
enum class PlaceConfirmationMode {
4040
None,
4141
PlaceThenAwait,
4242
AwaitThenPlace

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

Lines changed: 77 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@
1717

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

20+
import com.lambda.config.groups.BuildConfig
2021
import com.lambda.context.SafeContext
2122
import com.lambda.event.EventFlow.post
2223
import com.lambda.event.events.MovementEvent
2324
import com.lambda.event.events.TickEvent
2425
import com.lambda.event.events.UpdateManagerEvent
26+
import com.lambda.event.events.WorldEvent
2527
import com.lambda.event.listener.SafeListener.Companion.listen
2628
import com.lambda.interaction.construction.context.PlaceContext
29+
import com.lambda.interaction.construction.verify.TargetState
2730
import com.lambda.interaction.request.RequestHandler
2831
import com.lambda.interaction.request.hotbar.HotbarRequest
2932
import com.lambda.interaction.request.rotation.RotationManager.onRotate
@@ -41,20 +44,18 @@ import net.minecraft.item.ItemUsageContext
4144
import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket
4245
import net.minecraft.registry.RegistryKeys
4346
import net.minecraft.sound.SoundCategory
44-
import net.minecraft.stat.Stats
4547
import net.minecraft.util.ActionResult
4648
import net.minecraft.util.Hand
4749
import net.minecraft.util.hit.BlockHitResult
50+
import net.minecraft.util.math.BlockPos
4851
import net.minecraft.world.GameMode
49-
import net.minecraft.world.event.GameEvent
5052
import org.apache.commons.lang3.mutable.MutableObject
5153

5254
object PlaceManager : RequestHandler<PlaceRequest>() {
53-
private val pendingInteractions = LimitedDecayQueue<PlaceContext>(
55+
private val pendingInteractions = LimitedDecayQueue<PlaceInfo>(
5456
TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L
55-
) { info("${it::class.simpleName} at ${it.expectedPos.toShortString()} timed out") }
57+
) { info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out") }
5658

57-
//ToDo: Add server response check for pending interactions
5859
init {
5960
listen<TickEvent.Pre>(Int.MIN_VALUE) {
6061
preEvent()
@@ -65,9 +66,8 @@ object PlaceManager : RequestHandler<PlaceRequest>() {
6566
}
6667

6768
currentRequest?.let request@ { request ->
68-
if (pendingInteractions.size >= request.buildConfig.placeSettings.maxPendingPlacements) {
69+
if (pendingInteractions.size >= request.buildConfig.placeSettings.maxPendingPlacements)
6970
return@request
70-
}
7171

7272
if (request.placeContext.sneak && !player.isSneaking
7373
|| (request.buildConfig.placeSettings.rotateForPlace && !request.placeContext.rotation.done)
@@ -79,15 +79,28 @@ object PlaceManager : RequestHandler<PlaceRequest>() {
7979
pendingInteractions.setMaxSize(request.buildConfig.maxPendingInteractions)
8080
pendingInteractions.setDecayTime(request.buildConfig.interactionTimeout * 50L)
8181
placeBlock(request, Hand.MAIN_HAND)
82-
}
82+
}
8383

8484
postEvent()
8585
}
8686

87+
listen<WorldEvent.BlockUpdate.Server> { event ->
88+
pendingInteractions
89+
.firstOrNull { it.context.expectedPos == event.pos }
90+
?.let { pending ->
91+
pendingInteractions.remove(pending)
92+
if (!matchesTargetState(event.pos, pending.context.targetState, event.newState)) return@listen
93+
if (pending.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace)
94+
placeSound(pending.item, pending.context.expectedState, pending.context.expectedPos)
95+
pending.onPlace()
96+
}
97+
}
98+
8799
onRotate {
88100
currentRequest?.let { request ->
89-
if (request.buildConfig.placeSettings.rotateForPlace)
101+
if (request.buildConfig.placeSettings.rotateForPlace) {
90102
request.rotationConfig.request(request.placeContext.rotation)
103+
}
91104
}
92105
}
93106

@@ -96,37 +109,56 @@ object PlaceManager : RequestHandler<PlaceRequest>() {
96109
}
97110
}
98111

112+
private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) =
113+
if (targetState.matches(newState, pos, world)) true
114+
else {
115+
this@PlaceManager.warn("Place at ${pos.toShortString()} was rejected with $newState instead of $targetState")
116+
false
117+
}
118+
99119
private fun SafeContext.placeBlock(request: PlaceRequest, hand: Hand) {
100120
val stackInHand = player.getStackInHand(hand)
121+
val item = stackInHand.item as? BlockItem ?: return
101122
val stackCountPre = stackInHand.count
102123
val actionResult = interactBlock(request.buildConfig.placeSettings, hand, request.placeContext.result)
103124

104125
if (actionResult.isAccepted) {
105-
if (request.buildConfig.placeSettings.placeConfirmation != PlaceConfig.PlaceConfirmation.None)
106-
pendingInteractions.add(request.placeContext)
126+
if (request.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.None)
127+
request.onPlace()
128+
else {
129+
pendingInteractions.add(
130+
PlaceInfo(
131+
request.placeContext,
132+
item,
133+
request.buildConfig,
134+
request.onPlace
135+
)
136+
)
137+
}
107138

108-
if (actionResult.shouldSwingHand() && request.buildConfig.placeSettings.swing)
139+
if (actionResult.shouldSwingHand() && request.buildConfig.placeSettings.swing) {
109140
swingHand(request.buildConfig.placeSettings.swingType)
141+
}
110142

111-
if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory()))
143+
if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) {
112144
mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand)
145+
}
113146
} else {
114147
warn("Placement interaction failed with $actionResult")
115148
}
116-
request.onPlace()
117149
}
118150

119151
private fun SafeContext.interactBlock(placeConfig: PlaceConfig, hand: Hand, hitResult: BlockHitResult): ActionResult {
120152
interaction.syncSelectedSlot()
121153
if (!world.worldBorder.contains(hitResult.blockPos)) {
122154
return ActionResult.FAIL
123155
} else {
124-
val mutableObject = MutableObject<ActionResult>()
156+
val mutableActionResult = MutableObject<ActionResult>()
125157
interaction.sendSequencedPacket(world) { sequence: Int ->
126-
mutableObject.value = interactBlockInternal(placeConfig, hand, hitResult)
158+
mutableActionResult.value = interactBlockInternal(placeConfig, hand, hitResult)
127159
PlayerInteractBlockC2SPacket(hand, hitResult, sequence)
128160
}
129-
return mutableObject.value
161+
return mutableActionResult.value
130162
}
131163
}
132164

@@ -169,19 +201,12 @@ object PlaceManager : RequestHandler<PlaceRequest>() {
169201
) {
170202
return ActionResult.PASS
171203
}
172-
val item: BlockItem = (itemStack.item as? BlockItem) ?: return ActionResult.PASS
173-
val actionResult = useOnBlock(placeConfig, item, context)
174-
if (actionResult.shouldIncrementStat()) player.incrementStat(Stats.USED.getOrCreateStat(item))
204+
val item = (itemStack.item as? BlockItem) ?: return ActionResult.PASS
205+
val actionResult = place(placeConfig, item, ItemPlacementContext(context))
175206

176207
return actionResult
177208
}
178209

179-
private fun SafeContext.useOnBlock(
180-
placeConfig: PlaceConfig,
181-
item: BlockItem,
182-
context: ItemUsageContext
183-
) = place(placeConfig, item, ItemPlacementContext(context))
184-
185210
private fun SafeContext.place(
186211
placeConfig: PlaceConfig,
187212
item: BlockItem,
@@ -190,15 +215,15 @@ object PlaceManager : RequestHandler<PlaceRequest>() {
190215
if (!item.block.isEnabled(world.enabledFeatures)) return ActionResult.FAIL
191216
if (!context.canPlace()) return ActionResult.FAIL
192217

193-
val itemPlacementContext: ItemPlacementContext = item.getPlacementContext(context) ?: return ActionResult.FAIL
194-
val blockState: BlockState = item.getPlacementState(itemPlacementContext) ?: return ActionResult.FAIL
218+
val itemPlacementContext = item.getPlacementContext(context) ?: return ActionResult.FAIL
219+
val blockState = item.getPlacementState(itemPlacementContext) ?: return ActionResult.FAIL
195220

196-
if (placeConfig.placeConfirmation == PlaceConfig.PlaceConfirmation.AwaitThenPlace)
221+
if (placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace)
197222
return ActionResult.success(world.isClient)
198223

199-
//ToDo: Add restriction checks, like world height, to avoid needlessly awaiting a server response which will never return
200-
// if the user has the AwaitThenPlace confirmation setting enabled, as none of the state-setting methods which check these rules
201-
// are called
224+
// TODO: Implement restriction checks (e.g., world height) to prevent unnecessary server requests when the
225+
// "AwaitThenPlace" confirmation setting is enabled, as the block state setting methods that validate these
226+
// rules are not called.
202227
if (!item.place(itemPlacementContext, blockState)) return ActionResult.FAIL
203228

204229
val blockPos = itemPlacementContext.blockPos
@@ -210,23 +235,31 @@ object PlaceManager : RequestHandler<PlaceRequest>() {
210235
hitState.block.onPlaced(world, blockPos, hitState, player, itemStack)
211236
}
212237

213-
if (placeConfig.sounds) {
214-
val blockSoundGroup = hitState.soundGroup
215-
world.playSound(
216-
player,
217-
blockPos,
218-
item.getPlaceSound(hitState),
219-
SoundCategory.BLOCKS,
220-
(blockSoundGroup.getVolume() + 1.0f) / 2.0f,
221-
blockSoundGroup.getPitch() * 0.8f
222-
)
223-
}
224-
world.emitGameEvent(GameEvent.BLOCK_PLACE, blockPos, GameEvent.Emitter.of(player, hitState))
238+
if (placeConfig.sounds) placeSound(item, hitState, blockPos)
225239
if (!player.abilities.creativeMode) itemStack.decrement(1)
226240

227241
return ActionResult.success(world.isClient)
228242
}
229243

244+
private fun SafeContext.placeSound(item: BlockItem, state: BlockState, pos: BlockPos) {
245+
val blockSoundGroup = state.soundGroup
246+
world.playSound(
247+
player,
248+
pos,
249+
item.getPlaceSound(state),
250+
SoundCategory.BLOCKS,
251+
(blockSoundGroup.getVolume() + 1.0f) / 2.0f,
252+
blockSoundGroup.getPitch() * 0.8f
253+
)
254+
}
255+
256+
data class PlaceInfo(
257+
val context: PlaceContext,
258+
val item: BlockItem,
259+
val buildConfig: BuildConfig,
260+
val onPlace: () -> Unit
261+
)
262+
230263
override fun preEvent() = UpdateManagerEvent.Place.Pre().post()
231264
override fun postEvent() = UpdateManagerEvent.Place.Post().post()
232265
}

0 commit comments

Comments
 (0)