Skip to content

Commit 77277cf

Browse files
committed
Movement tracker with smart rubberband analytics
1 parent 846ecdb commit 77277cf

File tree

3 files changed

+113
-55
lines changed

3 files changed

+113
-55
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.lambda.interaction
2+
3+
import com.lambda.interaction.rotation.Rotation
4+
import net.minecraft.util.math.Vec3d
5+
6+
data class MovementConfiguration(
7+
var position: Vec3d,
8+
var rotation: Rotation,
9+
var onGround: Boolean,
10+
var sprinting: Boolean,
11+
var sneaking: Boolean
12+
)

common/src/main/kotlin/com/lambda/interaction/PlayerPacketManager.kt

Lines changed: 80 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,148 @@
11
package com.lambda.interaction
22

3-
import com.lambda.Loadable
3+
import com.lambda.core.Loadable
44
import com.lambda.context.SafeContext
5-
import com.lambda.event.EventFlow
5+
import com.lambda.event.EventFlow.post
6+
import com.lambda.event.EventFlow.postChecked
7+
import com.lambda.event.events.PacketEvent
68
import com.lambda.event.events.PlayerPacketEvent
7-
import com.lambda.interaction.rotation.Rotation
9+
import com.lambda.event.listener.SafeListener.Companion.listener
810
import com.lambda.threading.runSafe
11+
import com.lambda.util.Communication.warn
12+
import com.lambda.util.collections.LimitedOrderedSet
13+
import com.lambda.util.math.VecUtils.dist
914
import com.lambda.util.math.VecUtils.distSq
1015
import com.lambda.util.player.MovementUtils.motionX
1116
import com.lambda.util.player.MovementUtils.motionZ
1217
import com.lambda.util.primitives.extension.component1
1318
import com.lambda.util.primitives.extension.component2
1419
import com.lambda.util.primitives.extension.component3
20+
import com.lambda.util.text.Color
21+
import com.lambda.util.text.buildText
22+
import com.lambda.util.text.color
23+
import com.lambda.util.text.literal
1524
import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket
1625
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.*
26+
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket
1727
import net.minecraft.util.math.MathHelper.square
1828
import net.minecraft.util.math.Vec3d
1929

2030
object PlayerPacketManager : Loadable {
21-
private var prevPosition: Vec3d = Vec3d.ZERO
22-
private var lastPosition = Vec3d(0.0, -1000.0, 0.0)
23-
private var lastRotation = Rotation(0.0, -10000.0)
24-
private var lastOnGround: Boolean? = null
25-
private var lastSprinting: Boolean? = null
26-
private var lastSneaking: Boolean? = null
31+
private val configurations = LimitedOrderedSet<PlayerPacketEvent.Pre>(100)
2732
private var sendTicks = 0
2833

2934
@JvmStatic
3035
fun sendPlayerPackets() {
3136
runSafe {
32-
EventFlow.post(
33-
PlayerPacketEvent.Pre(
34-
player.pos,
35-
RotationManager.currentRotation,
36-
player.isOnGround,
37-
player.isSprinting
38-
)
39-
) {
37+
PlayerPacketEvent.Pre(
38+
player.pos,
39+
RotationManager.currentRotation,
40+
player.isOnGround,
41+
player.isSprinting,
42+
player.isSneaking
43+
).post {
4044
updatePlayerPackets(this)
4145
}
4246
}
4347
}
4448

45-
private fun SafeContext.updatePlayerPackets(event: PlayerPacketEvent.Pre) {
46-
reportSprint(event.isSprinting)
47-
reportSneak(player.isSneaking)
49+
init {
50+
listener<PacketEvent.Receive.Pre> { event ->
51+
if (event.packet !is PlayerPositionLookS2CPacket) return@listener
4852

49-
if (mc.cameraEntity != player) return
53+
if (configurations.isEmpty()) {
54+
warn("Position was reverted", "Rubberband")
55+
return@listener
56+
}
57+
58+
val newPos = Vec3d(event.packet.x, event.packet.y, event.packet.z)
59+
val last = configurations.minBy {
60+
it.position distSq newPos
61+
}
62+
val delta = last.position dist newPos
63+
64+
warn(buildText {
65+
literal("Reverted position by ")
66+
color(Color.YELLOW) {
67+
literal("${configurations.reversed().indexOf(last) + 1}")
68+
}
69+
literal(" ticks (derivation: ")
70+
color(Color.YELLOW) {
71+
literal("%.3f".format(delta))
72+
}
73+
literal(")")
74+
}, "Rubberband")
75+
}
76+
}
5077

51-
val position = event.position
52-
val rotation = event.rotation
53-
val ground = event.onGround
78+
private fun SafeContext.updatePlayerPackets(new: PlayerPacketEvent.Pre) {
79+
val previous = configurations.lastOrNull() ?: new
80+
configurations.add(new)
5481

55-
RotationManager.currentRotation = rotation
82+
reportSprint(previous, new)
83+
reportSneak(previous, new)
84+
85+
if (mc.cameraEntity != player) return
86+
87+
RotationManager.currentRotation = new.rotation
5688

5789
if (player.hasVehicle()) {
5890
connection.sendPacket(
5991
Full(
6092
player.motionX,
6193
-999.0,
6294
player.motionZ,
63-
rotation.yaw.toFloat(),
64-
rotation.pitch.toFloat(),
65-
ground
95+
new.rotation.yaw.toFloat(),
96+
new.rotation.pitch.toFloat(),
97+
new.onGround
6698
)
6799
)
68-
lastRotation = rotation
69100
return
70101
}
71102

72-
val updatePosition = (position.subtract(lastPosition) distSq Vec3d.ZERO) > square(2.0E-4) || ++sendTicks >= 20
73-
val updateRotation = rotation != lastRotation
103+
val updatePosition = (new.position.subtract(previous.position) distSq Vec3d.ZERO) > square(2.0E-4) || ++sendTicks >= 20
104+
val updateRotation = new.rotation != previous.rotation
74105

75-
val (x, y, z) = position
106+
val (x, y, z) = new.position
76107

77-
val (yawD, pitchD) = rotation
108+
val (yawD, pitchD) = new.rotation
78109
val (yaw, pitch) = yawD.toFloat() to pitchD.toFloat()
79110

80111
val packet = when {
81112
updatePosition && updateRotation -> {
82-
Full(x, y, z, yaw, pitch, ground)
113+
Full(x, y, z, yaw, pitch, new.onGround)
83114
}
84115

85116
updatePosition -> {
86-
PositionAndOnGround(x, y, z, ground)
117+
PositionAndOnGround(x, y, z, new.onGround)
87118
}
88119

89120
updateRotation -> {
90-
LookAndOnGround(yaw, pitch, ground)
121+
LookAndOnGround(yaw, pitch, new.onGround)
91122
}
92123

93-
lastOnGround != ground -> {
94-
OnGroundOnly(ground)
124+
previous.onGround != new.onGround -> {
125+
OnGroundOnly(new.onGround)
95126
}
96127

97128
else -> null
98129
}
99130

100131
if (updatePosition) {
101-
prevPosition = lastPosition
102-
lastPosition = position
103132
sendTicks = 0
104133
}
105-
if (updateRotation) {
106-
lastRotation = rotation
107-
}
108-
109-
lastOnGround = ground
110134

111135
packet?.let {
112-
EventFlow.postChecked(PlayerPacketEvent.Post(it)) {
136+
PlayerPacketEvent.Post(it).postChecked {
113137
connection.sendPacket(this.packet)
114138
}
115139
}
116140
}
117141

118-
private fun SafeContext.reportSprint(isSprinting: Boolean) {
119-
if (lastSprinting == isSprinting) return
120-
lastSprinting = isSprinting
142+
private fun SafeContext.reportSprint(previous: PlayerPacketEvent.Pre, new: PlayerPacketEvent.Pre) {
143+
if (previous.isSprinting == new.isSprinting) return
121144

122-
val state = if (isSprinting) {
145+
val state = if (new.isSprinting) {
123146
ClientCommandC2SPacket.Mode.START_SPRINTING
124147
} else {
125148
ClientCommandC2SPacket.Mode.STOP_SPRINTING
@@ -128,12 +151,14 @@ object PlayerPacketManager : Loadable {
128151
connection.sendPacket(ClientCommandC2SPacket(player, state))
129152
}
130153

131-
private fun SafeContext.reportSneak(flag: Boolean) {
132-
if (lastSneaking == flag) return
133-
lastSneaking = flag
154+
private fun SafeContext.reportSneak(previous: PlayerPacketEvent.Pre, new: PlayerPacketEvent.Pre) {
155+
if (previous.isSneaking == new.isSneaking) return
134156

135-
val state = if (flag) ClientCommandC2SPacket.Mode.PRESS_SHIFT_KEY
136-
else ClientCommandC2SPacket.Mode.RELEASE_SHIFT_KEY
157+
val state = if (new.isSneaking) {
158+
ClientCommandC2SPacket.Mode.PRESS_SHIFT_KEY
159+
} else {
160+
ClientCommandC2SPacket.Mode.RELEASE_SHIFT_KEY
161+
}
137162

138163
connection.sendPacket(ClientCommandC2SPacket(player, state))
139164
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.lambda.util.collections
2+
3+
class LimitedOrderedSet<E>(private val maxSize: Int) : LinkedHashSet<E>() {
4+
override fun add(element: E): Boolean {
5+
val added = super.add(element)
6+
if (size > maxSize) {
7+
val iterator = iterator()
8+
while (size > maxSize && iterator.hasNext()) {
9+
iterator.next()
10+
iterator.remove()
11+
}
12+
}
13+
return added
14+
}
15+
16+
override fun addAll(elements: Collection<E>): Boolean {
17+
var added = false
18+
elements.forEach { if (add(it)) added = true }
19+
return added
20+
}
21+
}

0 commit comments

Comments
 (0)