flow: to synchronized

This commit is contained in:
cha0s 2019-09-22 18:45:33 -05:00
parent 688820786d
commit 21ae93f1cc
26 changed files with 273 additions and 120 deletions

View File

@ -8,8 +8,8 @@ import {
merge,
mergeDiff,
} from '@avocado/core';
import {SynchronizedMixin} from '@avocado/net';
import {Resource} from '@avocado/resource';
import {Synchronized} from '@avocado/state';
import {EntityCreatePacket} from './packets/entity-create.packet';
import {hasTrait, lookupTrait} from './trait/registry';
@ -83,7 +83,7 @@ function enumerateTraitAccessorKeys(prototype) {
const decorate = compose(
EventEmitter,
Synchronized,
SynchronizedMixin,
);
let numericUid = 1;

View File

@ -1,4 +1,4 @@
export {Entity} from './entity.resource';
export {Entity} from './entity.synchronized';
export {EntityCreatePacket} from './packets/entity-create.packet';
export {EntityRemovePacket} from './packets/entity-remove.packet';

View File

@ -1,14 +1,12 @@
import {compose, EventEmitter} from '@avocado/core';
import {QuadTree, Rectangle, Vector} from '@avocado/math';
import {Synchronized} from '@avocado/state';
import {EntityCreatePacket} from '../packets/entity-create.packet';
import {EntityRemovePacket} from '../packets/entity-remove.packet';
import {Entity} from '../entity.resource';
import {Entity} from '../entity.synchronized';
const decorate = compose(
EventEmitter,
Synchronized,
);
export class EntityList extends decorate(class {}) {

View File

@ -1,9 +1,7 @@
import {compose, merge, mergeDiff, Property} from '@avocado/core';
import {Vector} from '@avocado/math';
import {Synchronized} from '@avocado/state';
const decorate = compose(
Synchronized,
);
export class Trait extends decorate(class {}) {

View File

@ -7,3 +7,17 @@ export {
registerPacket,
SocketIoParser,
} from './packet';
export {
allSynchronizeds,
ClientSynchronizer,
idFromSynchronized,
registerSynchronized,
ServerSynchronizer,
SynchronizedMixin,
SynchronizedPacket,
SynchronizedCreatePacket,
SynchronizedDestroyPacket,
SynchronizedUpdatePacket,
synchronizedFromId,
} from './s13n';

View File

@ -0,0 +1,58 @@
import {idFromSynchronized, synchronizedFromId} from './registry';
import {SynchronizedCreatePacket} from './synchronized-create.packet';
import {SynchronizedDestroyPacket} from './synchronized-destroy.packet';
import {SynchronizedUpdatePacket} from './synchronized-update.packet';
export class ClientSynchronizer {
constructor() {
this._synchronized = {};
}
acceptPackets(packets) {
for (let i = 0; i < packets.length; i++) {
const packet = packets[i];
if ((packet instanceof SynchronizedCreatePacket)) {
const json = packet.data.spec;
const {id, type} = packet.data.synchronized;
const Synchronized = synchronizedFromId(type);
if (!(type in this._synchronized)) {
this._synchronized[type] = {};
}
if (this._synchronized[type][id]) {
this._synchronized[type][id].fromJSON(json);
}
else {
this._synchronized[type][id] = new Synchronized(json);
}
}
else if ((packet instanceof SynchronizedUpdatePacket)) {
const {
synchronized: {id, type},
} = packet.data;
this._synchronized[type][id].acceptPacket(packet);
}
else if ((packet instanceof SynchronizedDestroyPacket)) {
const {
synchronized: {id, type},
} = packet.data;
this._synchronized[type][id].destroy();
this._synchronized[type][id] = null;
}
}
}
addSynchronized(synchronized) {
const type = idFromSynchronized(synchronized.constructor);
if (!(type in this._synchronized)) {
this._synchronized[type] = new Map();
}
const synchronizationId = synchronized.synchronizationId();
if (this._synchronized[type].has(synchronizationId)) {
return;
}
this._synchronized[type][synchronizationId] = synchronized;
}
}

View File

@ -0,0 +1,16 @@
export {
allSynchronizeds,
idFromSynchronized,
synchronizedFromId,
registerSynchronized,
} from './registry';
export {ClientSynchronizer} from './client';
export {ServerSynchronizer} from './server';
export {SynchronizedMixin} from './synchronized'
export {SynchronizedPacket} from './synchronized.packet';
export {SynchronizedCreatePacket} from './synchronized-create.packet';
export {SynchronizedDestroyPacket} from './synchronized-destroy.packet';
export {SynchronizedUpdatePacket} from './synchronized-update.packet';

View File

@ -0,0 +1,21 @@
let synchronizedId = 0;
const synchronizedToIdMap = new Map();
const idToSynchronizedMap = new Map();
export function allSynchronizeds() {
return Array.from(idToSynchronizedMap.values());
}
export function idFromSynchronized(Synchronized) {
return synchronizedToIdMap.get(Synchronized);
}
export function synchronizedFromId(id) {
return idToSynchronizedMap.get(id);
}
export function registerSynchronized(Synchronized) {
if (synchronizedToIdMap.has(Synchronized)) {
return;
}
const id = synchronizedId++;
synchronizedToIdMap.set(Synchronized, id);
idToSynchronizedMap.set(id, Synchronized);
return id;
}

View File

@ -0,0 +1,93 @@
import {idFromSynchronized, synchronizedFromId} from './registry';
import {SynchronizedCreatePacket} from './synchronized-create.packet';
import {SynchronizedDestroyPacket} from './synchronized-destroy.packet';
export class ServerSynchronizer {
constructor() {
this._added = [];
this._removed = [];
this._synchronized = {};
this._synchronizedFlat = [];
}
addSynchronized(synchronized) {
if (this.hasSynchronized(synchronized)) {
return;
}
else {
this._added.push(synchronized);
}
const type = idFromSynchronized(synchronized.constructor);
if (!(type in this._synchronized)) {
this._synchronized[type] = new Map();
}
this._synchronizedFlat.push(synchronized);
const synchronizationId = synchronized.synchronizationId();
this._synchronized[type].set(synchronizationId, synchronized);
}
destroy() {}
hasSynchronized(synchronized) {
return -1 !== this._synchronizedFlat.indexOf(synchronized);
}
removeSynchronized(synchronized) {
if (!this.hasSynchronized(synchronized)) {
return;
}
else {
this._removed.push(synchronized);
}
const index = this._synchronizedFlat.indexOf(synchronized);
this._synchronizedFlat.splice(index, 1);
const type = idFromSynchronized(synchronized.constructor);
const synchronizationId = synchronized.synchronizationId();
this._synchronized[type].delete(synchronizationId);
}
packetsFor(informed) {
const payload = [];
for (let i = 0; i < this._synchronizedFlat.length; i++) {
const synchronized = this._synchronizedFlat[i];
const id = synchronized.synchronizationId();
const type = idFromSynchronized(synchronized.constructor);
if (-1 !== this._added.indexOf(synchronized)) {
const packet = new SynchronizedCreatePacket({
synchronized: {
id,
type,
},
spec: synchronized.toJSON(),
});
payload.push(packet);
}
else if (-1 !== this._removed.indexOf(synchronized)) {
const packet = new SynchronizedDestroyPacket({
synchronized: {
id,
type,
},
});
payload.push(packet);
}
else {
const packets = synchronized.packetsFor(informed);
for (let j = 0; j < packets.length; j++) {
const packet = packets[j];
packet.data.synchronized = {
id,
type,
};
payload.push(packet);
}
}
}
this._added = [];
this._removed = [];
return payload;
}
}

View File

@ -0,0 +1,30 @@
import msgpack from 'msgpack-lite';
import {SynchronizedPacket} from './synchronized.packet';
export class SynchronizedCreatePacket extends SynchronizedPacket {
static pack(packet) {
const data = packet.data[1];
data.spec = msgpack.encode(data.spec);
return super.pack(packet);
}
static get schema() {
const superSchema = super.schema;
return {
...superSchema,
data: {
...superSchema.data,
spec: 'buffer',
},
};
}
static unpack(packet) {
const data = super.unpack(packet);
data.spec = msgpack.decode(data.spec);
return data;
}
}

View File

@ -0,0 +1,4 @@
import {SynchronizedPacket} from './synchronized.packet';
export class SynchronizedDestroyPacket extends SynchronizedPacket {
}

View File

@ -0,0 +1,4 @@
import {SynchronizedPacket} from './synchronized.packet';
export class SynchronizedUpdatePacket extends SynchronizedPacket {
}

View File

@ -2,8 +2,8 @@ export function SynchronizedMixin(Superclass) {
return class Synchronized extends Superclass {
constructor() {
super();
constructor(...args) {
super(...args);
this._idempotentPackets = [];
}
@ -11,15 +11,19 @@ export function SynchronizedMixin(Superclass) {
this._idempotentPackets = [];
}
destroy() {}
packets(informed) {}
packetsAreIdempotent() {
return true;
}
packets(informed) {
packetsFor(informed) {
if (this._idempotentPackets.length > 0) {
return this._idempotentPackets;
}
let packets = this.packetsForTick(informed);
let packets = this.packets(informed);
if (!packets) {
return [];
}
@ -30,7 +34,9 @@ export function SynchronizedMixin(Superclass) {
return packets;
}
packetsForTick(informed) {}
synchronizationId() {
return 0;
}
}

View File

@ -0,0 +1,17 @@
import {Packet} from '@avocado/net';
export class SynchronizedPacket extends Packet {
static get schema() {
return {
...super.schema,
data: {
synchronized: {
type: 'uint8',
id: 'uint32',
},
},
};
}
}

View File

@ -1,10 +1,2 @@
export {
allResources,
idFromResource,
resourceFromId,
registerResource,
} from './registry';
export {Resource} from './resource';
export {ResourceRegistry, globalRegistry} from './store';

View File

@ -1,13 +0,0 @@
import {Packet} from '@avocado/net';
export class ResourcePacket extends Packet {
static get schema() {
return {
...super.schema,
resourceType: 'string',
uuid: 'string',
};
}
}

View File

@ -1,21 +0,0 @@
let resourceId = 0;
const resourceToIdMap = new Map();
const idToResourceMap = new Map();
export function allResources() {
return Array.from(idToResourceMap.values());
}
export function idFromResource(Resource) {
return resourceToIdMap.get(Resource);
}
export function resourceFromId(id) {
return idToResourceMap.get(id);
}
export function registerResource(Resource) {
if (resourceToIdMap.has(Resource)) {
return;
}
const id = resourceId++;
resourceToIdMap.set(Resource, id);
idToResourceMap.set(id, Resource);
return id;
}

View File

@ -1,2 +0,0 @@
export {Synchronized} from './synchronized';
export {Synchronizer} from './synchronizer';

View File

@ -1,11 +0,0 @@
{
"name": "@avocado/state",
"version": "1.0.4",
"main": "index.js",
"author": "cha0s",
"license": "MIT",
"dependencies": {
"immutable": "4.0.0-rc.12",
"msgpack-lite": "0.1.26"
}
}

View File

@ -1,11 +0,0 @@
export function Synchronized(Superclass) {
return class Synchronized extends Superclass {
acceptPacket(packet) {}
packetsForUpdate(force = false) {
return [];
}
}
}

View File

@ -1,32 +0,0 @@
export class Synchronizer {
constructor(children) {
this.children = children;
this.childrenPacketsForUpdate = this.children.map((child) => {
return child.packetsForUpdate.bind(child);
});
}
acceptPacket(packet) {
for (let i = 0; i < this.children.length; i++) {
this.children[i].acceptPacket(packet);
}
}
addChild(child) {
this.children.push(child);
this.childrenPacketsForUpdate.push(child.packetsForUpdate.bind(child));
}
packetsForUpdate(force = false) {
const packetsForUpdate = [];
for (let i = 0; i < this.childrenPacketsForUpdate.length; i++) {
const childPacketsForUpdate = this.childrenPacketsForUpdate[i](force);
for (let j = 0; j < childPacketsForUpdate.length; j++) {
packetsForUpdate.push(childPacketsForUpdate[j]);
}
}
return packetsForUpdate;
}
}

View File

@ -1,7 +1,7 @@
export {Camera} from './camera';
export {Layer} from './layer';
export {LayerView} from './layer-view';
export {Room} from './room.resource';
export {Room} from './room.synchronized';
export {RoomView} from './room-view';
export {TilesRenderer} from './tiles-renderer';
export {Tiles} from './tiles';

View File

@ -2,7 +2,6 @@ import {compose, EventEmitter, Property} from '@avocado/core';
import {Entity, EntityCreatePacket, EntityList} from '@avocado/entity';
import {Vector} from '@avocado/math';
import {ShapeList} from '@avocado/physics';
import {Synchronized} from '@avocado/state';
import {Tiles} from './tiles';
import {Tileset} from './tileset';
@ -20,7 +19,6 @@ const decorate = compose(
Property('world', {
track: true,
}),
Synchronized,
);
export class Layer extends decorate(class {}) {

View File

@ -2,7 +2,6 @@ import * as I from 'immutable';
import {arrayUnique, compose, EventEmitter, flatten} from '@avocado/core';
import {EntityCreatePacket} from '@avocado/entity';
import {Synchronized} from '@avocado/state';
import {Layer} from './layer';
import {LayerCreatePacket} from './packets/layer-create.packet';
@ -10,7 +9,6 @@ import {TileUpdatePacket} from './packets/tile-update.packet';
const decorate = compose(
EventEmitter,
Synchronized,
);
export class Layers extends decorate(class {}) {

View File

@ -5,7 +5,6 @@ import {EntityCreatePacket, EntityPacket, EntityRemovePacket} from '@avocado/ent
import {Vector} from '@avocado/math';
import {RectangleShape} from '@avocado/physics';
import {Resource} from '@avocado/resource';
import {Synchronized} from '@avocado/state';
import {Layers} from './layers';
import {LayerCreatePacket} from './packets/layer-create.packet';
@ -24,7 +23,6 @@ const decorate = compose(
default: [0, 0],
track: true,
}),
Synchronized,
);
export class Room extends decorate(Resource) {

View File

@ -2,7 +2,6 @@ import * as I from 'immutable';
import {compose, EventEmitter} from '@avocado/core';
import {Rectangle, Vector} from '@avocado/math';
import {Synchronized} from '@avocado/state';
import {TileUpdatePacket} from './packets/tile-update.packet';
@ -11,7 +10,6 @@ const decorate = compose(
Vector.Mixin('size', 'width', 'height', {
default: [0, 0],
}),
Synchronized,
);
export class Tiles extends decorate(class {}) {