4.0 KiB
Creating a Client
public fun main() = runBlocking {
val client = createMinecraftClient(
"127.0.0.1", 25566, "MyBot",
generateOfflineUuid("MyBot"),
accessToken = null,
context = DefaultProtocolContext.withCustom {
socketEngine = KtorNetworkEngine()
}
)
client.launch { client.connect() }
while (true) {
delay(5.seconds)
}
}
In the example code above, a
MinecraftClientis created. This client will connect to an offline server at127.0.0.1:25565usingMyBotas the player name, and replaces the underlying TCP Socket implementation with aktor-networkbased TCP Socket. (For details on how to create a SocketEngine, please refer to Implementing TCP Socket. For details on how to create a Context, please refer to Required APIs) MinecraftClient implements CoroutineScope, and callingclient.connect()will execute the connection on a background thread. Blocking thread to prevent the application from exiting
Connecting to an Online-Mode Server
private val accessToken = "eyJraWQiOiIw..."
public fun main() = runBlocking {
val client = createMinecraftClient(
// Other parameters
username = "RTAkland",
uuid = Uuid.parse("bb033844-e68e-4909-a636-1a5d1821ddc4"),
accessToken = accessToken,
// Other parameters
)
}
In the example code above, the client will connect to the server using
RTAklandas the player name.
Get an AccessToken
Open minecraft.net, log in, press F12, and run the following code in the Console:
console.log(`; ${document.cookie}`.split('; bearer_token=').pop().split(';').shift())
Note: AccessTokens are valid for 24 hours
Listening for Packets
// Listen for specific received packets (must start with Clientbound)
client.onPacket<ClientboundLoginSuccessPacket> {
println(it)
}
// Listen for all received packets
client.on { packet, direction ->
println("$direction ->$packet")
}
// Listen for outgoing packets (must start with Serverbound)
cli.onSent<ServerboundPongConfigurationPacket> {
println(it)
}
Sending Packets
// Only packets starting with Serverbound can be sent, otherwise an UnsupportedOperationException will be thrown
client.networkChannel.sendPacket(
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".
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))
}