@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
85 lines (77 loc) • 2.69 kB
JavaScript
import { assert } from "../../../core/assert.js";
import { ResourceAccessKind } from "../../../core/model/ResourceAccessKind.js";
import { ResourceAccessSpecification } from "../../../core/model/ResourceAccessSpecification.js";
import { System } from "../../ecs/System.js";
import { NetworkIdentity } from "./components/NetworkIdentity.js";
/**
* ECS system that owns the per-entity replication lifecycle.
*
* Responsibilities:
* - When an entity gains a {@link NetworkIdentity}, allocate a `network_id`
* in the peer's slot table (or honour an explicit pre-set one — used by
* `Snapshotter` and incoming-packet flows).
* - When an entity loses its `NetworkIdentity` (or is destroyed), free the
* slot.
*
* **Not** responsible for the per-tick `begin_tick` / `end_tick` cadence —
* the engine's `EntityManager` doesn't expose pre/post-simulate hooks, so the
* application code wraps its `engine.simulate(dt)` call:
*
* ```js
* peer.begin_tick(frame_number);
* engine.simulate(dt);
* peer.end_tick();
* ```
*
* Add this system to the engine once per `NetworkPeer` instance:
*
* ```js
* const peer = new NetworkPeer({ ... });
* const network_system = new NetworkSystem(peer);
* engine.entityManager.addSystem(network_system);
* ```
*
* @author Alex Goldring
* @copyright Company Named Limited (c) 2025
*/
export class NetworkSystem extends System {
dependencies = [NetworkIdentity];
components_used = [
ResourceAccessSpecification.from(NetworkIdentity, ResourceAccessKind.Write),
];
/**
* @param {NetworkPeer} peer
*/
constructor(peer) {
super();
assert.ok(peer && peer.slot_table, 'NetworkSystem: peer must be a NetworkPeer instance with a slot_table');
/**
* @type {NetworkPeer}
*/
this.peer = peer;
}
/**
* @param {NetworkIdentity} identity
* @param {number} entity
*/
link(identity, entity) {
if (identity.network_id < 0) {
// Local spawn: allocate a fresh network_id.
identity.network_id = this.peer.slot_table.allocate(entity);
} else {
// Pre-set (e.g. from a Snapshotter or a remote peer's spawn action).
// Honour the explicit ID so both sides share the same slot.
this.peer.slot_table.allocate_at(identity.network_id, entity);
}
}
/**
* @param {NetworkIdentity} identity
* @param {number} entity
*/
unlink(identity, entity) {
if (identity.network_id >= 0) {
this.peer.slot_table.free(identity.network_id);
identity.network_id = -1;
}
}
}