From ddd9b1c8e87807b0b07474ca6050040c72c8088e Mon Sep 17 00:00:00 2001 From: cha0s Date: Sun, 29 Sep 2019 13:19:57 -0500 Subject: [PATCH] flow: massive sync update --- packages/entity/entity.synchronized.js | 22 +-- packages/entity/list/index.js | 40 ++--- packages/entity/trait/index.js | 54 +++--- packages/math/matrix.spec.coffee | 29 ---- packages/math/{matrix.js => matrix/index.js} | 0 packages/net/packet/packet.js | 9 +- packages/net/s13n/client.js | 58 ++++--- packages/net/s13n/server.js | 2 +- packages/net/s13n/synchronized.js | 8 + packages/sound/traits/audible.trait.js | 4 +- packages/timing/traits/animated.trait.js | 33 ++-- packages/topdown/layer-view.js | 3 + packages/topdown/layer.js | 53 +++--- packages/topdown/layers.js | 53 ++++-- .../topdown/packets/layer-create.packet.js | 18 -- .../packets/layer-update-entities.packet.js | 0 .../packets/layer-update-tiles.packet.js | 0 .../layer-update-tileset-uri.packet.js | 11 ++ .../packets/layers-update-layer.packet.js | 36 ++++ .../packets/room-size-update.packet.js | 12 -- .../packets/room-update-layers.packet.js | 26 +++ .../packets/room-update-size.packet.js | 24 +++ .../topdown/packets/tile-update.packet.js | 2 +- packages/topdown/room.synchronized.js | 134 ++++++++------- packages/topdown/tiles.js | 159 +++++++++++++----- packages/topdown/traits/followed.trait.js | 1 + 26 files changed, 482 insertions(+), 309 deletions(-) delete mode 100644 packages/math/matrix.spec.coffee rename packages/math/{matrix.js => matrix/index.js} (100%) delete mode 100644 packages/topdown/packets/layer-create.packet.js create mode 100644 packages/topdown/packets/layer-update-entities.packet.js create mode 100644 packages/topdown/packets/layer-update-tiles.packet.js create mode 100644 packages/topdown/packets/layer-update-tileset-uri.packet.js create mode 100644 packages/topdown/packets/layers-update-layer.packet.js delete mode 100644 packages/topdown/packets/room-size-update.packet.js create mode 100644 packages/topdown/packets/room-update-layers.packet.js create mode 100644 packages/topdown/packets/room-update-size.packet.js diff --git a/packages/entity/entity.synchronized.js b/packages/entity/entity.synchronized.js index de12f71..3d459b1 100644 --- a/packages/entity/entity.synchronized.js +++ b/packages/entity/entity.synchronized.js @@ -117,10 +117,6 @@ export class Entity extends decorate(Resource) { } acceptPacket(packet) { - for (let i = 0; i < this._traitsAcceptingPackets.length; i++) { - const instance = this._traitsAcceptingPackets[i]; - instance.acceptPacket(packet); - } } addTrait(type, json = {}) { @@ -200,6 +196,9 @@ export class Entity extends decorate(Resource) { fromJSON(json) { super.fromJSON(json); + if (json.instanceUuid) { + this.instanceUuid = json.instanceUuid; + } this.addTraits(json.traits); return this; } @@ -261,20 +260,8 @@ export class Entity extends decorate(Resource) { return mergeDiff(this._json, this.toJSON()); } - packetsForUpdate(force = false) { + packets(informed) { const packets = []; - if (force) { - const packet = new EntityCreatePacket(this.mergeDiff(), this); - packets.push(packet); - } - else { - for (let i = 0; i < this._traitsFlat.length; i++) { - const traitPackets = this._traitsFlat[i].packetsForUpdate(); - for (let j = 0; j < traitPackets.length; j++) { - packets.push(traitPackets[j]); - } - } - } return packets; } @@ -361,6 +348,7 @@ export class Entity extends decorate(Resource) { } return { ...super.toJSON(), + instanceUuid: this.numericUid, traits: json, }; } diff --git a/packages/entity/list/index.js b/packages/entity/list/index.js index d4e19c1..7d284ab 100644 --- a/packages/entity/list/index.js +++ b/packages/entity/list/index.js @@ -30,14 +30,6 @@ export class EntityList extends decorate(class {}) { } acceptPacket(packet) { - if (packet instanceof EntityCreatePacket) { - if (!this.findEntity(packet.data.uuid)) { - Entity.loadOrInstance(packet.data).then((entity) => { - entity.instanceUuid = packet.data.uuid; - this.addEntity(entity); - }); - } - } } addEntity(entity) { @@ -81,7 +73,7 @@ export class EntityList extends decorate(class {}) { fromJSON(json) { for (let i = 0; i < json.length; i++) { const entityJSON = json[i]; - if ('undefined' !== entityJSON.uri) { + if (entityJSON.uri) { Entity.read(entityJSON.uri).then((readJSON) => { this.addEntity(new Entity(readJSON, entityJSON)); }); @@ -92,28 +84,8 @@ export class EntityList extends decorate(class {}) { } } - packetsForUpdate(force = false) { + packets(informed) { const packets = []; - if (!force) { - for (let i = 0; i < this._entitiesJustAdded.length; i++) { - const entity = this._entitiesJustAdded[i]; - packets.push(new EntityCreatePacket(entity.mergeDiff(), entity)); - } - this._entitiesJustAdded = []; - } - for (let i = 0; i < this._flatEntities.length; i++) { - const entityPackets = this._flatEntities[i].packetsForUpdate(force); - for (let j = 0; j < entityPackets.length; j++) { - packets.push(entityPackets[j]); - } - } - if (!force) { - for (let i = 0; i < this._entitiesJustRemoved.length; i++) { - const entity = this._entitiesJustRemoved[i]; - packets.push(new EntityRemovePacket({}, entity)); - } - this._entitiesJustRemoved = []; - } return packets; } @@ -166,6 +138,14 @@ export class EntityList extends decorate(class {}) { } } + toJSON() { + const json = []; + for (let i = 0; i < this._flatEntities.length; i++) { + json.push(this._flatEntities[i].mergeDiff()); + } + return json; + } + visibleEntities(query) { const entities = []; const entitiesChecked = []; diff --git a/packages/entity/trait/index.js b/packages/entity/trait/index.js index 3bdaf05..441b5dd 100644 --- a/packages/entity/trait/index.js +++ b/packages/entity/trait/index.js @@ -22,6 +22,13 @@ export class Trait extends decorate(class {}) { } } + acceptPacket(packet) { + } + + static contextType() { + return {}; + } + createTraitPacketUpdates(Packet) { const packets = []; if (this.isDirty) { @@ -31,6 +38,25 @@ export class Trait extends decorate(class {}) { return packets; } + static defaultJSON() { + return { + params: this.defaultParams(), + state: this.defaultState(), + }; + } + + static defaultParams() { + return {}; + } + + static defaultState() { + return {}; + } + + static dependencies() { + return []; + } + destroy() {} hooks() { @@ -75,6 +101,11 @@ export class Trait extends decorate(class {}) { return {}; } + packets(informed) { + const packets = []; + return packets; + } + toJSON() { return { params: this.params, @@ -86,29 +117,6 @@ export class Trait extends decorate(class {}) { return value; } - static contextType() { - return {}; - } - - static defaultJSON() { - return { - params: this.defaultParams(), - state: this.defaultState(), - }; - } - - static defaultParams() { - return {}; - } - - static defaultState() { - return {}; - } - - static dependencies() { - return []; - } - } export function StateProperty(key, meta = {}) { diff --git a/packages/math/matrix.spec.coffee b/packages/math/matrix.spec.coffee deleted file mode 100644 index fc62834..0000000 --- a/packages/math/matrix.spec.coffee +++ /dev/null @@ -1,29 +0,0 @@ - -import * as Matrix from './matrix' - -describe 'Matrix', -> - - it 'can inspect size', -> - - matrix = [[0, 0], [0, 0], [0, 0], [0, 0]] - - expect(Matrix.size matrix).toBe 8 - expect(Matrix.sizeVector matrix).toEqual [2, 4] - - it 'can test equality', -> - - l = [[0, 0], [0, 0], [0, 0], [0, 0]] - r = [[0, 0], [0, 0], [0, 0], [0, 0]] - - expect(Matrix.equals l, r).toBe true - - it 'can make deep copies', -> - - matrix = [[1], [2], [3]] - matrix2 = Matrix.copy matrix - - expect(matrix).toEqual matrix2 - - matrix[0][0] = 4 - - expect(matrix).not.toEqual matrix2 diff --git a/packages/math/matrix.js b/packages/math/matrix/index.js similarity index 100% rename from packages/math/matrix.js rename to packages/math/matrix/index.js diff --git a/packages/net/packet/packet.js b/packages/net/packet/packet.js index f827c7b..5a99ae0 100644 --- a/packages/net/packet/packet.js +++ b/packages/net/packet/packet.js @@ -1,3 +1,4 @@ +import {idFromPacket} from './registry'; import schemapack from 'schemapack'; export class Packet { @@ -20,6 +21,13 @@ export class Packet { }); } + static packPacket(packet) { + const id = idFromPacket(packet.constructor); + return packet.constructor.pack({ + data: [id, packet.data], + }); + } + static get schema() { return { _id: 'uint8', @@ -32,4 +40,3 @@ export class Packet { } } - diff --git a/packages/net/s13n/client.js b/packages/net/s13n/client.js index d84ba7b..0bd1611 100644 --- a/packages/net/s13n/client.js +++ b/packages/net/s13n/client.js @@ -10,37 +10,34 @@ export class ClientSynchronizer { 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); - } + acceptPacket(packet) { + 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] = {}; } - else if ((packet instanceof SynchronizedUpdatePacket)) { - const { - synchronized: {id, type}, - } = packet.data; - this._synchronized[type][id].acceptPacket(packet); + if (this._synchronized[type][id]) { + this._synchronized[type][id].fromNetwork(json); } - else if ((packet instanceof SynchronizedDestroyPacket)) { - const { - synchronized: {id, type}, - } = packet.data; - this._synchronized[type][id].destroy(); - this._synchronized[type][id] = null; + 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) { @@ -55,4 +52,11 @@ export class ClientSynchronizer { this._synchronized[type][synchronizationId] = synchronized; } + synchronized(type, id) { + if (!this._synchronized[type]) { + return; + } + return this._synchronized[type][id]; + } + } diff --git a/packages/net/s13n/server.js b/packages/net/s13n/server.js index f5b892d..6b572b7 100644 --- a/packages/net/s13n/server.js +++ b/packages/net/s13n/server.js @@ -60,7 +60,7 @@ export class ServerSynchronizer { id, type, }, - spec: synchronized.toJSON(), + spec: synchronized.toNetwork(informed), }); payload.push(packet); } diff --git a/packages/net/s13n/synchronized.js b/packages/net/s13n/synchronized.js index f6c0ae3..1e3e258 100644 --- a/packages/net/s13n/synchronized.js +++ b/packages/net/s13n/synchronized.js @@ -13,6 +13,10 @@ export function SynchronizedMixin(Superclass) { destroy() {} + fromNetwork(json) { + this.fromJSON(json); + } + packets(informed) {} packetsAreIdempotent() { @@ -38,6 +42,10 @@ export function SynchronizedMixin(Superclass) { return 0; } + toNetwork(informed) { + return this.toJSON(); + } + } } diff --git a/packages/sound/traits/audible.trait.js b/packages/sound/traits/audible.trait.js index 9a8e255..588e5cd 100644 --- a/packages/sound/traits/audible.trait.js +++ b/packages/sound/traits/audible.trait.js @@ -15,7 +15,9 @@ export class Audible extends Trait { } hydrate() { - this.loadSounds(); + if (AVOCADO_CLIENT) { + this.loadSounds(); + } } constructor(entity, params, state) { diff --git a/packages/timing/traits/animated.trait.js b/packages/timing/traits/animated.trait.js index acf9b76..ed678cd 100644 --- a/packages/timing/traits/animated.trait.js +++ b/packages/timing/traits/animated.trait.js @@ -80,6 +80,10 @@ export class Animated extends decorate(Trait) { this.entity.container.removeChild(animationView); } + hydrate() { + return this.promiseAnimations(); + } + jitterFor(key) { if (!(key in this._animations) || !this._animations[key].jitter) { return 0; @@ -91,17 +95,7 @@ export class Animated extends decorate(Trait) { if (this.animationsPromise) { return; } - const animationPromises = []; - // Load all animations. - for (const key in this._animations) { - const {uri} = this._animations[key]; - const promise = Animation.load(uri).then((animation) => { - // Zip with key to make populating animations and views trivial. - return {animation, key}; - }); - animationPromises.push(promise); - } - this.animationsPromise = Promise.all(animationPromises); + this.promiseAnimations(); // Store keyed animations. this.animations = {}; this.animationsPromise.then((animations) => { @@ -154,6 +148,23 @@ export class Animated extends decorate(Trait) { return this.createTraitPacketUpdates(TraitAnimatedPacket); } + promiseAnimations() { + if (!this.animationsPromise) { + const animationPromises = []; + // Load all animations. + for (const key in this._animations) { + const {uri} = this._animations[key]; + const promise = Animation.load(uri).then((animation) => { + // Zip with key to make populating animations and views trivial. + return {animation, key}; + }); + animationPromises.push(promise); + } + this.animationsPromise = Promise.all(animationPromises); + } + return this.animationsPromise; + } + setSpriteScale() { if (!this.animationViews) { return; diff --git a/packages/topdown/layer-view.js b/packages/topdown/layer-view.js index 0a30edd..8bb0c2d 100644 --- a/packages/topdown/layer-view.js +++ b/packages/topdown/layer-view.js @@ -18,8 +18,11 @@ export class LayerView extends Container { this.tileGeometryViews = []; // Handle entity add/remove. layer.on('tileDataChanged', this.onLayerTileDataChanged, this); + this.onLayerTileDataChanged(); layer.on('tileGeometryChanged', this.onLayerTileGeometryChanged, this); + this.onLayerTileGeometryChanged(); layer.on('tilesetChanged', this.onLayerTilesetChanged, this); + this.onLayerTilesetChanged(layer.tileset, undefined); this.addChild(this.layerContainer); this.addChild(this.tileGeometryContainer); this.addChild(this.entityListView); diff --git a/packages/topdown/layer.js b/packages/topdown/layer.js index f8022da..03400ea 100644 --- a/packages/topdown/layer.js +++ b/packages/topdown/layer.js @@ -3,14 +3,17 @@ import {Entity, EntityCreatePacket, EntityList} from '@avocado/entity'; import {Vector} from '@avocado/math'; import {ShapeList} from '@avocado/physics'; +import { + LayerUpdateTilesetUriPacket, +} from './packets/layer-update-tileset-uri.packet'; + import {Tiles} from './tiles'; import {Tileset} from './tileset'; -import {LayerCreatePacket} from './packets/layer-create.packet'; -import {TileUpdatePacket} from './packets/tile-update.packet'; const decorate = compose( EventEmitter, Property('tilesetUri', { + default: '', track: true, }), Property('tileset', { @@ -30,6 +33,7 @@ export class Layer extends decorate(class {}) { this.tileEntities = {}; this.tileGeometry = []; this.tiles = new Tiles(); + this._tilesetUriChanged = false; // Listeners. this.entityList.on('entityAdded', this.onEntityAddedToLayer, this); this.entityList.on('entityRemoved', this.onEntityRemovedFromLayer, this); @@ -43,11 +47,8 @@ export class Layer extends decorate(class {}) { } acceptPacket(packet) { - if (packet instanceof TileUpdatePacket) { - this.tiles.acceptPacket(packet); - } - if (packet instanceof EntityCreatePacket) { - this.entityList.acceptPacket(packet); + if (packet instanceof LayerUpdateTilesetUriPacket) { + this.tilesetUri = packet.data; } } @@ -66,6 +67,7 @@ export class Layer extends decorate(class {}) { } addTileGeometry() { + return false; const tileset = this.tileset; if (!tileset) { return false; @@ -96,6 +98,10 @@ export class Layer extends decorate(class {}) { return Array.from(this.entityList); } + cleanPackets() { + this._tilesetUriChanged = false; + } + destroy() { this.entityList.destroy(); this.entityList.off('entityAdded', this.onEntityAddedToLayer); @@ -168,6 +174,7 @@ export class Layer extends decorate(class {}) { } onTilesetUriChanged() { + this._tilesetUriChanged = true; if (!this.tilesetUri) { return; } @@ -190,22 +197,10 @@ export class Layer extends decorate(class {}) { } } - packetsForUpdate(force = false) { + packets(informed) { const packets = []; - // Create layer during a force. - if (force) { - packets.push(new LayerCreatePacket(this.toJSON())); - } - const entityListPackets = this.entityList.packetsForUpdate(force); - for (let i = 0; i < entityListPackets.length; i++) { - packets.push(entityListPackets[i]); - } - if (!force) { - const tilesPackets = this.tiles.packetsForUpdate(); - for (let i = 0; i < tilesPackets.length; ++i) { - tilesPackets[i].data.layer = this.index; - packets.push(tilesPackets[i]); - } + if (this._tilesetUriChanged) { + packets.push(new LayerUpdateTilesetUriPacket(this.tilesetUri)); } return packets; } @@ -251,11 +246,25 @@ export class Layer extends decorate(class {}) { toJSON() { return { + entities: this.entityList.toJSON(), tilesetUri: this.tilesetUri, tiles: this.tiles.toJSON(), }; } + toNetwork(informed) { + const {areaToInform} = informed; + const visibleEntities = this.visibleEntities(areaToInform); + const visibleEntitiesDiffs = visibleEntities.map((visibleEntity) => { + return visibleEntity.mergeDiff(); + }); + return { + entities: visibleEntitiesDiffs, + tilesetUri: this.tilesetUri, + tiles: this.tiles.toNetwork(informed), + }; + } + visibleEntities(query) { return this.entityList.visibleEntities(query); } diff --git a/packages/topdown/layers.js b/packages/topdown/layers.js index 31b1644..c5c8a3c 100644 --- a/packages/topdown/layers.js +++ b/packages/topdown/layers.js @@ -4,8 +4,7 @@ import {arrayUnique, compose, EventEmitter, flatten} from '@avocado/core'; import {EntityCreatePacket} from '@avocado/entity'; import {Layer} from './layer'; -import {LayerCreatePacket} from './packets/layer-create.packet'; -import {TileUpdatePacket} from './packets/tile-update.packet'; +import {LayersUpdateLayerPacket} from './packets/layers-update-layer.packet'; const decorate = compose( EventEmitter, @@ -23,19 +22,21 @@ export class Layers extends decorate(class {}) { *[Symbol.iterator]() { for (let i = 0; i < this.layers.length; i++) { - yield this.layers[i]; + yield { + index: i, + layer: this.layers[i], + }; } } acceptPacket(packet) { - if (packet instanceof LayerCreatePacket) { - this.addLayer(new Layer(packet.data)); - } - if (packet instanceof TileUpdatePacket) { - this.layers[packet.data.layer].acceptPacket(packet); - } - if (packet instanceof EntityCreatePacket) { - this.layers[packet.data.layer].acceptPacket(packet); + if (packet instanceof LayersUpdateLayerPacket) { + for (let i = 0; i < packet.data.length; i++) { + const {layerIndex, layerPackets} = packet.data[i]; + for (let j = 0; j < layerPackets.length; j++) { + this.layers[layerIndex].acceptPacket(layerPackets[j]); + } + } } } @@ -66,6 +67,12 @@ export class Layers extends decorate(class {}) { return allEntities; } + cleanPackets() { + for (let i = 0; i < this.layers.length; i++) { + this.layers[i].cleanPackets(); + } + } + destroy() { for (let i = 0; i < this.layers.length; i++) { const layer = this.layers[i]; @@ -102,13 +109,21 @@ export class Layers extends decorate(class {}) { this.emit('entityRemoved', entity); } - packetsForUpdate(force = false) { + packets(informed) { const packets = []; + const updates = []; for (let i = 0; i < this.layers.length; i++) { - const layerPacketsForUpdate = this.layers[i].packetsForUpdate(force); - for (let j = 0; j < layerPacketsForUpdate.length; j++) { - packets.push(layerPacketsForUpdate[j]); + const layerPackets = this.layers[i].packets(informed); + if (0 === layerPackets.length) { + continue; } + updates.push({ + layerIndex: i, + layerPackets, + }); + } + if (updates.length > 0) { + packets.push(new LayersUpdateLayerPacket(updates)); } return packets; } @@ -139,6 +154,14 @@ export class Layers extends decorate(class {}) { } } + toJSON() { + return this.layers.map((layer) => layer.toJSON()); + } + + toNetwork(informed) { + return this.layers.map((layer) => layer.toNetwork(informed)); + } + visibleEntities(query) { const entities = []; for (let i = 0; i < this.layers.length; i++) { diff --git a/packages/topdown/packets/layer-create.packet.js b/packages/topdown/packets/layer-create.packet.js deleted file mode 100644 index c1cc8ea..0000000 --- a/packages/topdown/packets/layer-create.packet.js +++ /dev/null @@ -1,18 +0,0 @@ -import {Packet} from '@avocado/net'; - -export class LayerCreatePacket extends Packet { - - static get schema() { - return { - ...super.schema, - data: { - tilesetUri: 'string', - tiles: { - size: ['uint16'], - data: ['uint16'], - }, - }, - }; - } - -} diff --git a/packages/topdown/packets/layer-update-entities.packet.js b/packages/topdown/packets/layer-update-entities.packet.js new file mode 100644 index 0000000..e69de29 diff --git a/packages/topdown/packets/layer-update-tiles.packet.js b/packages/topdown/packets/layer-update-tiles.packet.js new file mode 100644 index 0000000..e69de29 diff --git a/packages/topdown/packets/layer-update-tileset-uri.packet.js b/packages/topdown/packets/layer-update-tileset-uri.packet.js new file mode 100644 index 0000000..0fea49b --- /dev/null +++ b/packages/topdown/packets/layer-update-tileset-uri.packet.js @@ -0,0 +1,11 @@ +import {Packet} from '@avocado/net'; + +export class LayerUpdateTilesetUriPacket extends Packet { + + static get schema() { + return { + data: 'string', + }; + } + +} diff --git a/packages/topdown/packets/layers-update-layer.packet.js b/packages/topdown/packets/layers-update-layer.packet.js new file mode 100644 index 0000000..23b2ecf --- /dev/null +++ b/packages/topdown/packets/layers-update-layer.packet.js @@ -0,0 +1,36 @@ +import {BundlePacket, Packet} from '@avocado/net'; + +export class LayersUpdateLayerPacket extends Packet { + + static pack(packet) { + const data = packet.data[1]; + for (let i = 0; i < data.length; i++) { + data[i].layerPackets = BundlePacket.packPacket( + new BundlePacket(data[i].layerPackets) + ); + } + return super.pack(packet); + } + + static get schema() { + return { + data: [ + { + layerIndex: 'uint8', + layerPackets: 'buffer', + }, + ], + }; + } + + static unpack(packet) { + const data = super.unpack(packet); + for (let i = 0; i < data.length; i++) { + data[i].layerPackets = BundlePacket.unpack( + data[i].layerPackets + ); + } + return data; + } + +} diff --git a/packages/topdown/packets/room-size-update.packet.js b/packages/topdown/packets/room-size-update.packet.js deleted file mode 100644 index 979bf88..0000000 --- a/packages/topdown/packets/room-size-update.packet.js +++ /dev/null @@ -1,12 +0,0 @@ -import {Packet} from '@avocado/net'; - -export class RoomSizeUpdatePacket extends Packet { - - static get schema() { - return { - ...super.schema, - data: 'uint32', - }; - } - -} diff --git a/packages/topdown/packets/room-update-layers.packet.js b/packages/topdown/packets/room-update-layers.packet.js new file mode 100644 index 0000000..996f3e6 --- /dev/null +++ b/packages/topdown/packets/room-update-layers.packet.js @@ -0,0 +1,26 @@ +import {BundlePacket} from '@avocado/net'; +import {SynchronizedUpdatePacket} from '@avocado/net'; + +export class RoomUpdateLayersPacket extends SynchronizedUpdatePacket { + + static pack(packet) { + const data = packet.data[1]; + data.layersPackets = BundlePacket.packPacket( + new BundlePacket(data.layersPackets) + ); + return super.pack(packet); + } + + static get synchronizationSchema() { + return { + layersPackets: 'buffer', + }; + } + + static unpack(packet) { + const data = super.unpack(packet); + data.layersPackets = BundlePacket.unpack(data.layersPackets); + return data; + } + +} diff --git a/packages/topdown/packets/room-update-size.packet.js b/packages/topdown/packets/room-update-size.packet.js new file mode 100644 index 0000000..abe1316 --- /dev/null +++ b/packages/topdown/packets/room-update-size.packet.js @@ -0,0 +1,24 @@ +import {Vector} from '@avocado/math'; +import {SynchronizedUpdatePacket} from '@avocado/net'; + +export class RoomUpdateSizePacket extends SynchronizedUpdatePacket { + + static pack(packet) { + const data = packet.data[1]; + data.size = Vector.packToUint32(data.size); + return super.pack(packet); + } + + static get synchronizationSchema() { + return { + size: 'uint32', + }; + } + + static unpack(packet) { + const data = super.unpack(packet); + data.size = Vector.unpackFromUint32(data.size); + return data; + } + +} diff --git a/packages/topdown/packets/tile-update.packet.js b/packages/topdown/packets/tile-update.packet.js index b85df9c..34ba304 100644 --- a/packages/topdown/packets/tile-update.packet.js +++ b/packages/topdown/packets/tile-update.packet.js @@ -6,7 +6,7 @@ export class TileUpdatePacket extends Packet { return { ...super.schema, data: { - layer: 'uint8', + ...super.schema.data, position: 'uint32', tile: 'uint16', }, diff --git a/packages/topdown/room.synchronized.js b/packages/topdown/room.synchronized.js index fde1eb6..cef02da 100644 --- a/packages/topdown/room.synchronized.js +++ b/packages/topdown/room.synchronized.js @@ -3,19 +3,20 @@ import * as I from 'immutable'; import {compose, EventEmitter, Property} from '@avocado/core'; import {EntityCreatePacket, EntityPacket, EntityRemovePacket} from '@avocado/entity'; import {Vector} from '@avocado/math'; +import {SynchronizedMixin} from '@avocado/net'; import {RectangleShape} from '@avocado/physics'; import {Resource} from '@avocado/resource'; import {Layers} from './layers'; -import {LayerCreatePacket} from './packets/layer-create.packet'; -import {RoomSizeUpdatePacket} from './packets/room-size-update.packet'; -import {TileUpdatePacket} from './packets/tile-update.packet'; +import {RoomUpdateSizePacket} from './packets/room-update-size.packet'; +import {RoomUpdateLayersPacket} from './packets/room-update-layers.packet'; const ROOM_BOUND_SIZE = 64; const HALF_ROOM_BOUND_SIZE = ROOM_BOUND_SIZE / 2; const decorate = compose( EventEmitter, + SynchronizedMixin, Property('world', { track: true, }), @@ -25,18 +26,26 @@ const decorate = compose( }), ); +let synchronizationId = 1; + export class Room extends decorate(Resource) { constructor(json) { super(); this.bounds = []; + this._sizeChanged = false; + this._synchronizationId = synchronizationId++; const layerJSON = 'undefined' !== typeof json ? json.layers : undefined; this.layers = new Layers(layerJSON); - this.queuedEntityPackets = {}; // Listeners. this.layers.on('entityAdded', this.onEntityAddedToRoom, this); this.layers.on('entityRemoved', this.onEntityRemovedFromRoom, this); this.layers.on('layerAdded', this.onLayerAdded, this); + const allEntities = this.allEntities(); + for (let i = 0; i < allEntities.length; i++) { + this.onEntityAddedToRoom(allEntities[i]); + + } this.on('sizeChanged', this.onSizeChanged, this); this.on('worldChanged', this.onWorldChanged, this); if ('undefined' !== typeof json && json.size) { @@ -45,39 +54,16 @@ export class Room extends decorate(Resource) { } acceptPacket(packet) { - if (packet instanceof EntityCreatePacket) { - this.queuedEntityPackets[packet.data.uuid] = []; - this.layers.acceptPacket(packet); + // Size update. + if (packet instanceof RoomUpdateSizePacket) { + this.size = packet.data.size; } - if ( - packet instanceof LayerCreatePacket - || packet instanceof TileUpdatePacket - ) { - this.layers.acceptPacket(packet); - } - if (packet instanceof EntityRemovePacket) { - const entity = this.findEntity(packet.data.uuid); - if (entity) { - entity.destroyGently(); + // Layer updates. + if (packet instanceof RoomUpdateLayersPacket) { + const {layersPackets} = packet.data; + for (let i = 0; i < layersPackets.length; ++i) { + this.layers.acceptPacket(layersPackets[i]); } - return; - } - if (packet instanceof EntityPacket) { - const entity = this.findEntity(packet.data.uuid); - if (entity) { - entity.acceptPacket(packet); - } - else { - const queuedPackets = this.queuedEntityPackets[packet.data.uuid]; - if (queuedPackets) { - queuedPackets.push(packet); - } - } - } - if (packet instanceof RoomSizeUpdatePacket) { - const x = packet.data & 0xFFFF; - const y = packet.data >> 16; - this.size = [x, y]; } } @@ -89,7 +75,14 @@ export class Room extends decorate(Resource) { return this.layers.allEntities(); } + cleanPackets() { + super.cleanPackets(); + this._sizeChanged = false; + this.layers.cleanPackets(); + } + destroy() { + super.destroy(); this.layers.destroy(); this.layers.off('entityAdded', this.onEntityAddedToRoom); this.layers.off('entityRemoved', this.onEntityRemovedFromRoom); @@ -116,15 +109,6 @@ export class Room extends decorate(Resource) { onEntityAddedToRoom(entity) { entity.setIntoRoom(this); - if (AVOCADO_CLIENT) { - const queuedPackets = this.queuedEntityPackets[entity.instanceUuid]; - if (queuedPackets) { - for (let i = 0; i < queuedPackets.length; i++) { - entity.acceptPacket(queuedPackets[i]); - } - } - this.queuedEntityPackets[entity.instanceUuid] = undefined; - } this.emit('entityAdded', entity) } @@ -138,12 +122,13 @@ export class Room extends decorate(Resource) { } onSizeChanged() { + this._sizeChanged = true; this.updateBounds(); } onWorldChanged() { const world = this.world; - for (const layer of this.layers) { + for (const {index, layer} of this.layers) { layer.world = world; for (const entity of layer.entityList) { if (entity.is('physical')) { @@ -155,24 +140,56 @@ export class Room extends decorate(Resource) { this.updateBounds(); } - packetsForUpdate(force = false) { - const packets = []; - if (force) { - const packed = (this.height << 16) | (this.width); - packets.push(new RoomSizeUpdatePacket(packed)); + packets(informed) { + const payload = []; + if (this._sizeChanged) { + payload.push(new RoomUpdateSizePacket({ + size: this.size, + })); } - // Layers packets. - const layersPacketsForUpdate = this.layers.packetsForUpdate(force); - for (let i = 0; i < layersPacketsForUpdate.length; i++) { - packets.push(layersPacketsForUpdate[i]); + // Layer updates. + const layersPackets = this.layers.packets(informed); + if (layersPackets.length > 0) { + payload.push(new RoomUpdateLayersPacket({ + layersPackets, + })); } - return packets; + return payload; + } + + packetsAreIdempotent() { + return false; } removeEntityFromLayer(entity, layerIndex) { this.layers.removeEntityFromLayer(entity, layerIndex); } + synchronizationId() { + return this._synchronizationId; + } + + tick(elapsed) { + this.layers.tick(elapsed); + if (this.world) { + this.world.tick(elapsed); + } + } + + toNetwork(informed) { + return { + layers: this.layers.toNetwork(informed), + size: this.size, + }; + } + + toJSON() { + return { + layer: this.layers.toJSON(), + size: this.size, + }; + } + updateBounds() { const world = this.world; if (!world) { @@ -212,13 +229,6 @@ export class Room extends decorate(Resource) { }); } - tick(elapsed) { - this.layers.tick(elapsed); - if (this.world) { - this.world.tick(elapsed); - } - } - visibleEntities(query) { return this.layers.visibleEntities(query); } diff --git a/packages/topdown/tiles.js b/packages/topdown/tiles.js index d586001..d89948e 100644 --- a/packages/topdown/tiles.js +++ b/packages/topdown/tiles.js @@ -5,6 +5,8 @@ import {Rectangle, Vector} from '@avocado/math'; import {TileUpdatePacket} from './packets/tile-update.packet'; +const CHUNK_AXIS = 10; + const decorate = compose( EventEmitter, Vector.Mixin('size', 'width', 'height', { @@ -16,57 +18,106 @@ export class Tiles extends decorate(class {}) { constructor(json) { super(); + // this.chunks = []; this.data = []; - this.updatePackets = []; if ('undefined' !== typeof json) { this.fromJSON(json); } } acceptPacket(packet) { - if (packet instanceof TileUpdatePacket) { - const unpackedPosition = [ - packet.data.position & 0xFFFF, - packet.data.position >> 16, - ]; - this.setTileAt(unpackedPosition, packet.data.tile); - } + // if (packet instanceof TileUpdatePacket) { + // const unpackedPosition = [ + // packet.data.position & 0xFFFF, + // packet.data.position >> 16, + // ]; + // this.setTileAt(unpackedPosition, packet.data.tile); + // } } - forEachTile(fn) { - let [x, y] = [0, 0]; - let [width, height] = this.size; - let i = 0; - for (let k = 0; k < height; ++k) { - for (let j = 0; j < width; ++j) { - fn(this.data[i], x, y, i); - ++i; - ++x; - } - x = 0; - ++y; - } - } + // chunkIndexAtPosition(position) { + // const chunkCount = Vector.ceil(Vector.scale(this.size, 1 / CHUNK_AXIS)); + // const chunkPosition = Vector.floor(Vector.scale(position, 1 / CHUNK_AXIS)); + // const chunkIndex = chunkPosition[1] * chunkCount[0] + chunkPosition[0]; + // return chunkIndex; + // } + + // static createChunkFromData(position, size, data) { + // const chunk = Array(CHUNK_AXIS * CHUNK_AXIS).fill(0); + // let chunkIndex = 0; + // const chunkPosition = Vector.scale(position, CHUNK_AXIS); + // let dataIndex = size[0] * chunkPosition[1] + chunkPosition[0]; + // for (let y = 0; y < CHUNK_AXIS; ++y) { + // for (let x = 0; x < CHUNK_AXIS; ++x) { + // if (chunkPosition[0] < size[0] && chunkPosition[1] < size[1]) { + // chunk[chunkIndex] = data[dataIndex]; + // } + // chunkIndex += 1; + // chunkPosition[0] += 1; + // dataIndex += 1; + // } + // chunkPosition[0] -= CHUNK_AXIS; + // chunkPosition[1] += 1; + // dataIndex -= CHUNK_AXIS; + // dataIndex += size[0]; + // } + // return chunk; + // } + + // forEachTile(fn) { + // let [x, y] = [0, 0]; + // let [width, height] = this.size; + // let i = 0; + // for (let k = 0; k < height; ++k) { + // for (let j = 0; j < width; ++j) { + // fn(this.data[i], x, y, i); + // ++i; + // ++x; + // } + // x = 0; + // ++y; + // } + // } fromJSON(json) { if (json.size) { - this.size = json.size; + super.size = json.size; } - if (json.data) { + // this.chunks = []; + // const chunkCount = Vector.ceil(Vector.scale(this.size, 1 / CHUNK_AXIS)); + // if (json.data && json.data.length > 0) { + // for (let y = 0; y < chunkCount[1]; ++y) { + // for (let x = 0; x < chunkCount[0]; ++x) { + // this.chunks.push(Tiles.createChunkFromData( + // [x, y], + // this.size, + // json.data + // )); + // } + // } + // } + // if (json.chunks && json.chunks.length > 0) { + + // } + if (json.data && json.data.length > 0) { this.data = json.data.slice(0); } + // Instantiate blank tiles if we got size but no data. + else { + this.data = Array(Vector.area(this.size)).fill(0); + } return this; } - indexAt(position) { - return this.width * position[1] + position[0]; - } + // indexAt(position) { + // return this.width * position[1] + position[0]; + // } - packetsForUpdate() { - const packetsForUpdate = this.updatePackets; - this.updatePackets = []; - return packetsForUpdate; - } + // packetsForUpdate() { + // const packetsForUpdate = this.updatePackets; + // this.updatePackets = []; + // return packetsForUpdate; + // } get rectangle() { return Rectangle.compose([0, 0], this.size); @@ -83,13 +134,32 @@ export class Tiles extends decorate(class {}) { } this.data[index] = tile; const packedPosition = position[1] << 16 | position[0]; - this.updatePackets.push(new TileUpdatePacket({ - position: packedPosition, - tile, - })); + // this.updatePackets.push(new TileUpdatePacket({ + // position: packedPosition, + // tile, + // })); this.emit('dataChanged'); } + get size() { + return super.size; + } + + set size(size) { + super.size = size; + // const newChunks = []; + // const chunkCount = Vector.ceil(Vector.div(size, [CHUNK_AXIS, CHUNK_AXIS])); + // const chunkLinearCount = Vector.area(chunkCount); + // for (let i = 0; i < chunkLinearCount; ++i) { + // newChunks. + // } + // for (let y = 0; y < chunkCount[1]; y++) { + // for (let x = 0; x < chunkCount[0]; x++) { + // newChunks[y][x] + // } + // } + } + slice(rectangle) { const tilesRectangle = this.rectangle; // Get intersection. @@ -119,13 +189,24 @@ export class Tiles extends decorate(class {}) { } tileAt(position) { - return this.data[this.indexAt(position)]; + const index = this.indexAt(position); + if (index < 0 || index >= this.data.length) { + return; + } + return this.data[index]; } toJSON() { return { - size: Vector.copy(this.size), - data: this.data.slice(0), + size: this.size, + data: this.data, + }; + } + + toNetwork(informed) { + return { + size: this.size, + data: this.data, }; } diff --git a/packages/topdown/traits/followed.trait.js b/packages/topdown/traits/followed.trait.js index 0a2f281..ff92e83 100644 --- a/packages/topdown/traits/followed.trait.js +++ b/packages/topdown/traits/followed.trait.js @@ -67,6 +67,7 @@ export class Followed extends Trait { }, traitAdded: (type) => { + this.onRoomSizeChanged(); if (-1 === [ 'positioned', 'followed',