Improve netcode and simplify (#4)
- Simplify the logic wherever possible to remove overengineering and simplify the netcode - Actually improve the netcode - The new lines are mainly the package locks and tests Reviewed-on: https://home.schmelczer.dev/git/andras/decla-red/pulls/4
This commit is contained in:
parent
5d854440ec
commit
93a7fe01ab
1 changed files with 15073 additions and 4754 deletions
101
test/client-send-cadence.test.ts
Normal file
101
test/client-send-cadence.test.ts
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
import { CommandSocket } from '../frontend/src/scripts/commands/command-socket';
|
||||
import { setFrameTimeMs } from '../frontend/src/scripts/helper/prediction/local-character-predictor';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const shared = require('../shared/lib/main.js');
|
||||
const { vec2 } = require('../shared/node_modules/gl-matrix');
|
||||
const { settings, MoveActionCommand, ClientHeartbeatCommand } = shared;
|
||||
|
||||
const drive = (
|
||||
frameRate: number,
|
||||
seconds: number,
|
||||
onFrame?: (socket: any) => void,
|
||||
connected = true,
|
||||
) => {
|
||||
const batches: Array<string> = [];
|
||||
const socket = {
|
||||
connected,
|
||||
emit: (_event: string, payload: string) => batches.push(payload),
|
||||
};
|
||||
const commandSocket = new CommandSocket(socket as never);
|
||||
|
||||
const frames = Math.round(frameRate * seconds);
|
||||
for (let f = 0; f < frames; f++) {
|
||||
setFrameTimeMs((f * 1000) / frameRate);
|
||||
onFrame?.(commandSocket);
|
||||
commandSocket.sendQueuedCommands();
|
||||
}
|
||||
return batches;
|
||||
};
|
||||
|
||||
describe('client send cadence', () => {
|
||||
it('is paced by the clock, not by the frame rate', () => {
|
||||
const seconds = 3;
|
||||
const expected = seconds / settings.clientSendInterval;
|
||||
|
||||
for (const frameRate of [60, 144, 240]) {
|
||||
const batches = drive(frameRate, seconds);
|
||||
expect(batches.length).toBeLessThanOrEqual(expected * 1.1);
|
||||
expect(batches.length).toBeGreaterThanOrEqual(expected * 0.85);
|
||||
}
|
||||
});
|
||||
|
||||
it('stays under the server allowance even at an absurd frame rate', () => {
|
||||
const seconds = 5;
|
||||
const batches = drive(1000, seconds);
|
||||
|
||||
expect(batches.length / seconds).toBeLessThan(settings.maxInboundMessagesPerSecond);
|
||||
});
|
||||
|
||||
it('still flushes real input on the frame it happens', () => {
|
||||
let sentOn = -1;
|
||||
const batches = drive(144, 0.1, (commandSocket) => {
|
||||
if (sentOn === -1) {
|
||||
commandSocket.queue(new MoveActionCommand(vec2.fromValues(1, 0), 0));
|
||||
sentOn = 0;
|
||||
}
|
||||
});
|
||||
|
||||
expect(batches.length).toBeGreaterThan(0);
|
||||
expect(batches[0]).toContain('MoveActionCommand');
|
||||
});
|
||||
|
||||
it('closes every batch with a heartbeat, so a held key keeps the ack moving', () => {
|
||||
const batches = drive(60, 1);
|
||||
|
||||
expect(batches.length).toBeGreaterThan(0);
|
||||
expect(batches.every((b) => b.includes(ClientHeartbeatCommand.name))).toBe(true);
|
||||
});
|
||||
|
||||
it('sends nothing at all while the transport is down', () => {
|
||||
const batches = drive(
|
||||
60,
|
||||
30,
|
||||
(commandSocket) =>
|
||||
commandSocket.queue(new MoveActionCommand(vec2.fromValues(1, 0), 0)),
|
||||
false,
|
||||
);
|
||||
|
||||
expect(batches).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('discards commands from the previous connection before the next join', () => {
|
||||
const batches: Array<string> = [];
|
||||
const socket = new CommandSocket({
|
||||
connected: true,
|
||||
emit: (_event: string, payload: string) => batches.push(payload),
|
||||
} as never);
|
||||
setFrameTimeMs(1000);
|
||||
socket.sendQueuedCommands();
|
||||
socket.queue(new MoveActionCommand(vec2.fromValues(1, 0), 1000));
|
||||
socket.reset();
|
||||
socket.sendQueuedCommands();
|
||||
|
||||
expect(batches).toHaveLength(2);
|
||||
expect(batches[1]).toContain(ClientHeartbeatCommand.name);
|
||||
expect(batches[1]).not.toContain(MoveActionCommand.name);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue