diff --git a/packages/entity/entity.resource.js b/packages/entity/entity.synchronized.js similarity index 99% rename from packages/entity/entity.resource.js rename to packages/entity/entity.synchronized.js index 8ba6044..de12f71 100644 --- a/packages/entity/entity.resource.js +++ b/packages/entity/entity.synchronized.js @@ -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; diff --git a/packages/entity/index.js b/packages/entity/index.js index 608b61e..fd9855e 100644 --- a/packages/entity/index.js +++ b/packages/entity/index.js @@ -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'; diff --git a/packages/entity/list/index.js b/packages/entity/list/index.js index 5a81259..d4e19c1 100644 --- a/packages/entity/list/index.js +++ b/packages/entity/list/index.js @@ -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 {}) { diff --git a/packages/entity/trait/index.js b/packages/entity/trait/index.js index 8e4bc2c..3bdaf05 100644 --- a/packages/entity/trait/index.js +++ b/packages/entity/trait/index.js @@ -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 {}) { diff --git a/packages/net/index.js b/packages/net/index.js index 8342f6e..55930ea 100644 --- a/packages/net/index.js +++ b/packages/net/index.js @@ -7,3 +7,17 @@ export { registerPacket, SocketIoParser, } from './packet'; + +export { + allSynchronizeds, + ClientSynchronizer, + idFromSynchronized, + registerSynchronized, + ServerSynchronizer, + SynchronizedMixin, + SynchronizedPacket, + SynchronizedCreatePacket, + SynchronizedDestroyPacket, + SynchronizedUpdatePacket, + synchronizedFromId, +} from './s13n'; diff --git a/packages/net/s13n/client.js b/packages/net/s13n/client.js new file mode 100644 index 0000000..d84ba7b --- /dev/null +++ b/packages/net/s13n/client.js @@ -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; + } + +} diff --git a/packages/net/s13n/index.js b/packages/net/s13n/index.js new file mode 100644 index 0000000..ebce87d --- /dev/null +++ b/packages/net/s13n/index.js @@ -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'; diff --git a/packages/net/s13n/registry.js b/packages/net/s13n/registry.js new file mode 100644 index 0000000..59259d2 --- /dev/null +++ b/packages/net/s13n/registry.js @@ -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; +} diff --git a/packages/net/s13n/server.js b/packages/net/s13n/server.js new file mode 100644 index 0000000..f5b892d --- /dev/null +++ b/packages/net/s13n/server.js @@ -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; + } + +} diff --git a/packages/net/s13n/synchronized-create.packet.js b/packages/net/s13n/synchronized-create.packet.js new file mode 100644 index 0000000..fd1ee58 --- /dev/null +++ b/packages/net/s13n/synchronized-create.packet.js @@ -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; + } + +} diff --git a/packages/net/s13n/synchronized-destroy.packet.js b/packages/net/s13n/synchronized-destroy.packet.js new file mode 100644 index 0000000..4282e8f --- /dev/null +++ b/packages/net/s13n/synchronized-destroy.packet.js @@ -0,0 +1,4 @@ +import {SynchronizedPacket} from './synchronized.packet'; + +export class SynchronizedDestroyPacket extends SynchronizedPacket { +} diff --git a/packages/net/s13n/synchronized-update.packet.js b/packages/net/s13n/synchronized-update.packet.js new file mode 100644 index 0000000..0c29102 --- /dev/null +++ b/packages/net/s13n/synchronized-update.packet.js @@ -0,0 +1,4 @@ +import {SynchronizedPacket} from './synchronized.packet'; + +export class SynchronizedUpdatePacket extends SynchronizedPacket { +} diff --git a/packages/net/packet/synchronized.js b/packages/net/s13n/synchronized.js similarity index 73% rename from packages/net/packet/synchronized.js rename to packages/net/s13n/synchronized.js index ae84af4..f6c0ae3 100644 --- a/packages/net/packet/synchronized.js +++ b/packages/net/s13n/synchronized.js @@ -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; + } } diff --git a/packages/net/s13n/synchronized.packet.js b/packages/net/s13n/synchronized.packet.js new file mode 100644 index 0000000..0719f27 --- /dev/null +++ b/packages/net/s13n/synchronized.packet.js @@ -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', + }, + }, + }; + } + +} diff --git a/packages/resource/index.js b/packages/resource/index.js index 0409774..73609c4 100644 --- a/packages/resource/index.js +++ b/packages/resource/index.js @@ -1,10 +1,2 @@ -export { - allResources, - idFromResource, - resourceFromId, - registerResource, -} from './registry'; export {Resource} from './resource'; export {ResourceRegistry, globalRegistry} from './store'; - - diff --git a/packages/resource/packet/resource.packet.js b/packages/resource/packet/resource.packet.js deleted file mode 100644 index f5b7a7f..0000000 --- a/packages/resource/packet/resource.packet.js +++ /dev/null @@ -1,13 +0,0 @@ -import {Packet} from '@avocado/net'; - -export class ResourcePacket extends Packet { - - static get schema() { - return { - ...super.schema, - resourceType: 'string', - uuid: 'string', - }; - } - -} diff --git a/packages/resource/registry.js b/packages/resource/registry.js deleted file mode 100644 index f753dcc..0000000 --- a/packages/resource/registry.js +++ /dev/null @@ -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; -} diff --git a/packages/state/index.js b/packages/state/index.js deleted file mode 100644 index 68aae8c..0000000 --- a/packages/state/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export {Synchronized} from './synchronized'; -export {Synchronizer} from './synchronizer'; diff --git a/packages/state/package.json b/packages/state/package.json deleted file mode 100644 index 23f1ab5..0000000 --- a/packages/state/package.json +++ /dev/null @@ -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" - } -} diff --git a/packages/state/synchronized.js b/packages/state/synchronized.js deleted file mode 100644 index 4cf3c20..0000000 --- a/packages/state/synchronized.js +++ /dev/null @@ -1,11 +0,0 @@ -export function Synchronized(Superclass) { - return class Synchronized extends Superclass { - - acceptPacket(packet) {} - - packetsForUpdate(force = false) { - return []; - } - - } -} diff --git a/packages/state/synchronizer.js b/packages/state/synchronizer.js deleted file mode 100644 index 18d05df..0000000 --- a/packages/state/synchronizer.js +++ /dev/null @@ -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; - } - -} diff --git a/packages/topdown/index.js b/packages/topdown/index.js index 543f1b6..83c4880 100644 --- a/packages/topdown/index.js +++ b/packages/topdown/index.js @@ -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'; diff --git a/packages/topdown/layer.js b/packages/topdown/layer.js index 34133cd..f8022da 100644 --- a/packages/topdown/layer.js +++ b/packages/topdown/layer.js @@ -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 {}) { diff --git a/packages/topdown/layers.js b/packages/topdown/layers.js index 7a17d88..31b1644 100644 --- a/packages/topdown/layers.js +++ b/packages/topdown/layers.js @@ -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 {}) { diff --git a/packages/topdown/room.resource.js b/packages/topdown/room.synchronized.js similarity index 99% rename from packages/topdown/room.resource.js rename to packages/topdown/room.synchronized.js index 3136502..fde1eb6 100644 --- a/packages/topdown/room.resource.js +++ b/packages/topdown/room.synchronized.js @@ -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) { diff --git a/packages/topdown/tiles.js b/packages/topdown/tiles.js index 304466e..d586001 100644 --- a/packages/topdown/tiles.js +++ b/packages/topdown/tiles.js @@ -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 {}) {