Fix set player position flag
This commit is contained in:
14 files changed
+222
-139
No files matched your search
@@ -80,3 +80,53 @@ client.networkChannel.sendPacket(
|
|||||||
ServerboundChatCommandPacket(command = "say Hello from libmc")
|
ServerboundChatCommandPacket(command = "say Hello from libmc")
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Respond velocity and update client motion
|
||||||
|
|
||||||
|
> This part uses math calculations
|
||||||
|
|
||||||
|
When joined to the level(aka `world`), the server will send a packet
|
||||||
|
`ClientboundSetEntityVelocityPacket` to the client, packet contains a vec3 and entity id,
|
||||||
|
The client sync this data to the player and sends it to the server during the next tick loop
|
||||||
|
to inform the server: "Hi, I know my current position; here is the result of my calculations. I'm sending it to you".
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
private var entityId = -1
|
||||||
|
private var motionX = 0
|
||||||
|
private var motionY = 0
|
||||||
|
private var motionZ = 0
|
||||||
|
private var isOnGround = false
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
// Set entity id
|
||||||
|
client.onPacket<ClientboundLoginPlayPacket> { entityId = it.entityId }
|
||||||
|
|
||||||
|
client.onPacket<ClientboundSetEntityVelocityPacket> {
|
||||||
|
if (it.entityId == entityId) {
|
||||||
|
motionX = it.velocity.x / 8000.0
|
||||||
|
motionY = it.velocity.y / 8000.0
|
||||||
|
motionZ = it.velocity.z / 8000.0
|
||||||
|
if (client.motionY > 0) client.isOnGround = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update current position and velocity
|
||||||
|
client.onTick {
|
||||||
|
if (client.stateMachine.currentState != ProtocolState.PLAY) return@registerListener
|
||||||
|
syncPlayerPosition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal suspend fun syncPlayerPosition() {
|
||||||
|
position.x += motionX
|
||||||
|
position.y += motionY
|
||||||
|
position.z += motionZ
|
||||||
|
motionX *= 0.91
|
||||||
|
motionY *= 0.98
|
||||||
|
motionZ *= 0.91
|
||||||
|
if (!isOnGround) motionY -= 0.08 else {
|
||||||
|
if (motionY < 0) motionY = 0.0
|
||||||
|
}
|
||||||
|
networkChannel.sendPacket(ServerboundSetPlayerPositionPacket(position.x, position.y, position.z, isOnGround))
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -25,7 +25,7 @@ public class ProtocolContextBuilder(private val onlineMode: Boolean) {
|
|||||||
if (::authProvider.isInitialized) authProvider else error("authProvider is required in online mode")
|
if (::authProvider.isInitialized) authProvider else error("authProvider is required in online mode")
|
||||||
} else if (::authProvider.isInitialized) authProvider else null,
|
} else if (::authProvider.isInitialized) authProvider else null,
|
||||||
|
|
||||||
engine = if (::socketEngine.isInitialized) socketEngine else error("SocketEngine is not configured")
|
engine = if (::socketEngine.isInitialized) socketEngine else error("SocketEngine is not configured"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 RTAkland
|
||||||
|
* Author: RTAkland
|
||||||
|
* Date: 2026/9/9
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cn.rtast.libmc.protocol.client
|
||||||
|
|
||||||
|
import cn.rtast.libmc.protocol.crypto.minecraftServerIdHash
|
||||||
|
import cn.rtast.libmc.protocol.crypto.rsaEncrypt
|
||||||
|
import cn.rtast.libmc.protocol.packet.configuration.clientbound.*
|
||||||
|
import cn.rtast.libmc.protocol.packet.configuration.serverbound.*
|
||||||
|
import cn.rtast.libmc.protocol.packet.login.clientbound.ClientboundDisconnectLoginPacket
|
||||||
|
import cn.rtast.libmc.protocol.packet.login.clientbound.ClientboundHelloPacket
|
||||||
|
import cn.rtast.libmc.protocol.packet.login.clientbound.ClientboundLoginSuccessPacket
|
||||||
|
import cn.rtast.libmc.protocol.packet.login.clientbound.ClientboundSetCompressionPacket
|
||||||
|
import cn.rtast.libmc.protocol.packet.login.serverbound.ServerboundKeyPacket
|
||||||
|
import cn.rtast.libmc.protocol.packet.login.serverbound.ServerboundLoginAcknowledgedPacket
|
||||||
|
import cn.rtast.libmc.protocol.packet.play.clientbound.*
|
||||||
|
import cn.rtast.libmc.protocol.packet.play.serverbound.*
|
||||||
|
import cn.rtast.libmc.protocol.protocol.state.ProtocolState
|
||||||
|
import cn.rtast.libmc.protocol.util.generateRandom16Bytes
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlin.concurrent.Volatile
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.time.Clock
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
|
public class ClientTickingLoop internal constructor(private val client: MinecraftClient) {
|
||||||
|
private var tickJob: Job? = null
|
||||||
|
private val tickIntervalMs = 50L
|
||||||
|
public var currentTick: Long = 0L
|
||||||
|
private set
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
public var isRunning: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
private val listeners = mutableListOf<suspend (Long) -> Unit>()
|
||||||
|
internal fun registerListener(action: suspend (Long) -> Unit) = listeners.add(action)
|
||||||
|
|
||||||
|
init {
|
||||||
|
client.onPacket<ClientboundLoginSuccessPacket> { handleLoginSuccess() }
|
||||||
|
client.onPacket<ClientboundSetCompressionPacket> { client.networkChannel.setCompression(it.threshold) }
|
||||||
|
client.onPacket<ClientboundHelloPacket> { handleEncryptRequest(it) }
|
||||||
|
client.onPacket<ClientboundDisconnectLoginPacket> { client.close() }
|
||||||
|
client.onPacket<ClientboundDisconnectPlayPacket> { client.close() }
|
||||||
|
client.onPacket<ClientboundDisconnectConfigurationPacket> { client.close() }
|
||||||
|
client.onPacket<ClientboundSetTimePacket> { syncServerTick(it.worldAge) }
|
||||||
|
client.onPacket<ClientboundPingPacket> { client.networkChannel.sendPacket(ServerboundPongPlayPacket(it.id)) }
|
||||||
|
client.onPacket<ClientboundSelectKnownPacksPacket> {
|
||||||
|
client.networkChannel.sendPacket(ServerboundSelectKnownPacksPacket(emptyList()))
|
||||||
|
}
|
||||||
|
client.onPacket<ClientboundCodeOfConductPacket> {
|
||||||
|
client.networkChannel.sendPacket(ServerboundAcceptCodeOfConductPacket)
|
||||||
|
}
|
||||||
|
client.onPacket<ClientboundKeepAlivePlayPacket> {
|
||||||
|
client.networkChannel.sendPacket(ServerboundKeepAlivePlayPacket(it.id))
|
||||||
|
}
|
||||||
|
client.onPacket<ClientboundPingConfigurationPacket> {
|
||||||
|
client.networkChannel.sendPacket(ServerboundPongConfigurationPacket(it.id))
|
||||||
|
}
|
||||||
|
client.onPacket<ClientboundStartConfigurationPacket> {
|
||||||
|
client.networkChannel.sendPacket(ServerboundConfigurationAcknowledgedPacket)
|
||||||
|
client.stateMachine.transitionTo(ProtocolState.CONFIGURATION)
|
||||||
|
}
|
||||||
|
client.onPacket<ClientboundKeepAliveConfigurationPacket> {
|
||||||
|
client.networkChannel.sendPacket(ServerboundKeepAliveConfigurationPacket(it.id))
|
||||||
|
}
|
||||||
|
client.onPacket<ClientboundFinishConfigurationPacket> {
|
||||||
|
client.networkChannel.sendPacket(ServerboundAckFinishConfigurationPacket)
|
||||||
|
client.stateMachine.transitionTo(ProtocolState.PLAY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal suspend fun handleLoginSuccess() {
|
||||||
|
client.networkChannel.sendPacket(ServerboundLoginAcknowledgedPacket)
|
||||||
|
client.stateMachine.transitionTo(ProtocolState.CONFIGURATION)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal suspend fun handleEncryptRequest(packet: ClientboundHelloPacket) {
|
||||||
|
val sharedSecret = generateRandom16Bytes()
|
||||||
|
if (client.isOnlineMode) {
|
||||||
|
val serverHash = minecraftServerIdHash(packet.serverId, sharedSecret, packet.publicKey)
|
||||||
|
client.protocolContext.authProvider!!.joinServer(
|
||||||
|
"https://sessionserver.mojang.com/session/minecraft/join",
|
||||||
|
client.accessToken!!, client.uuid.toString().replace("-", ""), serverHash
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val encryptedSecret = rsaEncrypt(packet.publicKey, sharedSecret)
|
||||||
|
val encryptedVerifyToken = rsaEncrypt(packet.publicKey, packet.verifyToken)
|
||||||
|
client.networkChannel.sendPacket(ServerboundKeyPacket(encryptedSecret, encryptedVerifyToken))
|
||||||
|
client.networkChannel.session.enableEncryption(sharedSecret)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun syncServerTick(serverWorldAge: Long) {
|
||||||
|
if (abs(this.currentTick - serverWorldAge) > 2) this.currentTick = serverWorldAge
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun start() {
|
||||||
|
if (isRunning) return
|
||||||
|
isRunning = true
|
||||||
|
tickJob = client.launch(CoroutineName("LibMC-ClientTickingLoop")) {
|
||||||
|
var nextTickTime = Clock.System.now().toEpochMilliseconds()
|
||||||
|
while (isActive && isRunning) {
|
||||||
|
val now = Clock.System.now().toEpochMilliseconds()
|
||||||
|
if (now >= nextTickTime) {
|
||||||
|
try {
|
||||||
|
listeners.forEach { it.invoke(currentTick) }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (e is CancellationException) throw e
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
currentTick++
|
||||||
|
nextTickTime += tickIntervalMs
|
||||||
|
if (now - nextTickTime > tickIntervalMs * 5) nextTickTime = now + tickIntervalMs
|
||||||
|
} else delay((nextTickTime - now).milliseconds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun stop() {
|
||||||
|
isRunning = false
|
||||||
|
tickJob?.cancel()
|
||||||
|
tickJob = null
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
-9
@@ -8,8 +8,6 @@ package cn.rtast.libmc.protocol.client
|
|||||||
|
|
||||||
import cn.rtast.libmc.crypto.ProtocolContext
|
import cn.rtast.libmc.crypto.ProtocolContext
|
||||||
import cn.rtast.libmc.crypto.ProtocolContextBuilder
|
import cn.rtast.libmc.crypto.ProtocolContextBuilder
|
||||||
import cn.rtast.libmc.protocol.event.InternalPacketDispatcher
|
|
||||||
import cn.rtast.libmc.protocol.event.PacketEventDispatcher
|
|
||||||
import cn.rtast.libmc.protocol.network.NetworkChannel
|
import cn.rtast.libmc.protocol.network.NetworkChannel
|
||||||
import cn.rtast.libmc.protocol.packet.handshake.ServerboundHandshakePacket
|
import cn.rtast.libmc.protocol.packet.handshake.ServerboundHandshakePacket
|
||||||
import cn.rtast.libmc.protocol.packet.login.serverbound.ServerboundLoginStartPacket
|
import cn.rtast.libmc.protocol.packet.login.serverbound.ServerboundLoginStartPacket
|
||||||
@@ -32,21 +30,26 @@ public class MinecraftClient internal constructor(
|
|||||||
internal val protocolContext: ProtocolContext,
|
internal val protocolContext: ProtocolContext,
|
||||||
) : PacketEventDispatcher(), CoroutineScope {
|
) : PacketEventDispatcher(), CoroutineScope {
|
||||||
internal val stateMachine = ClientStateMachine()
|
internal val stateMachine = ClientStateMachine()
|
||||||
public val networkChannel: NetworkChannel = NetworkChannel(
|
public val networkChannel: NetworkChannel = NetworkChannel(host, port, stateMachine, this, protocolContext)
|
||||||
host, port, stateMachine,
|
|
||||||
this, protocolContext
|
|
||||||
)
|
|
||||||
|
|
||||||
private val internalPacketDispatcher = InternalPacketDispatcher(this)
|
|
||||||
private val clientJob = SupervisorJob(parentJob)
|
private val clientJob = SupervisorJob(parentJob)
|
||||||
private var listenJob: Job? = null
|
private var listenJob: Job? = null
|
||||||
|
|
||||||
public val isOnlineMode: Boolean = accessToken != null
|
public val isOnlineMode: Boolean = accessToken != null
|
||||||
|
|
||||||
public val transactionManager: TransactionIdManager = TransactionIdManager()
|
public val transactionManager: TransactionIdManager = TransactionIdManager()
|
||||||
|
public val clientTickingLoop: ClientTickingLoop = ClientTickingLoop(this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a client ticking event callback.
|
||||||
|
* NOTE: Blocking operations will **block** the bot thread.
|
||||||
|
* Using #launch to avoid blocking.
|
||||||
|
*/
|
||||||
|
public fun onTick(action: suspend (Long) -> Unit): Unit = run { clientTickingLoop.registerListener(action) }
|
||||||
|
|
||||||
public suspend fun connect(protocolVersion: Int = CURRENT_MINECRAFT_PROTOCOL_VERSION) {
|
public suspend fun connect(protocolVersion: Int = CURRENT_MINECRAFT_PROTOCOL_VERSION) {
|
||||||
networkChannel.connect()
|
networkChannel.connect()
|
||||||
startListening()
|
startListening()
|
||||||
|
clientTickingLoop.start()
|
||||||
networkChannel.sendPacket(
|
networkChannel.sendPacket(
|
||||||
ServerboundHandshakePacket(
|
ServerboundHandshakePacket(
|
||||||
protocolVersion,
|
protocolVersion,
|
||||||
@@ -63,7 +66,7 @@ public class MinecraftClient internal constructor(
|
|||||||
private fun startListening() {
|
private fun startListening() {
|
||||||
listenJob = launch {
|
listenJob = launch {
|
||||||
try {
|
try {
|
||||||
while (isActive) internalPacketDispatcher.handleIncomingPackets(networkChannel.readNextPacket())
|
while (isActive) networkChannel.readNextPacket()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
@@ -77,6 +80,7 @@ public class MinecraftClient internal constructor(
|
|||||||
|
|
||||||
public fun close() {
|
public fun close() {
|
||||||
networkChannel.close()
|
networkChannel.close()
|
||||||
|
clientTickingLoop.stop()
|
||||||
clientJob.cancel()
|
clientJob.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@
|
|||||||
* Date: 2026/9/5
|
* Date: 2026/9/5
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package cn.rtast.libmc.protocol.event
|
package cn.rtast.libmc.protocol.client
|
||||||
|
|
||||||
import cn.rtast.libmc.packet.MinecraftPacket
|
import cn.rtast.libmc.packet.MinecraftPacket
|
||||||
import cn.rtast.libmc.protocol.protocol.PacketDirection
|
import cn.rtast.libmc.protocol.protocol.PacketDirection
|
||||||
@@ -14,7 +14,7 @@ import kotlin.reflect.KClass
|
|||||||
private typealias Handler = suspend (MinecraftPacket) -> Unit
|
private typealias Handler = suspend (MinecraftPacket) -> Unit
|
||||||
private typealias DirectionalHandler = suspend (MinecraftPacket, PacketDirection) -> Unit
|
private typealias DirectionalHandler = suspend (MinecraftPacket, PacketDirection) -> Unit
|
||||||
|
|
||||||
public open class PacketEventDispatcher {
|
public abstract class PacketEventDispatcher {
|
||||||
@Volatile
|
@Volatile
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal var receiveHandlers: Map<KClass<out MinecraftPacket>, List<Handler>> = emptyMap()
|
internal var receiveHandlers: Map<KClass<out MinecraftPacket>, List<Handler>> = emptyMap()
|
||||||
-96
@@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright © 2026 RTAkland
|
|
||||||
* Author: RTAkland
|
|
||||||
* Date: 2026/9/5
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
package cn.rtast.libmc.protocol.event
|
|
||||||
|
|
||||||
import cn.rtast.libmc.packet.MinecraftPacket
|
|
||||||
import cn.rtast.libmc.protocol.client.MinecraftClient
|
|
||||||
import cn.rtast.libmc.protocol.crypto.minecraftServerIdHash
|
|
||||||
import cn.rtast.libmc.protocol.crypto.rsaEncrypt
|
|
||||||
import cn.rtast.libmc.protocol.packet.configuration.clientbound.*
|
|
||||||
import cn.rtast.libmc.protocol.packet.configuration.serverbound.*
|
|
||||||
import cn.rtast.libmc.protocol.packet.login.clientbound.ClientboundDisconnectLoginPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.login.clientbound.ClientboundHelloPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.login.clientbound.ClientboundLoginSuccessPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.login.clientbound.ClientboundSetCompressionPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.login.serverbound.ServerboundKeyPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.login.serverbound.ServerboundLoginAcknowledgedPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundDisconnectPlayPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundKeepAlivePlayPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundPingPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.play.clientbound.ClientboundStartConfigurationPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.play.serverbound.ServerboundConfigurationAcknowledgedPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.play.serverbound.ServerboundKeepAlivePlayPacket
|
|
||||||
import cn.rtast.libmc.protocol.packet.play.serverbound.ServerboundPongPlayPacket
|
|
||||||
import cn.rtast.libmc.protocol.protocol.state.ProtocolState
|
|
||||||
import cn.rtast.libmc.protocol.util.generateRandom16Bytes
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal simple state machine trigger,
|
|
||||||
* Auto respond packets the server needed.
|
|
||||||
* Only including `Handshake`, `Login` and `Configuration` State
|
|
||||||
*/
|
|
||||||
public class InternalPacketDispatcher(private val client: MinecraftClient) {
|
|
||||||
public suspend fun handleIncomingPackets(packet: MinecraftPacket) {
|
|
||||||
when (packet) {
|
|
||||||
// login
|
|
||||||
is ClientboundDisconnectLoginPacket -> client.close()
|
|
||||||
is ClientboundSetCompressionPacket -> client.networkChannel.setCompression(packet.threshold)
|
|
||||||
is ClientboundLoginSuccessPacket -> {
|
|
||||||
client.networkChannel.sendPacket(ServerboundLoginAcknowledgedPacket)
|
|
||||||
client.stateMachine.transitionTo(ProtocolState.CONFIGURATION)
|
|
||||||
}
|
|
||||||
|
|
||||||
is ClientboundHelloPacket -> {
|
|
||||||
val sharedSecret = generateRandom16Bytes()
|
|
||||||
if (client.isOnlineMode) {
|
|
||||||
val serverHash = minecraftServerIdHash(packet.serverId, sharedSecret, packet.publicKey)
|
|
||||||
client.protocolContext.authProvider!!.joinServer(
|
|
||||||
"https://sessionserver.mojang.com/session/minecraft/join",
|
|
||||||
client.accessToken!!,
|
|
||||||
client.uuid.toString().replace("-", ""),
|
|
||||||
serverHash
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val encryptedSecret = rsaEncrypt(packet.publicKey, sharedSecret)
|
|
||||||
val encryptedVerifyToken = rsaEncrypt(packet.publicKey, packet.verifyToken)
|
|
||||||
client.networkChannel.sendPacket(ServerboundKeyPacket(encryptedSecret, encryptedVerifyToken))
|
|
||||||
client.networkChannel.session.enableEncryption(sharedSecret)
|
|
||||||
}
|
|
||||||
|
|
||||||
// configuration
|
|
||||||
ClientboundFinishConfigurationPacket -> {
|
|
||||||
client.networkChannel.sendPacket(ServerboundAckFinishConfigurationPacket)
|
|
||||||
client.stateMachine.transitionTo(ProtocolState.PLAY)
|
|
||||||
}
|
|
||||||
|
|
||||||
is ClientboundKeepAliveConfigurationPacket -> client.networkChannel.sendPacket(
|
|
||||||
ServerboundKeepAliveConfigurationPacket(packet.id)
|
|
||||||
)
|
|
||||||
|
|
||||||
is ClientboundPingConfigurationPacket -> client.networkChannel.sendPacket(
|
|
||||||
ServerboundPongConfigurationPacket(packet.id)
|
|
||||||
)
|
|
||||||
|
|
||||||
is ClientboundSelectKnownPacksPacket -> client.networkChannel.sendPacket(
|
|
||||||
ServerboundSelectKnownPacksPacket(emptyList())
|
|
||||||
) // TODO empty resource packs list
|
|
||||||
is ClientboundCodeOfConductPacket -> client.networkChannel.sendPacket(ServerboundAcceptCodeOfConductPacket)
|
|
||||||
|
|
||||||
// play
|
|
||||||
is ClientboundDisconnectPlayPacket -> client.close()
|
|
||||||
is ClientboundKeepAlivePlayPacket -> client.networkChannel.sendPacket(ServerboundKeepAlivePlayPacket(id = packet.id))
|
|
||||||
is ClientboundPingPacket -> client.networkChannel.sendPacket(ServerboundPongPlayPacket(packet.id))
|
|
||||||
ClientboundStartConfigurationPacket -> {
|
|
||||||
client.networkChannel.sendPacket(ServerboundConfigurationAcknowledgedPacket)
|
|
||||||
client.stateMachine.transitionTo(ProtocolState.CONFIGURATION)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-1
@@ -14,7 +14,7 @@ import cn.rtast.libmc.packet.writeBuffer
|
|||||||
import cn.rtast.libmc.primitives.readVarInt
|
import cn.rtast.libmc.primitives.readVarInt
|
||||||
import cn.rtast.libmc.primitives.writeVarInt
|
import cn.rtast.libmc.primitives.writeVarInt
|
||||||
import cn.rtast.libmc.protocol.client.ClientStateMachine
|
import cn.rtast.libmc.protocol.client.ClientStateMachine
|
||||||
import cn.rtast.libmc.protocol.event.PacketEventDispatcher
|
import cn.rtast.libmc.protocol.client.PacketEventDispatcher
|
||||||
import cn.rtast.libmc.protocol.protocol.GamePacketsProtocolCodec.clientboundGameProtocols
|
import cn.rtast.libmc.protocol.protocol.GamePacketsProtocolCodec.clientboundGameProtocols
|
||||||
import cn.rtast.libmc.protocol.protocol.GamePacketsProtocolCodec.serverboundGameProtocols
|
import cn.rtast.libmc.protocol.protocol.GamePacketsProtocolCodec.serverboundGameProtocols
|
||||||
import cn.rtast.libmc.zlibCompress
|
import cn.rtast.libmc.zlibCompress
|
||||||
|
|||||||
+2
-2
@@ -11,10 +11,10 @@ import cn.rtast.libmc.network.BytesBuffer
|
|||||||
import cn.rtast.libmc.packet.PacketCodec
|
import cn.rtast.libmc.packet.PacketCodec
|
||||||
import cn.rtast.libmc.packet.MinecraftPacket
|
import cn.rtast.libmc.packet.MinecraftPacket
|
||||||
import cn.rtast.libmc.primitives.readVarInt
|
import cn.rtast.libmc.primitives.readVarInt
|
||||||
import cn.rtast.libmc.protocol.protocol.game.math.LpVec3
|
import cn.rtast.libmc.protocol.protocol.game.math.LpVec3d
|
||||||
import cn.rtast.libmc.protocol.protocol.game.math.readLpVec3
|
import cn.rtast.libmc.protocol.protocol.game.math.readLpVec3
|
||||||
|
|
||||||
public data class ClientboundSetEntityVelocityPacket(val entityId: Int, val velocity: LpVec3) : MinecraftPacket {
|
public data class ClientboundSetEntityVelocityPacket(val entityId: Int, val velocity: LpVec3d) : MinecraftPacket {
|
||||||
internal companion object Codec : PacketCodec<ClientboundSetEntityVelocityPacket> {
|
internal companion object Codec : PacketCodec<ClientboundSetEntityVelocityPacket> {
|
||||||
override fun encode(buffer: BytesBuffer, value: ClientboundSetEntityVelocityPacket) {}
|
override fun encode(buffer: BytesBuffer, value: ClientboundSetEntityVelocityPacket) {}
|
||||||
override fun decode(buffer: BytesBuffer): ClientboundSetEntityVelocityPacket {
|
override fun decode(buffer: BytesBuffer): ClientboundSetEntityVelocityPacket {
|
||||||
|
|||||||
+2
-2
@@ -13,7 +13,7 @@ import cn.rtast.libmc.packet.PacketCodec
|
|||||||
import cn.rtast.libmc.primitives.readUuid
|
import cn.rtast.libmc.primitives.readUuid
|
||||||
import cn.rtast.libmc.primitives.readVarInt
|
import cn.rtast.libmc.primitives.readVarInt
|
||||||
import cn.rtast.libmc.protocol.protocol.game.math.Angle
|
import cn.rtast.libmc.protocol.protocol.game.math.Angle
|
||||||
import cn.rtast.libmc.protocol.protocol.game.math.LpVec3
|
import cn.rtast.libmc.protocol.protocol.game.math.LpVec3d
|
||||||
import cn.rtast.libmc.protocol.protocol.game.math.readAngle
|
import cn.rtast.libmc.protocol.protocol.game.math.readAngle
|
||||||
import cn.rtast.libmc.protocol.protocol.game.math.readLpVec3
|
import cn.rtast.libmc.protocol.protocol.game.math.readLpVec3
|
||||||
import kotlin.uuid.Uuid
|
import kotlin.uuid.Uuid
|
||||||
@@ -28,7 +28,7 @@ public data class ClientboundSpawnEntityPacket(
|
|||||||
val x: Double,
|
val x: Double,
|
||||||
val y: Double,
|
val y: Double,
|
||||||
val z: Double,
|
val z: Double,
|
||||||
val velocity: LpVec3,
|
val velocity: LpVec3d,
|
||||||
val pitch: Angle,
|
val pitch: Angle,
|
||||||
val yaw: Angle,
|
val yaw: Angle,
|
||||||
val headYaw: Angle,
|
val headYaw: Angle,
|
||||||
|
|||||||
+2
-2
@@ -11,14 +11,14 @@ import cn.rtast.libmc.network.BytesBuffer
|
|||||||
import cn.rtast.libmc.packet.PacketCodec
|
import cn.rtast.libmc.packet.PacketCodec
|
||||||
import cn.rtast.libmc.packet.MinecraftPacket
|
import cn.rtast.libmc.packet.MinecraftPacket
|
||||||
import cn.rtast.libmc.primitives.writeVarInt
|
import cn.rtast.libmc.primitives.writeVarInt
|
||||||
import cn.rtast.libmc.protocol.protocol.game.math.LpVec3
|
import cn.rtast.libmc.protocol.protocol.game.math.LpVec3d
|
||||||
import cn.rtast.libmc.protocol.protocol.game.math.writeLpVec3
|
import cn.rtast.libmc.protocol.protocol.game.math.writeLpVec3
|
||||||
import cn.rtast.libmc.protocol.protocol.game.player.Hand
|
import cn.rtast.libmc.protocol.protocol.game.player.Hand
|
||||||
|
|
||||||
public data class ServerboundInteractPacket(
|
public data class ServerboundInteractPacket(
|
||||||
val entityId: Int,
|
val entityId: Int,
|
||||||
val hand: Hand,
|
val hand: Hand,
|
||||||
val targetOffset: LpVec3,
|
val targetOffset: LpVec3d,
|
||||||
val isSneaking: Boolean,
|
val isSneaking: Boolean,
|
||||||
) : MinecraftPacket {
|
) : MinecraftPacket {
|
||||||
internal companion object Codec : PacketCodec<ServerboundInteractPacket> {
|
internal companion object Codec : PacketCodec<ServerboundInteractPacket> {
|
||||||
|
|||||||
+7
-4
@@ -8,22 +8,25 @@
|
|||||||
package cn.rtast.libmc.protocol.packet.play.serverbound
|
package cn.rtast.libmc.protocol.packet.play.serverbound
|
||||||
|
|
||||||
import cn.rtast.libmc.network.BytesBuffer
|
import cn.rtast.libmc.network.BytesBuffer
|
||||||
import cn.rtast.libmc.packet.PacketCodec
|
|
||||||
import cn.rtast.libmc.packet.MinecraftPacket
|
import cn.rtast.libmc.packet.MinecraftPacket
|
||||||
import cn.rtast.libmc.protocol.protocol.game.player.PlayerPositionFlag
|
import cn.rtast.libmc.packet.PacketCodec
|
||||||
|
|
||||||
public data class ServerboundSetPlayerPositionPacket(
|
public data class ServerboundSetPlayerPositionPacket(
|
||||||
val x: Double,
|
val x: Double,
|
||||||
val feetY: Double,
|
val feetY: Double,
|
||||||
val z: Double,
|
val z: Double,
|
||||||
val flags: PlayerPositionFlag,
|
val onGround: Boolean,
|
||||||
|
val pushingAgainstWall: Boolean = false,
|
||||||
) : MinecraftPacket {
|
) : MinecraftPacket {
|
||||||
internal companion object Codec : PacketCodec<ServerboundSetPlayerPositionPacket> {
|
internal companion object Codec : PacketCodec<ServerboundSetPlayerPositionPacket> {
|
||||||
override fun encode(buffer: BytesBuffer, value: ServerboundSetPlayerPositionPacket) {
|
override fun encode(buffer: BytesBuffer, value: ServerboundSetPlayerPositionPacket) {
|
||||||
buffer.writeDouble(value.x)
|
buffer.writeDouble(value.x)
|
||||||
buffer.writeDouble(value.feetY)
|
buffer.writeDouble(value.feetY)
|
||||||
buffer.writeDouble(value.z)
|
buffer.writeDouble(value.z)
|
||||||
buffer.writeByte(value.flags.flag)
|
var flags = 0
|
||||||
|
if (value.onGround) flags = flags or 0x01
|
||||||
|
if (value.pushingAgainstWall) flags = flags or 0x02
|
||||||
|
buffer.writeByte(flags.toByte())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun decode(buffer: BytesBuffer): ServerboundSetPlayerPositionPacket =
|
override fun decode(buffer: BytesBuffer): ServerboundSetPlayerPositionPacket =
|
||||||
|
|||||||
+12
-17
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package cn.rtast.libmc.protocol.protocol.game.math
|
package cn.rtast.libmc.protocol.protocol.game.math
|
||||||
|
|
||||||
import cn.rtast.libmc.network.ByteOrder
|
|
||||||
import cn.rtast.libmc.network.BytesBuffer
|
import cn.rtast.libmc.network.BytesBuffer
|
||||||
import cn.rtast.libmc.primitives.readVarInt
|
import cn.rtast.libmc.primitives.readVarInt
|
||||||
import cn.rtast.libmc.primitives.writeVarInt
|
import cn.rtast.libmc.primitives.writeVarInt
|
||||||
@@ -17,14 +16,10 @@ import kotlin.math.max
|
|||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
// ref: https://minecraft.wiki/w/Java_Edition_protocol/Data_types#LpVec3
|
// ref: https://minecraft.wiki/w/Java_Edition_protocol/Data_types#LpVec3
|
||||||
public data class LpVec3(
|
public data class LpVec3d(var x: Double, var y: Double, var z: Double) {
|
||||||
val x: Double,
|
|
||||||
val y: Double,
|
|
||||||
val z: Double,
|
|
||||||
) {
|
|
||||||
@Suppress("UNUSED")
|
@Suppress("UNUSED")
|
||||||
public companion object {
|
public companion object {
|
||||||
public val ZERO: LpVec3 = LpVec3(0.0, 0.0, 0.0)
|
public val ZERO: LpVec3d = LpVec3d(0.0, 0.0, 0.0)
|
||||||
|
|
||||||
private const val MAX_QUANTIZED_VALUE = 32766.0
|
private const val MAX_QUANTIZED_VALUE = 32766.0
|
||||||
private const val CONTINUATION_FLAG = 0x04L
|
private const val CONTINUATION_FLAG = 0x04L
|
||||||
@@ -39,23 +34,23 @@ public data class LpVec3(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ref: https://minecraft.wiki/w/Java_Edition_protocol/Data_types#LpVec3
|
// ref: https://minecraft.wiki/w/Java_Edition_protocol/Data_types#LpVec3
|
||||||
internal fun BytesBuffer.readLpVec3(): LpVec3 {
|
internal fun BytesBuffer.readLpVec3(): LpVec3d {
|
||||||
val byte1 = readByte().toInt() and 0xFF
|
val byte1 = readByte().toInt() and 0xFF
|
||||||
if (byte1 == 0) return LpVec3.ZERO
|
if (byte1 == 0) return LpVec3d.ZERO
|
||||||
val byte2 = readByte().toInt() and 0xFF
|
val byte2 = readByte().toInt() and 0xFF
|
||||||
val bytes3To6 = readInt().toLong() and 0xFFFFFFFFL
|
val bytes3To6 = readInt().toLong() and 0xFFFFFFFFL
|
||||||
val packed = (bytes3To6 shl 16) or (byte2.toLong() shl 8) or byte1.toLong()
|
val packed = (bytes3To6 shl 16) or (byte2.toLong() shl 8) or byte1.toLong()
|
||||||
var scaleFactor = byte1.toLong() and 0x03L
|
var scaleFactor = byte1.toLong() and 0x03L
|
||||||
if ((byte1.toLong() and 0x04L) != 0L) scaleFactor = scaleFactor or (readVarInt().toLong() shl 2)
|
if ((byte1.toLong() and 0x04L) != 0L) scaleFactor = scaleFactor or (readVarInt().toLong() shl 2)
|
||||||
val scale = scaleFactor.toDouble()
|
val scale = scaleFactor.toDouble()
|
||||||
val x = LpVec3.unpack(packed shr 3) * scale
|
val x = LpVec3d.unpack(packed shr 3) * scale
|
||||||
val y = LpVec3.unpack(packed shr 18) * scale
|
val y = LpVec3d.unpack(packed shr 18) * scale
|
||||||
val z = LpVec3.unpack(packed shr 33) * scale
|
val z = LpVec3d.unpack(packed shr 33) * scale
|
||||||
return LpVec3(x, y, z)
|
return LpVec3d(x, y, z)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ref: https://minecraft.wiki/w/Java_Edition_protocol/Data_types#LpVec3
|
// ref: https://minecraft.wiki/w/Java_Edition_protocol/Data_types#LpVec3
|
||||||
internal fun BytesBuffer.writeLpVec3(vec3: LpVec3) {
|
internal fun BytesBuffer.writeLpVec3(vec3: LpVec3d) {
|
||||||
val maxCoordinate = max(abs(vec3.x), max(abs(vec3.y), abs(vec3.z)))
|
val maxCoordinate = max(abs(vec3.x), max(abs(vec3.y), abs(vec3.z)))
|
||||||
if (maxCoordinate.isNaN() || maxCoordinate < 1.0 / 32766.0) {
|
if (maxCoordinate.isNaN() || maxCoordinate < 1.0 / 32766.0) {
|
||||||
writeByte(0x00)
|
writeByte(0x00)
|
||||||
@@ -64,9 +59,9 @@ internal fun BytesBuffer.writeLpVec3(vec3: LpVec3) {
|
|||||||
val scaleFactor = ceil(maxCoordinate).toLong()
|
val scaleFactor = ceil(maxCoordinate).toLong()
|
||||||
val needContinuation = (scaleFactor and 0x03L) != scaleFactor
|
val needContinuation = (scaleFactor and 0x03L) != scaleFactor
|
||||||
val packedScale = if (needContinuation) ((scaleFactor and 0x03L) or 0x04L) else scaleFactor
|
val packedScale = if (needContinuation) ((scaleFactor and 0x03L) or 0x04L) else scaleFactor
|
||||||
val packedX = LpVec3.pack((vec3.x / scaleFactor.toDouble()).toLong()) shl 3
|
val packedX = LpVec3d.pack((vec3.x / scaleFactor.toDouble()).toLong()) shl 3
|
||||||
val packedY = LpVec3.pack((vec3.y / scaleFactor.toDouble()).toLong()) shl 18
|
val packedY = LpVec3d.pack((vec3.y / scaleFactor.toDouble()).toLong()) shl 18
|
||||||
val packedZ = LpVec3.pack((vec3.z / scaleFactor.toDouble()).toLong()) shl 33
|
val packedZ = LpVec3d.pack((vec3.z / scaleFactor.toDouble()).toLong()) shl 33
|
||||||
val packed = packedZ or packedY or packedX or packedScale
|
val packed = packedZ or packedY or packedX or packedScale
|
||||||
writeByte(packed.toByte())
|
writeByte(packed.toByte())
|
||||||
writeByte((packed shr 8).toByte())
|
writeByte((packed shr 8).toByte())
|
||||||
+1
-1
@@ -9,7 +9,7 @@ package cn.rtast.libmc.protocol.protocol.game.math
|
|||||||
|
|
||||||
import cn.rtast.libmc.network.BytesBuffer
|
import cn.rtast.libmc.network.BytesBuffer
|
||||||
|
|
||||||
public data class Vec3d(val x: Double, val y: Double, val z: Double) {
|
public data class Vec3d(var x: Double, var y: Double, var z: Double) {
|
||||||
public companion object {
|
public companion object {
|
||||||
public val ZERO: Vec3d = Vec3d(0.0, 0.0, 0.0)
|
public val ZERO: Vec3d = Vec3d(0.0, 0.0, 0.0)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -7,10 +7,10 @@
|
|||||||
|
|
||||||
package cn.rtast.libmc.protocol.protocol.game.player
|
package cn.rtast.libmc.protocol.protocol.game.player
|
||||||
|
|
||||||
import cn.rtast.libmc.protocol.protocol.game.math.LpVec3
|
import cn.rtast.libmc.protocol.protocol.game.math.LpVec3d
|
||||||
|
|
||||||
public sealed class InteractType(public val id: Int) {
|
public sealed class InteractType(public val id: Int) {
|
||||||
public data class Interact(val hand: Hand) : InteractType(0)
|
public data class Interact(val hand: Hand) : InteractType(0)
|
||||||
public data object Attack : InteractType(1)
|
public data object Attack : InteractType(1)
|
||||||
public data class InteractAt(val targetOffset: LpVec3, val hand: Hand) : InteractType(2)
|
public data class InteractAt(val targetOffset: LpVec3d, val hand: Hand) : InteractType(2)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user