refactor: state reducer
This commit is contained in:
parent
2f3988cd55
commit
6b4a12e924
|
@ -36,6 +36,7 @@
|
|||
"@avocado/timing": "1.x",
|
||||
"@avocado/topdown": "^1.0.0",
|
||||
"glob": "^7.1.3",
|
||||
"immutablediff": "0.4.4",
|
||||
"is-plain-object": "2.0.4",
|
||||
"source-map-support": "^0.5.11"
|
||||
}
|
||||
|
|
|
@ -71,10 +71,10 @@ function createMainLoop() {
|
|||
lastTime = now;
|
||||
room.tick(elapsed);
|
||||
worldTime.tick(elapsed);
|
||||
stateSynchronizer.tick();
|
||||
// All informed entities get their own slice.
|
||||
stateSynchronizer.tick(elapsed);
|
||||
// Inform entities of the new state.
|
||||
for (const entity of informables) {
|
||||
entity.inform(stateSynchronizer);
|
||||
entity.inform(stateSynchronizer.state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import * as I from 'immutable';
|
||||
import immutablediff from 'immutablediff';
|
||||
import isPlainObject from 'is-plain-object';
|
||||
|
||||
import {compose} from '@avocado/core';
|
||||
import {Trait} from '@avocado/entity';
|
||||
import {Packer} from '@avocado/state';
|
||||
import {Packer, StateSynchronizer} from '@avocado/state';
|
||||
|
||||
const decorate = compose(
|
||||
);
|
||||
|
@ -11,11 +12,9 @@ const decorate = compose(
|
|||
export class Informed extends decorate(Trait) {
|
||||
|
||||
initialize() {
|
||||
this.entityReducer = this.createIndexedReducer();
|
||||
this._informState = I.Map();
|
||||
this._packer = new Packer();
|
||||
this._sentSelf = false;
|
||||
this._socket = undefined;
|
||||
this._state = I.Map();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
@ -25,73 +24,27 @@ export class Informed extends decorate(Trait) {
|
|||
}
|
||||
}
|
||||
|
||||
collapseStateDiff(diff) {
|
||||
for (const index in diff) {
|
||||
const value = diff[index];
|
||||
if (isPlainObject(value)) {
|
||||
if (!this.collapseStateDiff(value)) {
|
||||
delete diff[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (0 === Object.keys(diff).length) {
|
||||
return null;
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
createIndexedReducer() {
|
||||
let previous = {};
|
||||
return (diff, indexed) => {
|
||||
const reducedDiff = {};
|
||||
const current = {};
|
||||
for (const index in indexed) {
|
||||
const item = indexed[index];
|
||||
current[index] = item;
|
||||
// Diff.
|
||||
if (previous[index]) {
|
||||
if (diff[index]) {
|
||||
reducedDiff[index] = diff[index];
|
||||
}
|
||||
}
|
||||
// Added.
|
||||
else {
|
||||
reducedDiff[index] = item.state.toJS();
|
||||
}
|
||||
}
|
||||
// Removed.
|
||||
for (const index in previous) {
|
||||
if (!current[index]) {
|
||||
reducedDiff[index] = false;
|
||||
}
|
||||
}
|
||||
previous = current;
|
||||
return reducedDiff;
|
||||
};
|
||||
}
|
||||
|
||||
reduceStateDiff(diff) {
|
||||
if (diff && diff.room && diff.room.layers) {
|
||||
const room = this.entity.room;
|
||||
const camera = this.entity.camera;
|
||||
for (const index in diff.room.layers) {
|
||||
// Index entities.
|
||||
const layer = room.layer(index);
|
||||
const visibleEntities = layer.visibleEntities(
|
||||
camera.rectangle
|
||||
);
|
||||
const indexedEntities = {};
|
||||
for (const entity of visibleEntities) {
|
||||
indexedEntities[entity.instanceUuid] = entity;
|
||||
}
|
||||
// Reduce entities.
|
||||
diff.room.layers[index].entityList = this.entityReducer(
|
||||
diff.room.layers[index].entityList || {},
|
||||
indexedEntities,
|
||||
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,
|
||||
);
|
||||
}
|
||||
const entityListPath = ['room', 'layers', index, 'entityList'];
|
||||
state = state.setIn(entityListPath, reducedEntityList);
|
||||
}
|
||||
return this.collapseStateDiff(diff);
|
||||
return state;
|
||||
}
|
||||
|
||||
get socket() {
|
||||
|
@ -106,25 +59,21 @@ export class Informed extends decorate(Trait) {
|
|||
methods() {
|
||||
return {
|
||||
|
||||
inform: (stateSynchronizer) => {
|
||||
// Take diff.
|
||||
let diff = stateSynchronizer.diff(this._informState);
|
||||
if (!this._sentSelf) {
|
||||
diff.unshift({
|
||||
op: 'add',
|
||||
path: '/selfEntity',
|
||||
value: this.entity.instanceUuid,
|
||||
});
|
||||
this._sentSelf = true;
|
||||
inform: (state) => {
|
||||
// Reduce state.
|
||||
const reducedState = this.reduceState(state);
|
||||
// Take a pure JS diff.
|
||||
const steps = immutablediff(this._state, reducedState).toJS();
|
||||
this._state = reducedState;
|
||||
if (0 === steps.length) {
|
||||
return;
|
||||
}
|
||||
this._informState = stateSynchronizer.state;
|
||||
// Emit!
|
||||
if (diff) {
|
||||
this._socket.send({
|
||||
type: 'state-update',
|
||||
payload: this._packer.pack(diff),
|
||||
});
|
||||
}
|
||||
const packed = this._packer.pack(steps);
|
||||
this._socket.send({
|
||||
type: 'state-update',
|
||||
payload: packed,
|
||||
});
|
||||
},
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user