<!-- https://minecraftdocs.dev/systems/player/the-sword-swing — this page as markdown. The book's index: https://minecraftdocs.dev/llms.txt; the whole book in one file: https://minecraftdocs.dev/llms-full.txt -->

# The sword swing

> Verified against **Minecraft 26.3** · Part VIII · Left-click on a pig: the client picks a target and sends one integer, and the server rebuilds every part of the hit from scratch.

You put the crosshair on a pig and click. The client has already decided
what you are looking at — earlier in this same tick — checks a handful of
reasons not to swing, and sends a packet with almost nothing in it:
**`ServerboundAttackPacket` is a record of one int, the entity id.** No
hand, no sneak flag, no hit position, no damage. Everything else the server
re-derives: the weapon from your main hand, the geometry from the target's
bounding box measured against your eye, and the damage from an attribute, a cooldown curve applied twice in
two different shapes, and a multiplication order in which the mace's fall
bonus lands *before* the critical hit and is therefore multiplied by it.

## The cast

| class | what it decides | thread |
|---|---|---|
| `Minecraft` | what you are looking at, and whether the click swings at all | Render |
| `LocalPlayer` | the raycast, and which range its winner is judged against | Render |
| `MultiPlayerGameMode` | sends the attack, and predicts almost nothing | Render |
| `ServerGamePacketListenerImpl` | resolves the id, re-checks the range and the item | Server |
| `Player` | `Player.attack`: one method, and the order inside it is load-bearing | both (only the server's answer counts) |
| `LivingEntity` | the swing animation state, and the two attack clocks | both |
| `AttackRange` | a weapon's own reach, with a minimum as well as a maximum | — |

## Picking: what is under the crosshair

`Minecraft.pick` runs before `Minecraft.handleKeybinds` in the client tick
([the client
loop](../client/the-client-loop#what-a-tick-is-in-order)), and the keybind
drain is what turns `Options.keyAttack` into `Minecraft.startAttack` — so
**the hit result a click uses was computed earlier in the same tick**.
`Minecraft.pick` also runs per frame, for the crosshair and the block
outline, but that value is not what the attack sees.

It asks the local player, through `LocalPlayer.raycastHitResult`, with the
camera entity as the ray's origin. A player carries **two** reaches, and the
whole of this section turns on their being different numbers:
`Attributes.BLOCK_INTERACTION_RANGE` (4.5) and
`Attributes.ENTITY_INTERACTION_RANGE` (3.0) ([player
anatomy](player-anatomy#what-player-owns)).

If the **active** item (the one being used, if any, else the main hand)
carries an `AttackRange` (`DataComponents.ATTACK_RANGE`), that component's
own search runs first, and the classic algorithm runs only when that search
comes back empty: a block clip out to the greater of the two ranges, an
entity sweep with
`ProjectileUtil.getEntityHitResult` over the bounding box expanded along the
view direction and inflated by one, each candidate inflated by
`Entity.getPickRadius` (**zero** for everything but a pickable projectile) — and the
entity wins only if it is strictly nearer than the block. Then
`LocalPlayer.filterHitResult` measures the winner against *its own* one of
the two reaches, an entity against 3.0 and a block against 4.5. That is the
divergence outside creative: a wall four blocks away is in reach and a mob
four blocks away is not — and a mob at four blocks in front of a wall hides the wall, because the
mob wins the ray and is then turned into a miss.

`AttackRange` is worth a second look, because it is a reach *floor* as well
as a ceiling: a minimum and a maximum, separate creative values, a hitbox
margin and a mob factor. `AttackRange.isInRange` is the test,
`AttackRange.defaultFor` falls back to `Attributes.ENTITY_INTERACTION_RANGE`,
and `AttackRange.effectiveMinRange` / `AttackRange.effectiveMaxRange` apply
the mob factor — only for non-players.

## Deciding: most branches do not swing

`Minecraft.startAttack` is the branch point. It returns early — no swing, no
packet — while `Minecraft.missTime` is running, when there is no hit result
at all (setting a ten-tick miss time), while `LocalPlayer.isHandsBusy`, for
a disabled item, and when `Player.cannotAttackWithItem` refuses with a
tolerance of **zero**; spectators take a branch of their own. Two branches
do swing: the piercing short-circuit to `MultiPlayerGameMode.piercingAttack`
when the item carries `DataComponents.PIERCING_WEAPON` — [the
spear](the-spear) — and the tail of the hit-result switch: entity to
`MultiPlayerGameMode.attack`, block to
`MultiPlayerGameMode.startDestroyBlock` ([block
breaking](../blocks/block-breaking#one-dig-end-to-end)), a miss (nothing in reach, or an air block) to
`Player.resetAttackStrengthTicker` and the ten-tick miss time. Even the
entity branch is conditional — a weapon with its own `AttackRange` that the
hit falls outside of swings but sends no attack packet at all. The ten-tick
miss time only exists outside creative, and opening any screen parks the timer
at a very large number in every mode.

On the server, `ServerGamePacketListenerImpl.handleAttack` requires the
client to have loaded and the player not to be a spectator, resolves the id
with `ServerLevel.getEntityOrPart`, checks the world border, applies
`Player.isWithinAttackRange` with a **3.0-block server buffer** — applied to
*both* ends, so a weapon's minimum range effectively vanishes server-side —
rejects a piercing weapon (that path arrives elsewhere), checks the item is
enabled, re-checks `Player.cannotAttackWithItem` with a tolerance of **five
ticks**, more lenient than the client's zero, and calls `Player.attack`.
Attacking something absurd — an `ItemEntity`, an `ExperienceOrb`, an
unattackable `AbstractArrow`, yourself — is a **disconnect**, not a
rejection; failing the range check is a silent drop.

The packet is drained from `PacketProcessor` at the **top** of the tick,
before `MinecraftServer.tickServer` and therefore before any level ticks.
That ordering matters: `Player.attack` runs before the victim's
damage cooldown comes down for the tick ([damage and
death](../entities/damage-and-death#ten-ticks-in-which-nothing-shows-and-ten-that-protect-nothing)
owns `LivingEntity.damageCooldownTime` and who decrements it when). Each of the
resulting feedback packets is written and flushed on its own: the
connection suspends flushing across `MinecraftServer.tickChildren` (and
across a player's join), and the attack was handled before that bracket
opened.

## One click, one integer, one round trip

Two packets go out, and the one that comes back for the hit, the damage
event, has no damage number in it.

```mermaid
sequenceDiagram
    box transparent the client
    participant LP as LocalPlayer
    participant MPGM as MultiPlayer<br/>GameMode
    participant CPL as ClientPacketListener
    end
    box transparent the server
    participant SGPL as ServerGamePacket<br/>ListenerImpl
    participant Player as Player
    participant Entity as Entity
    end

    LP->>LP: raycastHitResult — AttackRange first, then the classic pick
    LP->>MPGM: attack, from Minecraft.startAttack
    MPGM->>SGPL: ServerboundAttackPacket — one varint, the entity id
    LP->>SGPL: ServerboundPunchPacket — no fields, from Minecraft.startAttack
    SGPL->>SGPL: ServerLevel.<br/>getEntityOrPart
    SGPL->>Player: isWithinAttackRange — the reach, plus a 3.0 buffer
    SGPL->>Player: attack — the damage rebuilt from nothing but the id
    Player->>Entity: hurtOrSimulate — its answer gates the tail
    Entity->>CPL: ClientboundDamageEventPacket — a damage type and three ids
    Player->>Player: causeExtraKnockback, doSweepAttack, itemAttackInteraction
```

*Everything the client sent is on the two arrows that cross to the right, and
between them they carry one integer: every number in the box on the right was
rebuilt there, and the arrow coming back carries none of them.*

## The two clocks a swing is charged against

Before the arithmetic, the thing the arithmetic reads. A player carries two
attack clocks, and both are declared two rungs up, on `LivingEntity`, while
being read, reset and incremented **only** from `Player`:
`LivingEntity.attackStrengthTicker` and `LivingEntity.itemSwapTicker`.

The first is the one you watch. `Player.getCurrentItemAttackStrengthDelay` is
twenty divided by `Attributes.ATTACK_SPEED`, and the scale the damage uses is
how far through that delay the ticker has got.
`Player.resetAttackStrengthTicker` clears both clocks;
`Player.resetOnlyAttackStrengthTicker` clears one. `Player.tick` resets both
when the main-hand *item type* changes — swap weapons and you start again.

The second exists only to drive the held-item swap animation.
`Player.onAttack` clears the attack ticker and leaves the swap ticker alone,
but it is not the only reset a swing gets: both sides reset the attack ticker
**twice** per swing, once inside `Player.attack` and once after it with
`Player.resetAttackStrengthTicker`, which clears both clocks — the client in
`MultiPlayerGameMode.attack`, the server on the punch packet that follows,
because `ServerGamePacketListenerImpl.handlePunch` resets the ticker too.

## The damage: one number, two curves, one order

`Player.attack` is a single method, and everything interesting about melee
combat is the order in which it touches one float.

```mermaid
flowchart TD
    S["s = Player.getAttackStrengthScale, at a partial tick of 0.5"]
    BASE["base = Attributes.ATTACK_DAMAGE"]
    BOOST["boost = Player.getEnchantedDamage minus base, times s"]
    BQ["base × (0.2 + s² × 0.8) — Player.baseDamageScaleFactor"]
    RESET["Player.onAttack — the ticker resets, the scale already read"]
    GATE{"base or boost<br/>above zero?"}
    NONE["nothing below runs"]
    KB["sprint knockback, if s is above 0.9 and you are sprinting"]
    ITEM["base += Item.getAttackDamageBonus — the mace's fall bonus"]
    CRIT["base × 1.5, if full strength and Player.canCriticalAttack"]
    TOTAL["total = base + boost"]
    HURT["Entity.hurtOrSimulate — was anything damaged?"]
    S --> BOOST
    S --> BQ
    BASE --> BOOST
    BASE --> BQ
    BQ --> RESET
    RESET --> GATE
    GATE -- "neither" --> NONE
    GATE -- "either" --> KB
    KB --> ITEM
    ITEM --> CRIT
    CRIT --> TOTAL
    BOOST --> TOTAL
    TOTAL --> HURT
```

*One float, followed down: the short branch is the enchantment bonus, which
is scaled once and then left alone, and the long one is the base damage,
which is scaled, gated, added to and multiplied before the two meet again at
the bottom.*

One node in it needs its meaning before the rest of the page spends it.
`Entity.hurtOrSimulate` is the wrapper that branches on the side —
`Entity.hurtServer` on the server, `Entity.hurtClient` on the client ([damage
and death](../entities/damage-and-death#everything-that-calls-it) owns the
wrapper itself) — and what it *returns* is not the obvious thing: the boolean
means **was anything damaged**, not *did the hit land*. That is what gates
the knockback, the sweep, the durability and the hit particles below.

Read the picture for the two things it makes obvious. **The cooldown is
applied twice, differently** — `Player.baseDamageScaleFactor` is the
quadratic ramp on the base damage, and a plain multiplication is the linear
one on the enchantment bonus, both from the same scale read with the same 0.5
partial tick. And **the item bonus is inside the crit**, because
`Item.getAttackDamageBonus` is added before the ×1.5 rather than after it.
One substitution the figure leaves out: while an auto-spin attack is in
flight the base is `LivingEntity.autoSpinAttackDmg`, the riptide number, instead of
the attribute.

The gates along the way are as particular as the arithmetic, and the first
two are put to the *target* rather than to the attacker.
**`Player.cannotAttack`** asks it two things: `Entity.isAttackable`, which
is true by default and false for a dropped item, an experience orb, a falling
block, a thrown eye of ender, a firework rocket and any arrow outside
`EntityTypeTags.REDIRECTABLE_PROJECTILE`, and `Entity.skipAttackInteraction`,
the hook by which a thing claims the swing for itself — an `Interaction`
entity uses it to record
who hit it, and a `BlockAttachedEntity` to re-enter through
`Entity.hurtOrSimulate` with zero damage. A *no* from the first or a *yes* from the second ends the swing
([damage outside `LivingEntity`](../../reference/non-living-damage) has
the per-class table). Then **`Player.deflectProjectile`**, which is why a
ghast fireball can be batted back: anything in
`EntityTypeTags.REDIRECTABLE_PROJECTILE` is turned around here rather than damaged, and the attack ends — all but a player's wind charge in its first five ticks, which `WindCharge` will not let be deflected.

Then three tests that are not gates but switches, each deciding what *kind*
of hit this is. **Sprint knockback** needs the scale above 0.9, plays a
sound, and adds a flat **0.5** to the knockback later; it is the step the
item bonus is added immediately after, which is why the item-bonus node in the
figure above sits where it does. `Player.canCriticalAttack`
needs falling, not on the ground, not climbing, not in water, not
mobility-restricted, not a passenger, **not sprinting**, a `LivingEntity`
target — and full strength as well. `Player.isSweepAttack` needs full
strength, *not* a crit, *not* sprint knockback, on the ground, moving slower
than 2.5× the walking speed, and something in `ItemTags.SWORDS`.

If the hit landed, the tail runs in order: `Player.causeExtraKnockback` —
which is also where the attacker's own motion is damped and sprinting
cancelled, using `LivingEntity.getKnockback` computed from
`Attributes.ATTACK_KNOCKBACK` through the enchantments and halved — then
`Player.doSweepAttack`, `Player.attackVisualEffects`,
`LivingEntity.setLastHurtMob`, `Player.itemAttackInteraction`,
`Player.damageStatsAndHearts`, and `Player.causeFoodExhaustion` of 0.1
([hunger and experience](hunger-and-experience#the-food-bar-is-four-numbers-and-a-file-of-constants)). If
it did not land, a no-damage sound. Either way `LivingEntity.postPiercingAttack`
runs at the end — the same hook a stab ends on, which is why the name says
*piercing* on a method that closes an ordinary swing ([the
spear](the-spear#the-stab)).

`Player.itemAttackInteraction` is itself three steps in a particular order:
`ItemStack.hurtEnemy` (the item's own hook and the use statistic, *not*
durability), then `EnchantmentHelper.doPostAttackEffectsWithItemSource`
([enchantments](../items/enchantments)),
then `ItemStack.postHurtEnemy`, which is where `Weapon`'s per-attack
durability cost is applied. `Weapon` (`DataComponents.WEAPON`) is a pair:
that cost, and `Weapon.disableBlockingForSeconds`, the axe's shield-breaking
rule. `LivingEntity.getSecondsToDisableBlocking` reads it back, but only when
the weapon in the attacker's hand is also their *active* item — so an axe
swung while the other hand is using something disables no shield.

`Player.doSweepAttack` damages every *living* entity in a box around the **primary
target** inflated by (1, 0.25, 1), for candidates within three blocks of the
**attacker**, excluding the attacker, the primary target, allies and marker
armour stands. Each takes `1.0 + Attributes.SWEEPING_DAMAGE_RATIO × base`,
run through `Player.getEnchantedDamage` and then scaled by the
attack-strength scale, plus a flat 0.4 knockback. Its sweep *sound* is
unguarded; the damage and the `ParticleTypes.SWEEP_ATTACK` particles sit
behind the server check.

Armour, the hurt cooldown, `DataComponents.BLOCKS_ATTACKS` and knockback
resistance decide how much of that damage arrives, and all four belong to [damage
and
death](../entities/damage-and-death#armour-and-why-big-hits-punch-through-it).

## Questions players ask

**Why does mashing do less damage?** Because the base damage is quadratic in
the charge and the enchantment bonus is linear in it. At half charge the base
is 0.2 + 0.25 × 0.8 — that is *s²* with *s* = 0.5 — so 40% of full, while the
enchantment bonus is at the full 50%. Mashing costs you more of the sword
than of the enchantments on it.

**Why does my sword make no sound until the server answers?** Because the
attack sounds are the counter-example to the client's own prediction rule:
`Player.playServerSideSound` excludes nobody, so every hit sound the attacker
hears arrives as a `ClientboundSoundPacket`, one round trip late ([what makes
a sound](../client/what-makes-a-sound)).

**Does the client predict any of this against a mob?** Almost none.
`Entity.hurtClient` returns false and neither `LivingEntity` nor `Mob`
overrides it, so on the client `Entity.hurtOrSimulate` reports that the hit
did not land and the entire block after it is skipped: no predicted
knockback, no sweep, no visual effects, no durability, no exhaustion. The
exceptions are the nine classes that do override it, and **none of them is a
mob** — a `RemotePlayer`, a vehicle, a hanging thing, an orb ([damage outside
`LivingEntity`](../../reference/non-living-damage) has the roll-call).
Against four of them — another player, a vehicle, an end crystal, a shulker
bullet — the whole block runs locally; a dropped item and an orb cannot be
attacked at all, and the block-attached ones either claim the swing through
`Entity.skipAttackInteraction` or refuse it in `Entity.hurtClient`. `Player.getEnchantedDamage` does nothing on `Player` either;
it returns its argument unchanged and only `ServerPlayer` overrides it. With
`Attributes.ATTACK_DAMAGE` not being client-syncable
([attributes](../entities/attributes)), the client's damage figure is
never authoritative wherever that block does run. That the client may not
decide a hit at all is the rule of [authority](../entities/authority#three-cases-read-on-both-sides),
applied to combat.

**Can a weapon be too close to swing?** No: it swings, and what an
`AttackRange` minimum withholds on the client is the attack packet. The
server's attack path would enforce at most the floor less three blocks — the
3.0-block leniency is subtracted from the minimum as well as added to the
maximum, which erases any floor under three — but
the one built-in weapon with a floor, the spear, never takes that path, and
its stab's own search starts at the floor.

**How does my client know how badly the pig was hurt?** It learns the new
health, not the hit. `ClientboundDamageEventPacket` carries no amount at all —
a damage-type holder, three entity ids and an optional source position — and
the victim's red flash and damage cooldown are reconstructed from it, with the
hurt sound too when the victim is you; a pig's hurt sound arrives as a sound
packet of its own ([damage and
death](../entities/damage-and-death#telling-everyone-and-what-a-block-replaces)
owns the packet). The health itself comes from [synched entity
data](../entities/synched-entity-data#nineteen-slots-and-where-the-numbers-come-from).

**Are sweep and knockback enchantment effects?** They are attributes, and the
two enchantments reach them differently: Sweeping Edge raises
`Attributes.SWEEPING_DAMAGE_RATIO` with an attribute modifier, while Knockback
leaves `Attributes.ATTACK_KNOCKBACK` alone and adds to the number read from it,
through an effect on the weapon. `Attributes.SWEEPING_DAMAGE_RATIO` defaults to zero, so a vanilla sweep does
1.0 — scaled by the attack-strength ratio, so slightly less than 1.0
anywhere in the sweep's legal window below full charge. And
`Attributes.ATTACK_KNOCKBACK` defaults to zero, so for an unenchanted sword
the *entire* attacker-side knockback is the sprint bonus of 0.5.

**What makes my own arm move?** Your own client does, before the server hears
about it. Every branch of `Minecraft.startAttack` that swings at all calls
`LivingEntity.swing`, which on the client only sets the animation state, and
the tail of the hit-result switch then sends `ServerboundPunchPacket`, a
packet with no fields at all. That is why a miss on an air block still animates,
and why the animation never waits for a round trip. The server's own
`LivingEntity.swing` then broadcasts `ClientboundSwingAnimationPacket` to your
**trackers** — the players who can see you — and not back to you, because you
played it a round trip ago.

**Why does my swing look different with a different weapon?** Because swing
duration is a data component — `ItemStack.getAttackAnimation` returns a
`SwingAnimation` — not a constant six ticks;
`MobEffects.MINING_FATIGUE` stretches it and haste shortens it. The
animation state itself is one `LivingEntity.SwingState`, which holds the
current `LivingEntity.SwingDescription` (hand, animation and length in
ticks), a tick count and the progress from zero to one, and
`ServerGamePacketListenerImpl.handlePunch` is where the server receives yours. Crit particles are the exception to the
never-echoed rule: they go to the **attacker**'s trackers and to the attacker
too, while naming the **victim**.

**Is this the only way to hit something in melee?** No. Two other paths end
in damage and neither goes through `Player.attack`: a `PiercingWeapon`
short-circuits before the hit-result switch, and a `KineticWeapon` is
reached from item *use* rather than attack. Both are [the
spear](the-spear).

> **For a 1.21-era reader.** *ServerboundSwingPacket* and *LocalPlayer.swing*
> are gone: after the local `LivingEntity.swing`, `Minecraft.startAttack`
> sends `ServerboundPunchPacket` itself, and the server passes the swing on as
> `ClientboundSwingAnimationPacket` rather than as a `ClientboundAnimatePacket`
> action. The fields *swinging*, *swingingArm*, *swingTime* and *attackAnim*
> are now one `LivingEntity.SwingState`, and the hurt cooldown that was
> `Entity.invulnerableTime` is `LivingEntity.damageCooldownTime`; the old name
> is now a separate timer that a hit does not set.

## Where to look

Two methods are the whole lecture, and they are worth reading in this order.
**`Minecraft.startAttack`** is the client's branch point: almost every reason
not to swing is in it (the rest are around it: `Minecraft.tick` skips the
keybind drain while a screen or an overlay is up, and `Minecraft.handleKeybinds` swallows
the click while you are using an item), and so are the two branches that do.
**`Player.attack`** is the server's, and everything surprising about melee
combat is the order of the lines in it — read it once for the shape and a
second time for where `Player.baseDamageScaleFactor` and
`Item.getAttackDamageBonus` land relative to the ×1.5.

Around them: **`ServerGamePacketListenerImpl.handleAttack`** for the nine
checks between the packet and the method, **`AttackRange`** for the reach
floor the server subtracts away, and **`Weapon`** for the two-field component
that charges the durability. **`Player.doSweepAttack`** and
**`Player.itemAttackInteraction`** are the two halves of the tail worth
opening on their own. On the wire, **`ServerboundAttackPacket`** takes ten
seconds to read and **`ClientboundDamageEventPacket`** is worth the same for
what it leaves out. Two doors this page only points at:
**`ProjectileUtil`**, which is where the entity sweep happens, and
**`SwingAnimation`**, the component behind the arm.

---

*Rules: names, never code · how the system works, not how the code reads ·
newest version only · every backticked name passes `tools/verify_names.py`.*
