drive automatic fire from server ticks
All checks were successful
build / image (push) Successful in 10s

This commit is contained in:
Syncer Deploy
2026-08-28 12:21:37 -03:00
parent 96b73922cc
commit a9a8375480
2 changed files with 46 additions and 1 deletions

View File

@@ -125,7 +125,14 @@ const shooterDefinition = defineNetworkedGame<
if (!player.alive) continue; if (!player.alive) continue;
simulateMovement(player, deltaSeconds); simulateMovement(player, deltaSeconds);
emitFootstep(state, player, tick, emit); emitFootstep(state, player, tick, emit);
if (player.bot) tryFireWeapon(state, player, emit); const repeatingAutomaticFire =
player.kind === "human" &&
player.firing &&
player.lastFirePressed &&
WEAPONS[player.weapon].automatic;
if (player.bot || repeatingAutomaticFire) {
tryFireWeapon(state, player, emit);
}
collectNearbyPickup(state, player, emit); collectNearbyPickup(state, player, emit);
player.lastFirePressed = player.firing; player.lastFirePressed = player.firing;
} }

View File

@@ -4,6 +4,7 @@ import {
BOT_COUNT, BOT_COUNT,
BOT_ID_BASE, BOT_ID_BASE,
Weapon, Weapon,
createShooterGame,
shooterGame, shooterGame,
} from "../dist/index.js"; } from "../dist/index.js";
@@ -61,6 +62,43 @@ test("backend simulation is deterministic", () => {
assert.deepEqual(runHeadlessMatch(2_400), runHeadlessMatch(2_400)); assert.deepEqual(runHeadlessMatch(2_400), runHeadlessMatch(2_400));
}); });
test("held automatic fire follows server ticks while semi-auto remains edge-triggered", () => {
const game = createShooterGame({ botCount: 0 });
const runHeldFire = (weapon, ticks) => {
const server = game.createServer();
server.addPlayer(1);
server.currentState.players.get(1).weapon = weapon;
const packet = {
sequence: 1,
targetTick: 1,
observedTick: 0,
input: {
strafe: 0,
forward: 0,
yaw: 0,
pitch: 0,
fire: true,
sprint: false,
reload: false,
weapon,
},
};
assert.deepEqual(server.submitInput(1, packet), { accepted: true });
let shots = 0;
for (let tick = 0; tick < ticks; tick += 1) {
if (tick > 0 && tick % 10 === 0) {
assert.deepEqual(server.submitInput(1, packet), { accepted: true });
}
shots += server.step().events.filter((event) => event.type === "shot").length;
}
return shots;
};
assert.equal(runHeldFire(Weapon.PulseRifle, 60), 10);
assert.equal(runHeldFire(Weapon.Scattergun, 100), 1);
});
test("human hitscan damages the historical target without rewinding live positions", () => { test("human hitscan damages the historical target without rewinding live positions", () => {
const server = shooterGame.createServer(); const server = shooterGame.createServer();
server.addPlayer(1); server.addPlayer(1);