Improve netcode and simplify (#4)
All checks were successful
Build & deploy / Build & deploy website (push) Successful in 32s
Build & deploy / Build & publish server image (push) Successful in 1m2s

- 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:
Andras Schmelczer 2026-09-15 07:57:57 +01:00
commit 93a7fe01ab

View file

@ -1,8 +1,3 @@
// Round-trip tests for the custom wire serializer — the single most fragile,
// highest-blast-radius mechanism in the codebase (every networked message goes
// through it, and dispatch is keyed on class name). We exercise the BUILT shared
// bundle (shared/lib/main.js) rather than the TS source, so the decorators are
// already applied exactly as they ship and there is no transform/ESM ambiguity.
import { describe, it, expect } from 'vitest';
import { createRequire } from 'node:module';
@ -17,21 +12,27 @@ const {
MoveActionCommand,
UpdatePropertyCommand,
PropertyUpdatesForObject,
// networked entity bases — their toArray() must mirror their constructor order
CharacterBase,
PlanetBase,
ProjectileBase,
LampBase,
// geometry — single source of truth shared by server & client prediction
headRadius,
feetRadius,
boundRadius,
headOffset,
leftFootOffset,
rightFootOffset,
CharacterMovementSnapshot,
} = shared;
describe('serialization round-trip (built shared lib)', () => {
it('preserves the authoritative leap cooldown for prediction', () => {
const snapshot = new CharacterMovementSnapshot(0, [1, 2], [0, 1], [0, 1], 5, 0, 0.25);
const out = deserialize(serialize(snapshot));
expect(out).toBeInstanceOf(CharacterMovementSnapshot);
expect(out.leapCooldownRemaining).toBe(0.25);
});
it('reconstructs a ServerAnnouncement as the right class with its payload', () => {
const [out] = deserialize(serialize([new ServerAnnouncement('Hello <b>world</b>')]));
expect(out).toBeInstanceOf(ServerAnnouncement);
@ -41,7 +42,6 @@ describe('serialization round-trip (built shared lib)', () => {
it('round-trips a Circle and rounds floats to 3 decimals', () => {
const out = deserialize(serialize(new Circle([12.34567, -7.1], 5.43219)));
expect(out).toBeInstanceOf(Circle);
// serialize.ts rounds every float via toFixed(3)
expect(out.center[0]).toBeCloseTo(12.346, 6);
expect(out.center[1]).toBeCloseTo(-7.1, 6);
expect(out.radius).toBeCloseTo(5.432, 6);
@ -68,8 +68,6 @@ describe('serialization round-trip (built shared lib)', () => {
});
it('preserves per-item class identity across a mixed batch', () => {
// A clobbered name→constructor mapping (two classes sharing a name) would
// surface here as an item deserializing to the wrong class.
const batch = [
new ServerAnnouncement('a'),
new MoveActionCommand([0, 1], 5),
@ -132,14 +130,25 @@ describe('networked entity round-trips (toArray ↔ constructor contract)', () =
it('round-trips a PlanetBase (derived centre/radius recomputed from vertices)', () => {
const out = deserialize(
serialize(new PlanetBase(10, [[0, 0], [100, 0], [50, 100]], 0.7, true)),
serialize(
new PlanetBase(
10,
[
[0, 0],
[100, 0],
[50, 100],
],
0.7,
true,
),
),
);
expect(out).toBeInstanceOf(PlanetBase);
expect(out.id).toBe(10);
expect(out.vertices).toHaveLength(3);
expect(out.ownership).toBeCloseTo(0.7, 5);
expect(out.isKeystone).toBe(true);
expect(out.center[0]).toBeCloseTo(50, 5); // recomputed by the constructor
expect(out.center[0]).toBeCloseTo(50, 5);
});
});
@ -147,7 +156,7 @@ describe('shared character geometry — single source of truth', () => {
it('matches the known body layout', () => {
expect(headRadius).toBe(50);
expect(feetRadius).toBe(20);
expect(boundRadius).toBe((headRadius + feetRadius * 2) * 2); // 180
expect(boundRadius).toBe((headRadius + feetRadius * 2) * 2);
});
it('posture offsets are measured from the centre of mass (they sum to ~0)', () => {