Pull Bow (PullBowPacket)

Composite packet · Minecraft Java 1.17+ · 中文条目

Pull Bow is a composite packet in Minecraft Java Edition 1.17+. It is not a registered packet type and has no packet ID of its own: it is a run of vanilla packets — S32/C0F sequence round trip, C08, half a C07, and the movement family — sent inside one tick and processed in arrival order. When it takes effect, the arrow that was not fired leaves the bow, and a short detection exemption follows.

What it is

Searching the official mappings for PullBow returns nothing, and that is expected. What exists there are the packets that make it up: ClientboundPingPacket, ServerboundPongPacket, ServerboundUseItemPacket, ServerboundPlayerActionPacket, ServerboundMovePlayerPacket and ClientboundPlayerPositionPacket. The server does not merge them; it decodes each frame and runs them one by one in the main-thread queue. The effect comes from that order.

The community names ClientboundPullBowPacket and ServerboundPullBowPacket follow Mojang's naming style, and PullBowS2CPacket / PullBowC2SPacket follow the older direction convention. None of the four is a real class.

Composition

#PacketClassDirectionContributes
1S32ClientboundPingPacketS2CSequence
2C0FServerboundPongPacketC2SSequence echoed back, asynchronously
3C08ServerboundUseItemPacketC2SResets the server-side use timer
4C07ServerboundPlayerActionPacketC2SRELEASE_USE_ITEM, action field only
5C04 / C03ServerboundMovePlayerPacketC2SX, Y, Z, Yaw, Pitch, On Ground
6S08ClientboundPlayerPositionPacketS2CPosition baseline, applied late
7local stateLivingEntity.getTicksUsingItem()—Charge Ticks

Trigger

Two conditions are mandatory: the bow must be in the main hand, and the client's own getTicksUsingItem() must be at least 20 — one full second, the point where BowItem.getPowerForTime(20) returns 1.0. Two further conditions belong to the handshake: the server's first packet must arrive first, and the reply has to land within one tick (50 ms) after it. Alignment inside that tick is probabilistic; how much slack the client has left in that tick decides whether the run goes out together.

Sequence

  1. Charge the bow to 20 ticks.
  2. Receive S32 and echo its id in C0F — asynchronously, outside the main-thread queue.
  3. Resend C08, which zeroes the server-side timer.
  4. Send half a C07 immediately after: action field valid, position and facing left as placeholders.
  5. Optionally send C06 to sync position.
  6. Receive S08 but delay applying it by 15–20 ticks; the delay is on applying, not on receiving.
  7. The server's first packet opens the one-tick reply window.
  8. Reply inside the window with half or full form, Sequence matching.

Steps 1 through 5 must share one tick. Swapping the order does not work: C08 has to be processed before the release, otherwise the timer never resets and the arrow leaves immediately.

Effect

When the run takes effect, the arrow that was held back actually fires. During that tick the server's charge timer had just been reset, so the half C07 computed a power of 0 and spawned no arrow; after the reply inside the window, the arrow leaves in the same round, with the impact point computed at full charge.

A short detection exemption follows: the server's item-use state was reset inside that same tick, so the release and the movement packets are settled against the new state, and the window does not accumulate as two consecutive full-charge draws.

Note: GrimAC appears to already detect this packet run. Treat that as the constraint when testing.

Versions and packet IDs

Play-state IDs are renumbered every version, so an ID quoted from an old table will not resolve. In 1.21.4 (protocol 769) the relevant serverbound IDs are 0x1C position, 0x1D position-look, 0x1F status-only, 0x27 player action, 0x2B pong and 0x3D use item; on the clientbound side 0x37 is ping and 0x42 is player position.

The S32/C0F pair only carries Ping/Pong semantics from 1.17 onward; in 1.16 and earlier the same IDs were transaction acknowledgements. That is why the discussion is scoped to 1.17+.

Capturing it

Run the server with online-mode=false, capture on the loopback adapter, filter tcp.port == 25565 && tcp.len > 0, and reassemble the byte stream rather than counting TCP segments: read a VarInt length, take that many bytes, read the packet ID as the next VarInt. Frames from the same tick are typically less than a millisecond apart; frames from different ticks are at least tens of milliseconds apart.

FAQ

Is Pull Bow a real packet?
No. It is a name for a run of vanilla packets sent inside one tick. There is no class and no assigned ID.
Is it clientbound or serverbound?
Both directions carry content: the sequence round trip and position correction on the S2C side, C08, half a C07 and the movement family on the C2S side. The C08 payload account treats only the C2S side as Pull Bow itself.
Does it work on every server?
No. Servers differ in how they queue consecutive packets inside one tick, so the same send rhythm succeeds on some and fails on others. GrimAC also appears to detect the run.