flow: massive sync update
This commit is contained in:
parent
dfd2dc2a41
commit
ddd9b1c8e8
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 = [];
|
||||
|
|
|
@ -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 = {}) {
|
||||
|
|
|
@ -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
|
|
@ -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 {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ export class ServerSynchronizer {
|
|||
id,
|
||||
type,
|
||||
},
|
||||
spec: synchronized.toJSON(),
|
||||
spec: synchronized.toNetwork(informed),
|
||||
});
|
||||
payload.push(packet);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,7 +15,9 @@ export class Audible extends Trait {
|
|||
}
|
||||
|
||||
hydrate() {
|
||||
this.loadSounds();
|
||||
if (AVOCADO_CLIENT) {
|
||||
this.loadSounds();
|
||||
}
|
||||
}
|
||||
|
||||
constructor(entity, params, state) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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'],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
}
|
11
packages/topdown/packets/layer-update-tileset-uri.packet.js
Normal file
11
packages/topdown/packets/layer-update-tileset-uri.packet.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import {Packet} from '@avocado/net';
|
||||
|
||||
export class LayerUpdateTilesetUriPacket extends Packet {
|
||||
|
||||
static get schema() {
|
||||
return {
|
||||
data: 'string',
|
||||
};
|
||||
}
|
||||
|
||||
}
|
36
packages/topdown/packets/layers-update-layer.packet.js
Normal file
36
packages/topdown/packets/layers-update-layer.packet.js
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
import {Packet} from '@avocado/net';
|
||||
|
||||
export class RoomSizeUpdatePacket extends Packet {
|
||||
|
||||
static get schema() {
|
||||
return {
|
||||
...super.schema,
|
||||
data: 'uint32',
|
||||
};
|
||||
}
|
||||
|
||||
}
|
26
packages/topdown/packets/room-update-layers.packet.js
Normal file
26
packages/topdown/packets/room-update-layers.packet.js
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
24
packages/topdown/packets/room-update-size.packet.js
Normal file
24
packages/topdown/packets/room-update-size.packet.js
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,7 @@ export class TileUpdatePacket extends Packet {
|
|||
return {
|
||||
...super.schema,
|
||||
data: {
|
||||
layer: 'uint8',
|
||||
...super.schema.data,
|
||||
position: 'uint32',
|
||||
tile: 'uint16',
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ export class Followed extends Trait {
|
|||
},
|
||||
|
||||
traitAdded: (type) => {
|
||||
this.onRoomSizeChanged();
|
||||
if (-1 === [
|
||||
'positioned',
|
||||
'followed',
|
||||
|
|
Loading…
Reference in New Issue
Block a user