@javelin/net
Version:
Networking protocol and utilities for Javelin ECS.
126 lines • 5.38 kB
JavaScript
import { createPatch, getSchemaId, resetPatch, UNSAFE_internals, } from "@javelin/ecs";
import { createEntityMap } from "./entity_map";
import EntityPriorityQueue from "./entity_priority_queue";
import * as Message from "./message";
import * as MessageOp from "./message_op";
export function createMessageProducer(options = {}) {
const { maxByteLength = Infinity } = options;
const queue = [Message.createMessage()];
const entityPriorities = new EntityPriorityQueue();
const entityUpdates = createEntityMap();
const entityPatches = createEntityMap();
let previousModel = null;
function amplify(entity, priority) {
var _a;
entityPriorities.changePriority(entity, ((_a = entityPriorities.getPriority(entity)) !== null && _a !== void 0 ? _a : 0) + priority);
}
function enqueue(op, kind) {
let message = queue[0];
// calculate the new message length. if we exceed the maxByteLength threshold,
// create an enqueue a new message
if (message === undefined ||
op.byteLength + message.byteLength > maxByteLength) {
message = Message.createMessage();
queue.unshift(message);
}
Message.insert(message, kind, op);
return message;
}
function attach(entity, components) {
enqueue(MessageOp.snapshot(Message.getEnhancedModel(), entity, components), Message.MessagePartKind.Attach);
}
function update(entity, components, priority = 1) {
let updates = entityUpdates[entity];
if (updates === undefined) {
updates = entityUpdates[entity] = new Map();
}
// overwrite existing component updates with latest component, in case it
// changed (i.e. the original component was detached and a new instance
// was attached)
for (let i = 0; i < components.length; i++) {
const component = components[i];
updates.set(getSchemaId(component), component);
}
amplify(entity, priority);
}
function patch(entity, component, priority = 1) {
var _a;
const id = getSchemaId(component);
const patches = ((_a = entityPatches[entity]) !== null && _a !== void 0 ? _a : (entityPatches[entity] = new Map()));
const patch = patches.get(id);
if (patch === undefined) {
patches.set(id, createPatch(component));
}
else {
createPatch(component, patch);
}
amplify(entity, priority);
}
function detach(entity, components) {
enqueue(MessageOp.detach(entity, components.map(getSchemaId)), Message.MessagePartKind.Detach);
}
function destroy(entity) {
enqueue(MessageOp.destroy(entity), Message.MessagePartKind.Destroy);
// remove any planned patches or updates since the entity was destroyed
delete entityPatches[entity];
delete entityUpdates[entity];
entityPriorities.remove(entity);
}
function take(includeModel = previousModel !== UNSAFE_internals.model) {
const message = queue.pop() || Message.createMessage();
// insert a new model automatically if it has changed. otherwise, the
// caller can pass `true` to force model inclusion
if (includeModel) {
Message.model(message);
previousModel = UNSAFE_internals.model;
}
const model = Message.getEnhancedModel();
while (true) {
// take the next-highest priority entity out of the priority queue
const entity = entityPriorities.poll();
if (entity === null || entity === undefined) {
break;
}
const updates = entityUpdates[entity];
const patches = entityPatches[entity];
// include component updates
if (updates && updates.size > 0) {
const components = Array.from(updates.values());
const op = MessageOp.snapshot(model, entity, components);
// message would exceed max byte length
if (op.byteLength + (message === null || message === void 0 ? void 0 : message.byteLength) >= maxByteLength) {
break;
}
Message.insert(message, Message.MessagePartKind.Snapshot, op);
updates.clear();
}
// include component patches
if (patches && patches.size > 0) {
for (const patch of patches.values()) {
if (patch.changes.size === 0) {
continue;
}
const op = MessageOp.patch(model, entity, patch);
// message would exceed max byte length
if (op.byteLength + (message === null || message === void 0 ? void 0 : message.byteLength) >= maxByteLength) {
break;
}
Message.insert(message, Message.MessagePartKind.Patch, op);
// reset the patch since it was incorporated into a message
resetPatch(patch);
}
}
}
return message;
}
return {
amplify,
attach,
update,
patch,
detach,
destroy,
take,
};
}
//# sourceMappingURL=message_producer.js.map