SoulFire LogoSoulFire
SDK

Raw protocol access

View and send Minecraft packets through the SoulFire SDK.

The protocol module exposes SoulFire's active Minecraft packet codec. Use it for missing high-level operations, custom packet extensions, or protocol debugging.

Use the typed world, inventory, chat, action, and task APIs for normal automation. Raw packet code depends on the Minecraft version and can break a bot connection.

Check the active protocols

info reports both protocol versions involved in a connection:

  • minecraftProtocolVersion is SoulFire's native client protocol. Packet schemas and encoded bytes in this API always use this version.
  • remoteProtocolVersion is the Minecraft server protocol negotiated by ViaVersion.
  • protocolState identifies the active native state, such as play or configuration.
  • The limits describe the maximum encoded packet size and send rate.
const info = yield* bot.protocol.info();

yield* Effect.logInfo("Active protocol", {
  native: info.minecraftVersionName,
  remote: info.remoteVersionName,
  state: info.protocolState,
});

Discover packet IDs

List the packet names and numeric IDs for the bot's current native protocol state:

import { PacketDirection } from "@soulfiremc/sdk";

const packets = yield* bot.protocol.schemas(
  PacketDirection.SERVERBOUND,
);

for (const packet of packets) {
  yield* Effect.logInfo(`${packet.networkId}: ${packet.name}`);
}

Packet discovery reports the active packet registry, not a stable cross-version contract. Re-read it after a protocol state transition or a bot reconnect.

Observe packets

Packet observation is available to callers with READ_BOT_INFO. Filter at the server by direction and canonical packet name so high-volume connections do not send irrelevant events to the SDK.

import { PacketDirection } from "@soulfiremc/sdk";

yield* bot.protocol.packets({
  directions: [PacketDirection.CLIENTBOUND],
  names: ["minecraft:game_event"],
  includeEncodedPacket: true,
  maximumEncodedBytes: 4096,
}).pipe(
  Stream.runForEach((packet) =>
    Effect.logInfo("Packet received", {
      name: packet.name,
      bytes: packet.encodedPacket.byteLength,
      droppedBefore: packet.droppedBefore,
    })
  ),
);

The Promise facade returns an AsyncIterable, and the Python clients return an async iterator or iterator:

from soulfire import PACKET_DIRECTION_CLIENTBOUND

async for packet in bot.protocol.packets(
    directions=[PACKET_DIRECTION_CLIENTBOUND],
    names=["minecraft:game_event"],
    include_encoded_packet=True,
    maximum_encoded_bytes=4096,
):
    print(packet.name, len(packet.encoded_packet), packet.dropped_before)

The stream respects transport backpressure. If the consumer cannot keep up, SoulFire drops observations instead of blocking the bot's network thread. The next delivered event reports the number in droppedBefore or dropped_before.

Send a native packet

send accepts one complete unframed native packet. The byte array must contain the native protocol packet ID followed by its payload. SoulFire decodes the bytes through the active serverbound codec, rejects trailing data, and then sends the decoded packet through the normal connection pipeline. ViaVersion translates it to the remote server version afterward.

const result = yield* bot.protocol.send(encodedPacket, {
  expectedName: "minecraft:custom_payload",
});

yield* Effect.logInfo("Packet sent", result);
result = await bot.protocol.send(
    encoded_packet,
    expected_name="minecraft:custom_payload",
)
print(result.name, result.encoded_bytes)

Set expectedName or expected_name whenever the packet type is known. This guard prevents bytes built for one packet schema from being sent as another packet after a version or state change.

Raw packet sending requires the RAW_PROTOCOL instance permission. By default, SoulFire grants this permission only to administrators. It limits packets to 1 MiB and 20 sends per second for each user and bot. It writes each send to the server log.

Raw observation and injection do not bypass plugin RPCs, task ownership, or the normal typed SDK. A SoulFire plugin can expose a typed RPC when an operation is reusable. Reserve raw packets for genuinely version-specific work and short-lived diagnostics.

How is this page?

Last updated on

On this page