Fix: prevent a single fire command from spawning duplicate bullets with the same ID#89
Merged
Merged
Conversation
flemming-n-larsen
approved these changes
Jun 3, 2026
flemming-n-larsen
added a commit
that referenced
this pull request
Jun 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A queued
BulletCommandcould be fired more than once by the battle engine,producing two (or more)
BulletPeers that share the samebulletId. Thischange marks a
BulletCommandas consumed the moment it produces a bullet andskips already-consumed commands on subsequent turns, so each fire command yields
at most one bullet.
Changes
robocode.core–BulletCommandtransient boolean consumedflag withisConsumed()/consume()accessors. It istransientbecause it ispurely engine bookkeeping and must not become part of the serialized wire
format exchanged with the robot host.
robocode.battle–RobotPeer.fireBullets(...)BulletCommandwhoseisConsumed()istrue.BulletPeer, callconsume()sothe same command (and its
bulletId) cannot fire again on a later turn.When do two bullets get the same ID?
Bullet IDs are assigned once, on the host side, when the robot calls
fire()/setFire(). InBasicRobotProxy.setFireImpl(...)the proxyincrements
nextBulletId, stores it inside a newBulletCommand, and appendsthat command to the shared
ExecCommandsbullet list. The ID is therefore fixedand unique per fire call — the duplication does not come from ID
generation, it comes from the same command being executed twice on the
battle side.
The battle side replays commands like this every turn:
RobotPeer.performLoadCommands()reads the latestExecCommandsfrom thecommandsAtomicReferenceand callsfireBullets(currentCommands.getBullets())every turn.fireBullets(...)walks the bullet-command list. If the gun is still hot(
gunHeat > 0), it returns early without removing the command — the firerequest stays pending in the list until the gun cools down.
The problem appears when the robot stops producing fresh commands while an
old command list is still installed, for example when the robot:
execute()/ returns fromrun(), orIn that situation the
commandsreference keeps handing back the sameExecCommandsinstance turn after turn, so the sameBulletCommandobjects are re-processed repeatedly. A pending fire command then behaves like
this:
gunHeat == 0, the command fires and aBulletPeeriscreated with
bulletCmd.getBulletId().list.
once more and fires a second
BulletPeer— reusing the identicalbulletIdbaked into that command.The result is two live bullets sharing one ID, which corrupts any
ID-keyed bookkeeping (the host tracks bullets in a
Mapkeyed bybulletId,and downstream snapshot consumers assume IDs are unique).
Fix rationale
Marking the command
consumedafter it spawns its bullet makes the commandsingle-shot: it fires exactly once regardless of how many times the persisted
command list is replayed, guaranteeing one
bulletId↔ one bullet.