Skip to content

Commit 21705b1

Browse files
committed
- more placement customisation settings
- account for max pending breaks in break manager - split pending interactions to breaks and placements
1 parent 6a61c5c commit 21705b1

File tree

9 files changed

+184
-32
lines changed

9 files changed

+184
-32
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,14 @@ class BreakSettings(
3232
override val doubleBreak by c.setting("Double Break", false, "Allows breaking two blocks at once") { vis() }
3333
override val breakDelay by c.setting("Break Delay", 5, 0..5, 1, "The delay between breaking blocks", " ticks") { vis() }
3434
override val swing by c.setting("Swing Mode", SwingMode.Constant, "The times at which to swing the players hand") { vis() }
35-
override val swingType by c.setting("Swing Type", SwingType.Vanilla, "The style of swing") { vis() }
36-
override val sounds by c.setting("Sounds", true, "Plays the breaking sounds") { vis() }
35+
override val swingType by c.setting("Break Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() }
36+
override val sounds by c.setting("Break Sounds", true, "Plays the breaking sounds") { vis() }
3737
override val particles by c.setting("Particles", true, "Renders the breaking particles") { vis() }
3838
override val breakingTexture by c.setting("Breaking Overlay", true, "Overlays the breaking texture at its different stages") { vis() }
3939
override val rotateForBreak by c.setting("Rotate For Break", true, "Rotate towards block while breaking") { vis() }
4040
override val ignoredBlocks by c.setting("Ignored Blocks", allSigns, "Blocks that wont be broken") { vis() }
4141
override val breakConfirmation by c.setting("Break Confirmation", BreakConfirmationMode.BreakThenAwait, "The style of confirmation used when breaking") { vis() }
42+
override val maxPendingBreaks by c.setting("Max Pending Breaks", 2, 0..5, 1, "The maximum amount of pending breaks") { vis() }
4243
override val breaksPerTick by c.setting("Instant Breaks Per Tick", 5, 1..30, 1, "Maximum instant block breaks per tick") { vis() }
4344
override val breakWeakBlocks by c.setting("Break Weak Blocks", false, "Break blocks that dont have structural integrity (e.g: grass)") { vis() }
4445
override val forceSilkTouch by c.setting("Force Silk Touch", false, "Force silk touch when breaking blocks") { vis() }

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,10 @@ interface BuildConfig {
3030

3131
// Placing
3232
val placeSettings: PlaceSettings
33+
34+
enum class SwingType {
35+
Vanilla,
36+
Server,
37+
Client
38+
}
3339
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,9 @@ class PlaceSettings(
2828
) : PlaceConfig(priority) {
2929
override val rotateForPlace by c.setting("Rotate For Place", true, "Rotate towards block while placing") { vis() }
3030
override val placeConfirmation by c.setting("Place Confirmation", PlaceConfirmation.PlaceThenAwait, "Wait for block placement confirmation") { vis() }
31+
override val maxPendingPlacements by c.setting("Max Pending Placements", 2, 0..5, 1, "The maximum amount of pending placements") { vis() }
3132
override val placementsPerTick by c.setting("Instant Places Per Tick", 1, 1..30, 1, "Maximum instant block places per tick") { vis() }
33+
override val swing by c.setting("Swing", true, "Swings the players hand when placing") { vis() }
34+
override val swingType by c.setting("Place Swing Type", BuildConfig.SwingType.Vanilla, "The style of swing") { vis() }
35+
override val sounds by c.setting("Place Sounds", true, "Plays the placing sounds") { vis() }
3236
}

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

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

20+
import com.lambda.config.groups.BuildConfig
2021
import com.lambda.interaction.request.Priority
2122
import com.lambda.interaction.request.RequestConfig
2223
import net.minecraft.block.Block
@@ -29,12 +30,13 @@ abstract class BreakConfig(
2930
abstract val doubleBreak: Boolean
3031
abstract val breakDelay: Int
3132
abstract val swing: SwingMode
32-
abstract val swingType: SwingType
33+
abstract val swingType: BuildConfig.SwingType
3334
abstract val sounds: Boolean
3435
abstract val particles: Boolean
3536
abstract val breakingTexture: Boolean
3637
abstract val rotateForBreak: Boolean
3738
abstract val breakConfirmation: BreakConfirmationMode
39+
abstract val maxPendingBreaks: Int
3840
abstract val breaksPerTick: Int
3941
abstract val breakWeakBlocks: Boolean
4042
abstract val forceSilkTouch: Boolean
@@ -58,12 +60,6 @@ abstract class BreakConfig(
5860
End
5961
}
6062

61-
enum class SwingType {
62-
Vanilla,
63-
Server,
64-
Client
65-
}
66-
6763
enum class BreakConfirmationMode {
6864
None,
6965
BreakThenAwait,

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

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import com.lambda.util.Communication.info
4141
import com.lambda.util.Communication.warn
4242
import com.lambda.util.collections.LimitedDecayQueue
4343
import com.lambda.util.item.ItemUtils.block
44-
import com.lambda.util.player.swingHandClient
44+
import com.lambda.util.player.swingHand
4545
import net.minecraft.block.BlockState
4646
import net.minecraft.block.OperatorBlock
4747
import net.minecraft.client.sound.PositionedSoundInstance
@@ -50,7 +50,6 @@ import net.minecraft.client.world.ClientWorld
5050
import net.minecraft.entity.ItemEntity
5151
import net.minecraft.entity.player.PlayerEntity
5252
import net.minecraft.item.ItemStack
53-
import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket
5453
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket
5554
import net.minecraft.sound.SoundCategory
5655
import net.minecraft.util.math.BlockPos
@@ -87,8 +86,11 @@ object BreakManager : RequestHandler<BreakRequest>() {
8786
if (updateRequest { true }) {
8887
currentRequest?.let request@ { request ->
8988
var instaBreaks = 0
89+
val config = breakingInfos.firstOrNull()?.breakConfig ?: request.buildConfig.breakSettings
90+
val takeCount = config.maxPendingBreaks - (breakingInfos.count { it != null } + pendingInteractions.size)
9091
request.contexts
9192
.sortedBy { it.instantBreak }
93+
.take(takeCount)
9294
.forEach { requestCtx ->
9395
if (!canAccept(requestCtx)) return@forEach
9496
val breakType = with(request) {
@@ -242,7 +244,7 @@ object BreakManager : RequestHandler<BreakRequest>() {
242244
info.nullify()
243245
return false
244246
}
245-
if (info.breakConfig.swing != BreakConfig.SwingMode.End) swingHand(info)
247+
if (info.breakConfig.swing != BreakConfig.SwingMode.End) swingHand(info.breakConfig.swingType)
246248
return true
247249
}
248250

@@ -293,10 +295,10 @@ object BreakManager : RequestHandler<BreakRequest>() {
293295
onBlockBreak(info)
294296
PlayerActionC2SPacket(PlayerActionC2SPacket.Action.STOP_DESTROY_BLOCK, ctx.expectedPos, hitResult.side, sequence)
295297
}
296-
if (info.breakConfig.swing != BreakConfig.SwingMode.Start) swingHand(info)
298+
if (info.breakConfig.swing != BreakConfig.SwingMode.Start) swingHand(info.breakConfig.swingType)
297299
setBreakCooldown(info.breakConfig.breakDelay)
298300
} else {
299-
if (info.breakConfig.swing == BreakConfig.SwingMode.Constant) swingHand(info)
301+
if (info.breakConfig.swing == BreakConfig.SwingMode.Constant) swingHand(info.breakConfig.swingType)
300302
}
301303

302304
return true
@@ -409,14 +411,6 @@ object BreakManager : RequestHandler<BreakRequest>() {
409411
)
410412
}
411413

412-
private fun SafeContext.swingHand(info: BreakInfo) {
413-
when (info.breakConfig.swingType) {
414-
BreakConfig.SwingType.Vanilla -> player.swingHand(player.activeHand)
415-
BreakConfig.SwingType.Server -> connection.sendPacket(HandSwingC2SPacket(player.activeHand))
416-
BreakConfig.SwingType.Client -> swingHandClient(player.activeHand)
417-
}
418-
}
419-
420414
private fun isOnBreakCooldown() = blockBreakingCooldown > 0
421415
private fun setBreakCooldown(cooldown: Int) {
422416
blockBreakingCooldown = cooldown

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

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

20+
import com.lambda.config.groups.BuildConfig
2021
import com.lambda.interaction.request.Priority
2122
import com.lambda.interaction.request.RequestConfig
2223

@@ -25,7 +26,11 @@ abstract class PlaceConfig(
2526
) : RequestConfig<PlaceRequest>(priority) {
2627
abstract val rotateForPlace: Boolean
2728
abstract val placeConfirmation: PlaceConfirmation
29+
abstract val maxPendingPlacements: Int
2830
abstract val placementsPerTick: Int
31+
abstract val swing: Boolean
32+
abstract val swingType: BuildConfig.SwingType
33+
abstract val sounds: Boolean
2934

3035
override fun requestInternal(request: PlaceRequest) {
3136
PlaceManager.registerRequest(this, request)

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

Lines changed: 143 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,30 @@ import com.lambda.module.modules.client.TaskFlowModule
3131
import com.lambda.util.Communication.info
3232
import com.lambda.util.Communication.warn
3333
import com.lambda.util.collections.LimitedDecayQueue
34+
import com.lambda.util.player.swingHand
35+
import net.minecraft.block.BlockState
36+
import net.minecraft.block.pattern.CachedBlockPosition
37+
import net.minecraft.item.BlockItem
38+
import net.minecraft.item.ItemPlacementContext
39+
import net.minecraft.item.ItemStack
40+
import net.minecraft.item.ItemUsageContext
41+
import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket
42+
import net.minecraft.registry.RegistryKeys
43+
import net.minecraft.sound.SoundCategory
44+
import net.minecraft.stat.Stats
45+
import net.minecraft.util.ActionResult
3446
import net.minecraft.util.Hand
47+
import net.minecraft.util.hit.BlockHitResult
48+
import net.minecraft.world.GameMode
49+
import net.minecraft.world.event.GameEvent
50+
import org.apache.commons.lang3.mutable.MutableObject
3551

3652
object PlaceManager : RequestHandler<PlaceRequest>() {
3753
private val pendingInteractions = LimitedDecayQueue<PlaceContext>(
3854
TaskFlowModule.build.maxPendingInteractions, TaskFlowModule.build.interactionTimeout * 50L
3955
) { info("${it::class.simpleName} at ${it.expectedPos.toShortString()} timed out") }
4056

57+
//ToDo: Add server response check for pending interactions
4158
init {
4259
listen<TickEvent.Pre>(Int.MIN_VALUE) {
4360
preEvent()
@@ -47,7 +64,11 @@ object PlaceManager : RequestHandler<PlaceRequest>() {
4764
return@listen
4865
}
4966

50-
currentRequest?.let { request ->
67+
currentRequest?.let request@ { request ->
68+
if (pendingInteractions.size >= request.buildConfig.placeSettings.maxPendingPlacements) {
69+
return@request
70+
}
71+
5172
if (request.placeContext.sneak && !player.isSneaking
5273
|| (request.buildConfig.placeSettings.rotateForPlace && !request.placeContext.rotation.done)
5374
|| (!request.hotbarConfig.request(HotbarRequest(request.placeContext.hotbarIndex)).done)
@@ -76,24 +97,136 @@ object PlaceManager : RequestHandler<PlaceRequest>() {
7697
}
7798

7899
private fun SafeContext.placeBlock(request: PlaceRequest, hand: Hand) {
79-
val actionResult = interaction.interactBlock(
80-
player, hand, request.placeContext.result
81-
)
100+
val stackInHand = player.getStackInHand(hand)
101+
val stackCountPre = stackInHand.count
102+
val actionResult = interactBlock(request.buildConfig.placeSettings, hand, request.placeContext.result)
82103

83104
if (actionResult.isAccepted) {
84-
if (actionResult.shouldSwingHand() && request.interactionConfig.swingHand) {
85-
player.swingHand(hand)
86-
}
105+
if (request.buildConfig.placeSettings.placeConfirmation != PlaceConfig.PlaceConfirmation.None)
106+
pendingInteractions.add(request.placeContext)
87107

88-
if (!player.getStackInHand(hand).isEmpty && interaction.hasCreativeInventory()) {
108+
if (actionResult.shouldSwingHand() && request.buildConfig.placeSettings.swing)
109+
swingHand(request.buildConfig.placeSettings.swingType)
110+
111+
if (!stackInHand.isEmpty && (stackInHand.count != stackCountPre || interaction.hasCreativeInventory()))
89112
mc.gameRenderer.firstPersonRenderer.resetEquipProgress(hand)
90-
}
91113
} else {
92-
warn("Internal interaction failed with $actionResult")
114+
warn("Placement interaction failed with $actionResult")
93115
}
94116
request.onPlace()
95117
}
96118

119+
private fun SafeContext.interactBlock(placeConfig: PlaceConfig, hand: Hand, hitResult: BlockHitResult): ActionResult {
120+
interaction.syncSelectedSlot()
121+
if (!world.worldBorder.contains(hitResult.blockPos)) {
122+
return ActionResult.FAIL
123+
} else {
124+
val mutableObject = MutableObject<ActionResult>()
125+
interaction.sendSequencedPacket(world) { sequence: Int ->
126+
mutableObject.value = interactBlockInternal(placeConfig, hand, hitResult)
127+
PlayerInteractBlockC2SPacket(hand, hitResult, sequence)
128+
}
129+
return mutableObject.value
130+
}
131+
}
132+
133+
private fun SafeContext.interactBlockInternal(
134+
placeConfig: PlaceConfig,
135+
hand: Hand,
136+
hitResult: BlockHitResult
137+
): ActionResult {
138+
val itemStack = player.getStackInHand(hand)
139+
if (interaction.currentGameMode == GameMode.SPECTATOR) return ActionResult.SUCCESS
140+
141+
val handNotEmpty = !player.getStackInHand(hand).isEmpty
142+
val cantInteract = player.shouldCancelInteraction() && handNotEmpty
143+
if (!cantInteract) return ActionResult.PASS
144+
145+
if (!itemStack.isEmpty && !player.itemCooldownManager.isCoolingDown(itemStack.item)) {
146+
val itemUsageContext = ItemUsageContext(player, hand, hitResult)
147+
val itemUseResult: ActionResult
148+
if (interaction.currentGameMode.isCreative) {
149+
val i = itemStack.count
150+
itemUseResult = useOnBlock(placeConfig, itemStack, itemUsageContext)
151+
itemStack.count = i
152+
} else
153+
itemUseResult = useOnBlock(placeConfig, itemStack, itemUsageContext)
154+
155+
return itemUseResult
156+
}
157+
return ActionResult.PASS
158+
}
159+
160+
private fun SafeContext.useOnBlock(
161+
placeConfig: PlaceConfig,
162+
itemStack: ItemStack,
163+
context: ItemUsageContext
164+
): ActionResult {
165+
val blockPos = context.blockPos
166+
val cachedBlockPosition = CachedBlockPosition(context.world, blockPos, false)
167+
if (!player.abilities.allowModifyWorld
168+
&& !itemStack.canPlaceOn(context.world.registryManager.get(RegistryKeys.BLOCK), cachedBlockPosition)
169+
) {
170+
return ActionResult.PASS
171+
}
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))
175+
176+
return actionResult
177+
}
178+
179+
private fun SafeContext.useOnBlock(
180+
placeConfig: PlaceConfig,
181+
item: BlockItem,
182+
context: ItemUsageContext
183+
) = place(placeConfig, item, ItemPlacementContext(context))
184+
185+
private fun SafeContext.place(
186+
placeConfig: PlaceConfig,
187+
item: BlockItem,
188+
context: ItemPlacementContext
189+
): ActionResult {
190+
if (!item.block.isEnabled(world.enabledFeatures)) return ActionResult.FAIL
191+
if (!context.canPlace()) return ActionResult.FAIL
192+
193+
val itemPlacementContext: ItemPlacementContext = item.getPlacementContext(context) ?: return ActionResult.FAIL
194+
val blockState: BlockState = item.getPlacementState(itemPlacementContext) ?: return ActionResult.FAIL
195+
196+
if (placeConfig.placeConfirmation == PlaceConfig.PlaceConfirmation.AwaitThenPlace)
197+
return ActionResult.success(world.isClient)
198+
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
202+
if (!item.place(itemPlacementContext, blockState)) return ActionResult.FAIL
203+
204+
val blockPos = itemPlacementContext.blockPos
205+
val itemStack = itemPlacementContext.stack
206+
var hitState = world.getBlockState(blockPos)
207+
if (hitState.isOf(blockState.block)) {
208+
hitState = item.placeFromNbt(blockPos, world, itemStack, hitState)
209+
item.postPlacement(blockPos, world, player, itemStack, hitState)
210+
hitState.block.onPlaced(world, blockPos, hitState, player, itemStack)
211+
}
212+
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))
225+
if (!player.abilities.creativeMode) itemStack.decrement(1)
226+
227+
return ActionResult.success(world.isClient)
228+
}
229+
97230
override fun preEvent() = UpdateManagerEvent.Place.Pre().post()
98231
override fun postEvent() = UpdateManagerEvent.Place.Post().post()
99232
}

common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package com.lambda.util.player
22

3+
import com.lambda.config.groups.BuildConfig
34
import com.lambda.context.SafeContext
45
import com.mojang.authlib.GameProfile
56
import net.minecraft.client.network.ClientPlayerEntity
67
import net.minecraft.client.network.OtherClientPlayerEntity
78
import net.minecraft.client.network.PlayerListEntry
89
import net.minecraft.entity.player.PlayerEntity
10+
import net.minecraft.network.packet.c2s.play.HandSwingC2SPacket
911
import net.minecraft.util.Hand
1012

1113
fun SafeContext.copyPlayer(entity: ClientPlayerEntity) =
@@ -41,6 +43,13 @@ fun SafeContext.spawnFakePlayer(
4143
return entity
4244
}
4345

46+
fun SafeContext.swingHand(swingType: BuildConfig.SwingType) =
47+
when (swingType) {
48+
BuildConfig.SwingType.Vanilla -> player.swingHand(player.activeHand)
49+
BuildConfig.SwingType.Server -> connection.sendPacket(HandSwingC2SPacket(player.activeHand))
50+
BuildConfig.SwingType.Client -> swingHandClient(player.activeHand)
51+
}
52+
4453
fun SafeContext.swingHandClient(hand: Hand) {
4554
if (!player.handSwinging || player.handSwingTicks >= player.handSwingDuration / 2 || player.handSwingTicks < 0) {
4655
player.handSwingTicks = -1

common/src/main/resources/lambda.accesswidener

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ accessible field net/minecraft/client/world/ClientChunkManager$ClientChunkMap ch
1616
accessible field net/minecraft/client/network/ClientPlayerInteractionManager currentBreakingProgress F
1717
accessible field net/minecraft/client/network/ClientPlayerInteractionManager blockBreakingCooldown I
1818
accessible field net/minecraft/client/network/ClientPlayerInteractionManager currentBreakingPos Lnet/minecraft/util/math/BlockPos;
19+
accessible method net/minecraft/item/BlockItem place (Lnet/minecraft/item/ItemPlacementContext;Lnet/minecraft/block/BlockState;)Z
20+
accessible method net/minecraft/item/BlockItem placeFromNbt (Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/World;Lnet/minecraft/item/ItemStack;Lnet/minecraft/block/BlockState;)Lnet/minecraft/block/BlockState;
21+
accessible method net/minecraft/item/BlockItem postPlacement (Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/block/BlockState;)Z
22+
accessible method net/minecraft/item/BlockItem getPlaceSound (Lnet/minecraft/block/BlockState;)Lnet/minecraft/sound/SoundEvent;
1923

2024
# Entity
2125
accessible field net/minecraft/entity/projectile/FireworkRocketEntity shooter Lnet/minecraft/entity/LivingEntity;

0 commit comments

Comments
 (0)