Skip to content

Commit c2244a1

Browse files
committed
allow multiple block placements in one tick
1 parent 96f90b3 commit c2244a1

File tree

7 files changed

+117
-76
lines changed

7 files changed

+117
-76
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class PlaceSettings(
3333
get() = airPlace.isEnabled() && axisRotateSetting
3434
override val placeConfirmationMode by c.setting("Place Confirmation", PlaceConfirmationMode.PlaceThenAwait, "Wait for block placement confirmation") { vis() }
3535
override val maxPendingPlacements by c.setting("Max Pending Placements", 5, 0..30, 1, "The maximum amount of pending placements") { vis() }
36-
override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() }
36+
override val placementsPerTick by c.setting("Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() }
3737
override val swing by c.setting("Swing", true, "Swings the players hand when placing") { vis() }
3838
override val swingType by c.setting("Place Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() && swing }
3939
override val sounds by c.setting("Place Sounds", true, "Plays the placing sounds") { vis() }

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import com.lambda.interaction.request.Priority
3535
import com.lambda.interaction.request.RequestHandler
3636
import com.lambda.interaction.request.breaking.BreakConfig.BreakConfirmationMode
3737
import com.lambda.interaction.request.breaking.BreakConfig.BreakMode
38+
import com.lambda.interaction.request.hotbar.HotbarManager
3839
import com.lambda.interaction.request.hotbar.HotbarRequest
3940
import com.lambda.interaction.request.placing.PlaceManager
4041
import com.lambda.interaction.request.rotation.RotationManager.onRotate
@@ -119,12 +120,14 @@ object BreakManager : RequestHandler<BreakRequest>(), PositionBlocking {
119120
currentRequest?.let request@ { request ->
120121
if (instantBreaks.isEmpty()) return@request
121122

122-
instantBreaks.forEach { ctx ->
123-
val breakInfo = handleRequestContext(ctx, request) ?: return@request
124-
if (!breakInfo.requestHotbarSwap()) return@forEach
125-
updateBlockBreakingProgress(breakInfo)
126-
activeThisTick = true
127-
}
123+
instantBreaks
124+
.sortedBy { HotbarManager.serverSlot == it.hotbarIndex }
125+
.forEach { ctx ->
126+
val breakInfo = handleRequestContext(ctx, request) ?: return@request
127+
if (!breakInfo.requestHotbarSwap()) return@forEach
128+
updateBlockBreakingProgress(breakInfo)
129+
activeThisTick = true
130+
}
128131
instantBreaks = emptyList()
129132
}
130133

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import com.lambda.util.BlockUtils.blockState
2929
import net.minecraft.entity.ItemEntity
3030

3131
data class BreakRequest(
32-
val contexts: List<BreakContext>,
32+
val contexts: Collection<BreakContext>,
3333
val buildConfig: BuildConfig,
3434
val rotationConfig: RotationConfig,
3535
val hotbarConfig: HotbarConfig,

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

Lines changed: 76 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,16 @@ import com.lambda.event.events.TickEvent
2525
import com.lambda.event.events.UpdateManagerEvent
2626
import com.lambda.event.events.WorldEvent
2727
import com.lambda.event.listener.SafeListener.Companion.listen
28+
import com.lambda.interaction.construction.context.BuildContext
2829
import com.lambda.interaction.construction.context.PlaceContext
2930
import com.lambda.interaction.construction.verify.TargetState
3031
import com.lambda.interaction.request.PositionBlocking
3132
import com.lambda.interaction.request.Priority
3233
import com.lambda.interaction.request.RequestHandler
3334
import com.lambda.interaction.request.breaking.BreakManager
35+
import com.lambda.interaction.request.hotbar.HotbarManager
3436
import com.lambda.interaction.request.hotbar.HotbarRequest
3537
import com.lambda.interaction.request.rotation.RotationManager.onRotate
36-
import com.lambda.interaction.request.rotation.RotationManager.onRotatePost
37-
import com.lambda.interaction.request.rotation.RotationRequest
3838
import com.lambda.module.modules.client.TaskFlowModule
3939
import com.lambda.util.BlockUtils.blockState
4040
import com.lambda.util.BlockUtils.item
@@ -62,19 +62,18 @@ import net.minecraft.util.math.Direction
6262
import net.minecraft.world.GameMode
6363

6464
object PlaceManager : RequestHandler<PlaceRequest>(), PositionBlocking {
65-
private val pendingPlacements = LimitedDecayQueue<PlaceRequest>(
65+
private val pendingPlacements = LimitedDecayQueue<PlaceInfo>(
6666
TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L
6767
) {
68-
info("${it::class.simpleName} at ${it.placeContext.expectedPos.toShortString()} timed out")
69-
mc.world?.setBlockState(it.placeContext.expectedPos, it.placeContext.checkedState)
70-
it.pendingInteractionsList.remove(it.placeContext)
68+
info("${it::class.simpleName} at ${it.context.expectedPos.toShortString()} timed out")
69+
mc.world?.setBlockState(it.context.expectedPos, it.context.checkedState)
70+
it.pendingInteractionsList.remove(it.context)
7171
}
7272

73-
override val blockedPositions
74-
get() = pendingPlacements.map { it.placeContext.expectedPos }
73+
private var placeContexts = emptyList<PlaceContext>()
7574

76-
private var rotation: RotationRequest? = null
77-
private var validRotation = false
75+
override val blockedPositions
76+
get() = pendingPlacements.map { it.context.expectedPos }
7877

7978
fun Any.onPlace(
8079
alwaysListen: Boolean = false,
@@ -95,23 +94,27 @@ object PlaceManager : RequestHandler<PlaceRequest>(), PositionBlocking {
9594
init {
9695
listen<TickEvent.Pre>(priority = Int.MIN_VALUE) {
9796
currentRequest?.let { request ->
98-
val notSneaking = !player.isSneaking
99-
val hotbarRequest = request.hotbarConfig.request(HotbarRequest(request.placeContext.hotbarIndex))
100-
if ((request.placeContext.sneak && notSneaking) || !hotbarRequest.done || !validRotation)
101-
return@listen
102-
103-
val actionResult = placeBlock(request, Hand.MAIN_HAND)
104-
if (!actionResult.isAccepted) {
105-
warn("Placement interaction failed with $actionResult")
106-
}
107-
activeThisTick = true
97+
placeContexts
98+
.forEach { ctx ->
99+
val notSneaking = !player.isSneaking
100+
val hotbarRequest = request.hotbarConfig.request(HotbarRequest(ctx.hotbarIndex))
101+
if ((ctx.sneak && notSneaking) || !hotbarRequest.done || !ctx.rotation.done)
102+
return@listen
103+
104+
val actionResult = placeBlock(ctx, request, Hand.MAIN_HAND)
105+
if (!actionResult.isAccepted) {
106+
warn("Placement interaction failed with $actionResult")
107+
}
108+
activeThisTick = true
109+
}
110+
placeContexts = emptyList()
108111
}
109112
}
110113

111114
onRotate(priority = Int.MIN_VALUE) {
112115
preEvent()
113116

114-
if (!updateRequest { request -> canPlace(request.value.placeContext) }) {
117+
if (!updateRequest()) {
115118
postEvent()
116119
return@onRotate
117120
}
@@ -122,53 +125,56 @@ object PlaceManager : RequestHandler<PlaceRequest>(), PositionBlocking {
122125
}
123126

124127
currentRequest?.let request@ { request ->
125-
if (pendingPlacements.size >= request.buildConfig.placeSettings.maxPendingPlacements) {
126-
postEvent()
127-
return@onRotate
128-
}
128+
pendingPlacements.setMaxSize(request.buildConfig.placeSettings.maxPendingPlacements)
129+
pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L)
129130

130131
val placeConfig = request.buildConfig.placeSettings
131-
rotation = if (placeConfig.rotateForPlace || placeConfig.axisRotate)
132-
request.rotationConfig.request(request.placeContext.rotation)
133-
else null
134132

135-
pendingPlacements.setMaxSize(request.buildConfig.placeSettings.maxPendingPlacements)
136-
pendingPlacements.setDecayTime(request.buildConfig.interactionTimeout * 50L)
133+
val maxPlacementsThisTick = (placeConfig.maxPendingPlacements - pendingPlacements.size).coerceAtLeast(0)
134+
val takeCount = (placeConfig.placementsPerTick.coerceAtMost(maxPlacementsThisTick))
135+
val isSneaking = player.isSneaking
136+
val currentHotbarIndex = HotbarManager.serverSlot
137+
placeContexts = request.placeContexts
138+
.filter { canPlace(it) }
139+
.sortedBy { isSneaking == it.sneak && currentHotbarIndex == it.hotbarIndex }
140+
.take(takeCount)
141+
142+
request.placeContexts.firstOrNull()?.let { ctx ->
143+
if (placeConfig.rotateForPlace || placeConfig.axisRotate)
144+
request.rotationConfig.request(ctx.rotation)
145+
}
137146
}
138-
}
139147

140-
onRotatePost(priority = Int.MIN_VALUE) {
141-
validRotation = rotation?.done ?: true
142148
postEvent()
143149
}
144150

145151
listen<MovementEvent.InputUpdate> {
146-
if (currentRequest?.placeContext?.sneak == true) it.input.sneaking = true
152+
if (placeContexts.firstOrNull()?.sneak == true) it.input.sneaking = true
147153
}
148154

149155
listen<WorldEvent.BlockUpdate.Server> { event ->
150156
pendingPlacements
151-
.firstOrNull { it.placeContext.expectedPos == event.pos }
152-
?.let { request ->
153-
removePendingPlace(request)
157+
.firstOrNull { it.context.expectedPos == event.pos }
158+
?.let { info ->
159+
removePendingPlace(info)
154160

155161
// return if the block wasn't placed
156-
if (!matchesTargetState(event.pos, request.placeContext.targetState, event.newState))
162+
if (!matchesTargetState(event.pos, info.context.targetState, event.newState))
157163
return@listen
158164

159-
if (request.buildConfig.placeSettings.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace)
160-
with (request.placeContext) {
165+
if (info.placeConfig.placeConfirmationMode == PlaceConfig.PlaceConfirmationMode.AwaitThenPlace)
166+
with (info.context) {
161167
placeSound(expectedState.block.item as BlockItem, expectedState, expectedPos)
162168
}
163-
request.onPlace()
169+
info.onPlace()
164170
return@listen
165171
}
166172
}
167173
}
168174

169175
private fun canPlace(placeContext: PlaceContext) =
170176
pendingPlacements.none { pending ->
171-
pending.placeContext.expectedPos == placeContext.expectedPos
177+
pending.context.expectedPos == placeContext.expectedPos
172178
}
173179

174180
private fun SafeContext.matchesTargetState(pos: BlockPos, targetState: TargetState, newState: BlockState) =
@@ -178,15 +184,16 @@ object PlaceManager : RequestHandler<PlaceRequest>(), PositionBlocking {
178184
false
179185
}
180186

181-
private fun SafeContext.placeBlock(request: PlaceRequest, hand: Hand): ActionResult {
187+
private fun SafeContext.placeBlock(placeContext: PlaceContext, request: PlaceRequest, hand: Hand): ActionResult {
182188
interaction.syncSelectedSlot()
183-
val hitResult = request.placeContext.result
189+
val hitResult = placeContext.result
184190
if (!world.worldBorder.contains(hitResult.blockPos)) return ActionResult.FAIL
185191
if (gamemode == GameMode.SPECTATOR) return ActionResult.PASS
186-
return interactBlockInternal(request, request.buildConfig.placeSettings, hand, hitResult)
192+
return interactBlockInternal(placeContext, request, request.buildConfig.placeSettings, hand, hitResult)
187193
}
188194

189195
private fun SafeContext.interactBlockInternal(
196+
placeContext: PlaceContext,
190197
request: PlaceRequest,
191198
placeConfig: PlaceConfig,
192199
hand: Hand,
@@ -212,17 +219,18 @@ object PlaceManager : RequestHandler<PlaceRequest>(), PositionBlocking {
212219
val itemUsageContext = ItemUsageContext(player, hand, hitResult)
213220
return if (gamemode.isCreative) {
214221
val i = itemStack.count
215-
useOnBlock(request, hand, hitResult, placeConfig, itemStack, itemUsageContext)
222+
useOnBlock(placeContext, request, hand, hitResult, placeConfig, itemStack, itemUsageContext)
216223
.also {
217224
itemStack.count = i
218225
}
219226
} else
220-
useOnBlock(request, hand, hitResult, placeConfig, itemStack, itemUsageContext)
227+
useOnBlock(placeContext, request, hand, hitResult, placeConfig, itemStack, itemUsageContext)
221228
}
222229
return ActionResult.PASS
223230
}
224231

225232
private fun SafeContext.useOnBlock(
233+
placeContext: PlaceContext,
226234
request: PlaceRequest,
227235
hand: Hand,
228236
hitResult: BlockHitResult,
@@ -238,10 +246,11 @@ object PlaceManager : RequestHandler<PlaceRequest>(), PositionBlocking {
238246

239247
val item = (itemStack.item as? BlockItem) ?: return ActionResult.PASS
240248

241-
return place(request, hand, hitResult, placeConfig, item, ItemPlacementContext(context))
249+
return place(placeContext, request, hand, hitResult, placeConfig, item, ItemPlacementContext(context))
242250
}
243251

244252
private fun SafeContext.place(
253+
placeContext: PlaceContext,
245254
request: PlaceRequest,
246255
hand: Hand,
247256
hitResult: BlockHitResult,
@@ -258,10 +267,12 @@ object PlaceManager : RequestHandler<PlaceRequest>(), PositionBlocking {
258267
val stackInHand = player.getStackInHand(hand)
259268
val stackCountPre = stackInHand.count
260269
if (placeConfig.placeConfirmationMode != PlaceConfig.PlaceConfirmationMode.None) {
261-
addPendingPlace(request)
270+
addPendingPlace(
271+
PlaceInfo(placeContext, request.onPlace, request.pendingInteractionsList, placeConfig)
272+
)
262273
}
263274

264-
if (request.buildConfig.placeSettings.airPlace == PlaceConfig.AirPlaceMode.Grim) {
275+
if (placeConfig.airPlace == PlaceConfig.AirPlaceMode.Grim) {
265276
val placeHand = if (hand == Hand.MAIN_HAND) Hand.OFF_HAND else Hand.MAIN_HAND
266277
airPlaceOffhandSwap()
267278
sendPlacePacket(placeHand, hitResult)
@@ -270,8 +281,8 @@ object PlaceManager : RequestHandler<PlaceRequest>(), PositionBlocking {
270281
sendPlacePacket(hand, hitResult)
271282
}
272283

273-
if (request.buildConfig.placeSettings.swing) {
274-
swingHand(request.buildConfig.placeSettings.swingType, hand)
284+
if (placeConfig.swing) {
285+
swingHand(placeConfig.swingType, hand)
275286

276287
if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory())) {
277288
mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand)
@@ -332,16 +343,23 @@ object PlaceManager : RequestHandler<PlaceRequest>(), PositionBlocking {
332343
)
333344
}
334345

335-
private fun addPendingPlace(request: PlaceRequest) {
336-
pendingPlacements.add(request)
337-
request.pendingInteractionsList.add(request.placeContext)
346+
private fun addPendingPlace(info: PlaceInfo) {
347+
pendingPlacements.add(info)
348+
info.pendingInteractionsList.add(info.context)
338349
}
339350

340-
private fun removePendingPlace(request: PlaceRequest) {
341-
pendingPlacements.remove(request)
342-
request.pendingInteractionsList.remove(request.placeContext)
351+
private fun removePendingPlace(info: PlaceInfo) {
352+
pendingPlacements.remove(info)
353+
info.pendingInteractionsList.remove(info.context)
343354
}
344355

356+
private data class PlaceInfo(
357+
val context: PlaceContext,
358+
val onPlace: () -> Unit,
359+
val pendingInteractionsList: MutableCollection<BuildContext>,
360+
val placeConfig: PlaceConfig
361+
)
362+
345363
override fun preEvent() = UpdateManagerEvent.Place.Pre().post()
346364
override fun postEvent() = UpdateManagerEvent.Place.Post().post()
347365
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import com.lambda.threading.runSafe
2929
import com.lambda.util.BlockUtils.blockState
3030

3131
data class PlaceRequest(
32-
val placeContext: PlaceContext,
32+
val placeContexts: Collection<PlaceContext>,
3333
val buildConfig: BuildConfig,
3434
val rotationConfig: RotationConfig,
3535
val hotbarConfig: HotbarConfig,
@@ -40,6 +40,6 @@ data class PlaceRequest(
4040
) : Request(prio) {
4141
override val done: Boolean
4242
get() = runSafe {
43-
placeContext.targetState.matches(blockState(placeContext.expectedPos), placeContext.expectedPos, world)
43+
placeContexts.all { it.targetState.matches(blockState(it.expectedPos), it.expectedPos, world) }
4444
} == true
4545
}

common/src/main/kotlin/com/lambda/interaction/request/rotation/RotationManager.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ import com.lambda.Lambda
2121
import com.lambda.context.SafeContext
2222
import com.lambda.core.Loadable
2323
import com.lambda.event.EventFlow.post
24-
import com.lambda.event.events.*
24+
import com.lambda.event.events.ConnectionEvent
25+
import com.lambda.event.events.PacketEvent
26+
import com.lambda.event.events.PlayerPacketEvent
27+
import com.lambda.event.events.RotationEvent
28+
import com.lambda.event.events.TickEvent
29+
import com.lambda.event.events.UpdateManagerEvent
2530
import com.lambda.event.listener.SafeListener.Companion.listen
2631
import com.lambda.event.listener.UnsafeListener.Companion.listenUnsafe
2732
import com.lambda.interaction.request.Priority

0 commit comments

Comments
 (0)