2019-03-20 18:35:59 -05:00
|
|
|
import * as I from 'immutable';
|
2019-04-07 14:59:55 -05:00
|
|
|
import immutablediff from 'immutablediff';
|
2019-03-28 03:39:57 -05:00
|
|
|
import isPlainObject from 'is-plain-object';
|
2019-03-20 18:35:59 -05:00
|
|
|
|
2019-03-20 18:58:19 -05:00
|
|
|
import {compose} from '@avocado/core';
|
2019-04-05 11:59:14 -05:00
|
|
|
import {Trait} from '@avocado/entity';
|
2019-04-07 20:04:34 -05:00
|
|
|
import {Packer, Synchronizer} from '@avocado/state';
|
2019-03-20 18:35:59 -05:00
|
|
|
|
2019-04-11 15:27:39 -05:00
|
|
|
import {KeysPacket, StatePacket} from '../common/packet';
|
|
|
|
|
2019-03-20 18:58:19 -05:00
|
|
|
const decorate = compose(
|
|
|
|
);
|
|
|
|
|
2019-04-05 11:59:14 -05:00
|
|
|
export class Informed extends decorate(Trait) {
|
2019-03-20 18:35:59 -05:00
|
|
|
|
|
|
|
initialize() {
|
2019-04-05 22:40:33 -05:00
|
|
|
this._packer = new Packer();
|
2019-04-11 23:50:44 -05:00
|
|
|
this._rememberedEntities = {};
|
2019-03-20 23:01:48 -05:00
|
|
|
this._socket = undefined;
|
2019-04-07 14:59:55 -05:00
|
|
|
this._state = I.Map();
|
2019-03-20 18:35:59 -05:00
|
|
|
}
|
|
|
|
|
2019-03-20 23:22:54 -05:00
|
|
|
destroy() {
|
2019-03-21 01:32:59 -05:00
|
|
|
if (this._socket) {
|
|
|
|
delete this._socket.entity;
|
|
|
|
delete this._socket;
|
|
|
|
}
|
2019-03-20 23:22:54 -05:00
|
|
|
}
|
|
|
|
|
2019-04-07 14:59:55 -05:00
|
|
|
reduceState(state) {
|
|
|
|
// Set client's self entity.
|
|
|
|
state = state.set('selfEntity', this.entity.instanceUuid);
|
|
|
|
// Reduce entity list to visible.
|
|
|
|
const room = this.entity.room;
|
|
|
|
const camera = this.entity.camera;
|
|
|
|
for (const {index, layer} of room.layers) {
|
|
|
|
const visibleEntities = layer.visibleEntities(
|
|
|
|
camera.rectangle
|
|
|
|
);
|
|
|
|
let reducedEntityList = I.Map();
|
|
|
|
for (const entity of visibleEntities) {
|
|
|
|
reducedEntityList = reducedEntityList.set(
|
|
|
|
entity.instanceUuid,
|
|
|
|
entity.state,
|
2019-04-05 12:47:49 -05:00
|
|
|
);
|
2019-03-28 02:10:46 -05:00
|
|
|
}
|
2019-04-07 14:59:55 -05:00
|
|
|
const entityListPath = ['room', 'layers', index, 'entityList'];
|
|
|
|
state = state.setIn(entityListPath, reducedEntityList);
|
2019-03-28 02:10:46 -05:00
|
|
|
}
|
2019-04-07 14:59:55 -05:00
|
|
|
return state;
|
2019-03-28 02:10:46 -05:00
|
|
|
}
|
|
|
|
|
2019-03-20 23:01:48 -05:00
|
|
|
get socket() {
|
|
|
|
return this._socket;
|
|
|
|
}
|
|
|
|
|
|
|
|
set socket(socket) {
|
2019-03-21 00:29:44 -05:00
|
|
|
socket.entity = this.entity;
|
2019-03-20 23:01:48 -05:00
|
|
|
this._socket = socket;
|
|
|
|
}
|
|
|
|
|
2019-03-20 18:35:59 -05:00
|
|
|
methods() {
|
|
|
|
return {
|
|
|
|
|
2019-04-07 14:59:55 -05:00
|
|
|
inform: (state) => {
|
|
|
|
// Reduce state.
|
|
|
|
const reducedState = this.reduceState(state);
|
|
|
|
// Take a pure JS diff.
|
|
|
|
const steps = immutablediff(this._state, reducedState).toJS();
|
2019-04-11 23:50:44 -05:00
|
|
|
// Rewrite entity removals.
|
|
|
|
const entityRemovals = steps.filter((step) => {
|
|
|
|
return 'remove' === step.op && step.path.match(
|
|
|
|
/\/room\/layers\/.*\/entityList\/[a-z0-9-]+/
|
|
|
|
);
|
|
|
|
});
|
|
|
|
for (const entityRemoval of entityRemovals) {
|
|
|
|
// Remember the entity.
|
|
|
|
const parts = entityRemoval.path.split('/');
|
|
|
|
parts.shift();
|
|
|
|
const uuid = parts[4];
|
|
|
|
this._rememberedEntities[uuid] = state.getIn(parts);
|
|
|
|
// Swap out the removal op for replaces to hide the entity.
|
|
|
|
const index = steps.indexOf(entityRemoval);
|
|
|
|
steps.splice(index, 1);
|
|
|
|
steps.push({
|
|
|
|
op: 'replace',
|
|
|
|
path: entityRemoval.path + '/existent/state/isTicking',
|
|
|
|
value: false,
|
|
|
|
});
|
|
|
|
steps.push({
|
|
|
|
op: 'replace',
|
|
|
|
path: entityRemoval.path + '/physical/state/addedToPhysics',
|
|
|
|
value: false,
|
|
|
|
});
|
|
|
|
steps.push({
|
|
|
|
op: 'replace',
|
|
|
|
path: entityRemoval.path + '/graphical/state/isVisible',
|
|
|
|
value: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
// Rewrite entity adds.
|
|
|
|
const entityAdds = steps.filter((step) => {
|
|
|
|
return 'add' === step.op && step.path.match(
|
|
|
|
/\/room\/layers\/.*\/entityList\/[a-z0-9-]+/
|
|
|
|
);
|
|
|
|
});
|
|
|
|
for (const entityAdd of entityAdds) {
|
|
|
|
// Remember the entity.
|
|
|
|
const parts = entityAdd.path.split('/');
|
|
|
|
parts.shift();
|
|
|
|
const uuid = parts[4];
|
|
|
|
if (!(uuid in this._rememberedEntities)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const currentState = state.getIn(parts);
|
|
|
|
const addSteps = immutablediff(
|
|
|
|
this._rememberedEntities[uuid],
|
|
|
|
currentState,
|
|
|
|
).toJS();
|
|
|
|
const fullAddSteps = addSteps.map((addStep) => {
|
|
|
|
return {
|
|
|
|
op: addStep.op,
|
|
|
|
path: entityAdd.path + addStep.path,
|
|
|
|
value: addStep.value,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
// Swap out the add op for replaces to show the entity.
|
|
|
|
const index = steps.indexOf(entityAdd);
|
|
|
|
steps.splice(index, 1);
|
|
|
|
steps.push(...fullAddSteps);
|
|
|
|
steps.push({
|
|
|
|
op: 'replace',
|
|
|
|
path: entityAdd.path + '/existent/state/isTicking',
|
|
|
|
value: true,
|
|
|
|
});
|
|
|
|
steps.push({
|
|
|
|
op: 'replace',
|
|
|
|
path: entityAdd.path + '/physical/state/addedToPhysics',
|
|
|
|
value: true,
|
|
|
|
});
|
|
|
|
steps.push({
|
|
|
|
op: 'replace',
|
|
|
|
path: entityAdd.path + '/graphical/state/isVisible',
|
|
|
|
value: true,
|
|
|
|
});
|
|
|
|
delete this._rememberedEntities[uuid];
|
|
|
|
}
|
|
|
|
// Remember state.
|
2019-04-07 14:59:55 -05:00
|
|
|
this._state = reducedState;
|
|
|
|
if (0 === steps.length) {
|
|
|
|
return;
|
2019-04-05 22:50:24 -05:00
|
|
|
}
|
2019-03-28 02:10:46 -05:00
|
|
|
// Emit!
|
2019-04-11 12:55:21 -05:00
|
|
|
const keys = this._packer.computeNewKeys(steps);
|
|
|
|
if (0 !== keys[0].length) {
|
2019-04-11 15:27:39 -05:00
|
|
|
this._socket.send(new KeysPacket(keys));
|
2019-04-11 12:55:21 -05:00
|
|
|
}
|
2019-04-07 14:59:55 -05:00
|
|
|
const packed = this._packer.pack(steps);
|
2019-04-11 15:27:39 -05:00
|
|
|
this._socket.send(new StatePacket(packed));
|
2019-03-20 18:35:59 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|