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.breaking
19+
20+ import com.lambda.interaction.construction.context.BreakContext
21+ import com.lambda.util.BlockUtils.calcItemBlockBreakingDelta
22+ import net.minecraft.client.network.ClientPlayerEntity
23+ import net.minecraft.client.network.ClientPlayerInteractionManager
24+ import net.minecraft.client.world.ClientWorld
25+ import net.minecraft.entity.ItemEntity
26+ import net.minecraft.entity.player.PlayerEntity
27+ import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket
28+ import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action
29+
30+ data class BreakInfo (
31+ var context : BreakContext ,
32+ var type : BreakType ,
33+ var request : BreakRequest
34+ ) {
35+ val breakConfig get() = request.buildConfig.breakSettings
36+ val rotationConfig get() = request.rotationConfig
37+
38+ val pendingInteractionsList get() = request.pendingInteractionsList
39+ private val onCancel get() = request.onCancel
40+ private val onBreak get() = request.onBreak
41+ private val onItemDrop get() = request.onItemDrop
42+
43+ var breaking = false
44+ var breakingTicks = 0
45+ var soundsCooldown = 0.0f
46+
47+ val redundant
48+ get() = type == BreakType .RedundantSecondary
49+
50+ @Volatile
51+ var broken = false
52+ private set
53+ private var item: ItemEntity ? = null
54+
55+ val callbacksCompleted
56+ @Synchronized get() = broken && (onItemDrop == null || item != null )
57+
58+ fun internalOnBreak () {
59+ synchronized(this ) {
60+ broken = true
61+ onBreak?.invoke(context.expectedPos)
62+ item?.let { item ->
63+ onItemDrop?.invoke(item)
64+ }
65+ }
66+ }
67+
68+ fun internalOnItemDrop (item : ItemEntity ) {
69+ synchronized(this ) {
70+ this .item = item
71+ if (broken) {
72+ onItemDrop?.invoke(item)
73+ }
74+ }
75+ }
76+
77+ fun internalOnCancel () {
78+ onCancel?.invoke(context.expectedPos)
79+ }
80+
81+ fun updateInfo (context : BreakContext , request : BreakRequest ) {
82+ this .context = context
83+ this .request = request
84+ if (redundant) {
85+ type = BreakType .Secondary
86+ }
87+ }
88+
89+ fun setBreakingTextureStage (
90+ player : ClientPlayerEntity ,
91+ world : ClientWorld ,
92+ stage : Int = getBreakTextureProgress(player, world)
93+ ) {
94+ world.setBlockBreakingInfo(player.id, context.expectedPos, stage)
95+ }
96+
97+ private fun getBreakTextureProgress (player : PlayerEntity , world : ClientWorld ): Int {
98+ val breakDelta = context.checkedState.calcItemBlockBreakingDelta(player, world, context.expectedPos, player.mainHandStack)
99+ val progress = (breakDelta * breakingTicks) / getBreakThreshold()
100+ return if (progress > 0.0f ) (progress * 10.0f ).toInt() else - 1
101+ }
102+
103+ fun getBreakThreshold () = type.getBreakThreshold(breakConfig)
104+
105+ fun startBreakPacket (world : ClientWorld , interaction : ClientPlayerInteractionManager ) =
106+ breakPacket(Action .START_DESTROY_BLOCK , world, interaction)
107+
108+ fun stopBreakPacket (world : ClientWorld , interaction : ClientPlayerInteractionManager ) =
109+ breakPacket(Action .STOP_DESTROY_BLOCK , world, interaction)
110+
111+ fun abortBreakPacket (world : ClientWorld , interaction : ClientPlayerInteractionManager ) =
112+ breakPacket(Action .ABORT_DESTROY_BLOCK , world, interaction)
113+
114+ private fun breakPacket (action : Action , world : ClientWorld , interaction : ClientPlayerInteractionManager ) =
115+ interaction.sendSequencedPacket(world) { sequence: Int ->
116+ PlayerActionC2SPacket (
117+ action,
118+ context.expectedPos,
119+ context.result.side,
120+ sequence
121+ )
122+ }
123+ }
124+
125+ enum class BreakType (val index : Int ) {
126+ Primary (0 ),
127+ Secondary (1 ),
128+ RedundantSecondary (2 );
129+
130+ fun getBreakThreshold (breakConfig : BreakConfig ) =
131+ when (this ) {
132+ Primary -> breakConfig.breakThreshold
133+ else -> 1.0f
134+ }
135+ }
0 commit comments