@javelin/net
Version:
Networking protocol and utilities for Javelin ECS.
192 lines • 5.54 kB
JavaScript
import { $kind, assert, createStackPool, FieldKind, isPrimitiveField, isSchema, isSimple, mutableEmpty, } from "@javelin/core";
import { getSchemaId } from "@javelin/ecs";
import { encode, uint16, uint32, uint8, } from "@javelin/pack";
import { encodeModel } from "./model";
export const $buffer = Symbol("javelin_array_buffer");
export function createOp() {
return {
data: [],
view: [],
byteLength: 0,
};
}
export function resetOp(op) {
mutableEmpty(op.data);
mutableEmpty(op.view);
op.byteLength = 0;
return op;
}
export const messageOpPool = createStackPool(createOp, resetOp, 1000);
export function insert(op, data, view) {
var _a;
op.data.push(data);
op.view.push(view !== null && view !== void 0 ? view : $buffer);
op.byteLength += view
? view.byteLength * ((_a = view.length) !== null && _a !== void 0 ? _a : 1)
: data.byteLength;
return op.data.length - 1;
}
export function modify(op, index, data) {
const current = op.data[index];
const view = op.view[index];
op.data[index] = data;
if (view === $buffer) {
op.byteLength +=
data.byteLength - current.byteLength;
}
}
/**
* Create a snapshot message op.
* @example
* [
* entity: uint32,
* count: uint8,
* components: [schemaId: uint8, encoded: *][],
* ]
* @param entity
* @param components
* @returns MessageOp
*/
export function snapshot(model, entity, components) {
const op = messageOpPool.retain();
const count = components.length;
insert(op, entity, uint32);
insert(op, count, uint8);
for (let i = 0; i < count; i++) {
const component = components[i];
const schemaId = getSchemaId(component);
const encoded = encode(component, model[schemaId]);
insert(op, schemaId, uint8);
insert(op, encoded);
}
return op;
}
function patchInner(op, node, patch, total = 0, traverse) {
var _a;
const { changes, children } = patch;
const { size } = changes;
if (size > 0) {
insert(op, node.id, uint8);
insert(op, (_a = traverse === null || traverse === void 0 ? void 0 : traverse.length) !== null && _a !== void 0 ? _a : 0, uint8);
if (traverse !== undefined) {
for (let i = 0; i < traverse.length; i++) {
const [traverseKey, traverseView] = traverse[i];
insert(op, traverseKey, traverseView);
}
}
insert(op, size, uint8);
if (isSchema(node)) {
changes.forEach((value, key) => {
const child = node.fieldsByKey[key];
insert(op, child.id, uint8);
if (isPrimitiveField(child)) {
insert(op, value, child);
}
else {
insert(op, encode(value, child));
}
});
}
else {
assert("element" in node);
const element = node.element;
switch (node[$kind]) {
case FieldKind.Array:
changes.forEach((value, key) => {
if (key === "length")
return;
insert(op, Number(key), uint16);
if (isPrimitiveField(element)) {
insert(op, value, element);
}
else {
insert(op, encode(value, element));
}
});
break;
}
}
}
if (!isSimple(node)) {
if (isSchema(node)) {
children.forEach((changes, key) => {
total = patchInner(op, node.fieldsByKey[key], changes, total, traverse);
});
}
else {
assert("element" in node);
const element = node.element;
children.forEach(changes => (total = patchInner(op, element, changes, total, traverse)));
}
}
return total + 1;
}
/**
* Create a patch message op.
* [entity, schemaId, [field, traverse, [operation, ...args]*]*]
* @param model
* @param entity
* @param schemaId
* @param patch
* @returns MessageOp
*/
export function patch(model, entity, patch) {
const op = messageOpPool.retain();
const { schemaId } = patch;
const root = model[schemaId];
insert(op, entity, uint32);
insert(op, schemaId, uint8);
modify(op, insert(op, 0, uint8), patchInner(op, root, patch));
return op;
}
/**
* Create a detach message op.
* @example
* [
* entity: uint32,
* count: uint8,
* schemaIds: uint8[],
* ]
* @param entity
* @param schemaIds
* @returns MessageOp
*/
export function detach(entity, schemaIds) {
const op = messageOpPool.retain();
const count = schemaIds.length;
insert(op, entity, uint32);
insert(op, count, uint8);
for (let i = 0; i < count; i++) {
insert(op, schemaIds[i], uint8);
}
return op;
}
/**
* Create a destroy message op.
* @example
* [
* entity: uint32,
* ]
* @param entity
* @returns MessageOp
*/
export function destroy(entity) {
const op = messageOpPool.retain();
insert(op, entity, uint32);
return op;
}
/**
* Create a model message op.
* @example
* [
* model: *,
* ]
* @param model
* @returns MessageOp
*/
export function model(model) {
const op = messageOpPool.retain();
insert(op, encodeModel(model));
return op;
}
//# sourceMappingURL=message_op.js.map