refactor: immutable diff steps
This commit is contained in:
parent
1b8f5a823a
commit
64c4d0605c
|
@ -31,6 +31,110 @@ export class Informed extends decorate(Trait) {
|
|||
}
|
||||
}
|
||||
|
||||
entityOverrides(path, fn) {
|
||||
const pathOverrides = [
|
||||
['physical', 'state', 'addedToPhysics'],
|
||||
['visible', 'state', 'isVisible'],
|
||||
// Ticking change last.
|
||||
['existent', 'state', 'isTicking'],
|
||||
]
|
||||
return I.List().withMutations((steps) => {
|
||||
for (const pathOverride of pathOverrides) {
|
||||
steps.push(I.Map({
|
||||
op: 'replace',
|
||||
path: path + '/' + pathOverride.join('/'),
|
||||
value: fn(pathOverride),
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
entityStepFilter(op) {
|
||||
return (step) => {
|
||||
if (op !== step.get('op')) {
|
||||
return;
|
||||
}
|
||||
const parts = step.get('parts');
|
||||
if (6 !== parts.length) {
|
||||
return false;
|
||||
}
|
||||
if ('room' !== parts[1]) {
|
||||
return false;
|
||||
}
|
||||
if ('layers' !== parts[2]) {
|
||||
return false;
|
||||
}
|
||||
if ('entityList' !== parts[4]) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
rewriteEntityAdds(state, steps) {
|
||||
return steps.withMutations((steps) => {
|
||||
const filter = this.entityStepFilter('add');
|
||||
const adds = steps.map((step) => {
|
||||
return step.set('parts', step.get('path').split('/'));
|
||||
}).filter(filter);
|
||||
const iterator = adds.values();
|
||||
for (const add of iterator) {
|
||||
// Remember the entity.
|
||||
const parts = add.get('parts');
|
||||
const layerId = parts[3];
|
||||
const uuid = parts[5];
|
||||
if (!(uuid in this._rememberedEntities)) {
|
||||
continue;
|
||||
}
|
||||
const rememberedEntity = this._rememberedEntities[uuid];
|
||||
delete this._rememberedEntities[uuid];
|
||||
// Take a diff from what the client remembers to now.
|
||||
const currentState = state.getIn(
|
||||
['room', 'layers', layerId, 'entityList', uuid]
|
||||
);
|
||||
const addSteps = immutablediff(rememberedEntity, currentState);
|
||||
// Translate step paths to full state location.
|
||||
const fullAddSteps = addSteps.map((addStep) => {
|
||||
return addStep.set('path', add.get('path') + addStep.get('path'));
|
||||
});
|
||||
steps.concat(fullAddSteps);
|
||||
// Add overrides.
|
||||
steps.concat(this.entityOverrides(add.get('path'), (path) => {
|
||||
return rememberedEntity.getIn(path);
|
||||
}));
|
||||
}
|
||||
for (const add of iterator) {
|
||||
// Remove adds.
|
||||
steps.splice(steps.indexOf(add), 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
rewriteEntityRemovals(state, steps) {
|
||||
return steps.withMutations((steps) => {
|
||||
const filter = this.entityStepFilter('remove');
|
||||
const removals = steps.map((step) => {
|
||||
return step.set('parts', step.get('path').split('/'));
|
||||
}).filter(filter);
|
||||
const iterator = removals.values();
|
||||
for (const removal of iterator) {
|
||||
// Remember the entity.
|
||||
const parts = removal.get('parts');
|
||||
const layerId = parts[3];
|
||||
const uuid = parts[5];
|
||||
this._rememberedEntities[uuid] = state.getIn(
|
||||
['room', 'layers', layerId, 'entityList', uuid]
|
||||
);
|
||||
// Add overrides.
|
||||
steps.concat(this.entityOverrides(removal.get('path'), () => false));
|
||||
}
|
||||
for (const removal of iterator) {
|
||||
// Remove removals.
|
||||
steps.splice(steps.indexOf(removal), 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
reduceState(state) {
|
||||
// Set client's self entity.
|
||||
state = state.set('selfEntity', this.entity.instanceUuid);
|
||||
|
@ -79,89 +183,16 @@ export class Informed extends decorate(Trait) {
|
|||
// Reduce state.
|
||||
const reducedState = this.reduceState(state);
|
||||
// Take a pure JS diff.
|
||||
const steps = immutablediff(this._state, reducedState).toJS();
|
||||
// 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);
|
||||
const pathOverrides = [
|
||||
['physical', 'state', 'addedToPhysics'],
|
||||
['visible', 'state', 'isVisible'],
|
||||
// Ticking change last.
|
||||
['existent', 'state', 'isTicking'],
|
||||
]
|
||||
for (const pathOverride of pathOverrides) {
|
||||
steps.push({
|
||||
op: 'replace',
|
||||
path: entityRemoval.path + '/' + pathOverride.join('/'),
|
||||
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) {
|
||||
// Remembered entity?
|
||||
const parts = entityAdd.path.split('/');
|
||||
parts.shift();
|
||||
const uuid = parts[4];
|
||||
if (!(uuid in this._rememberedEntities)) {
|
||||
continue;
|
||||
}
|
||||
const rememberedEntity = this._rememberedEntities[uuid];
|
||||
// Take a diff from what the client remembers to now.
|
||||
const currentState = state.getIn(parts);
|
||||
const addSteps = immutablediff(
|
||||
rememberedEntity,
|
||||
currentState,
|
||||
).toJS();
|
||||
// Translate step paths to full state location.
|
||||
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);
|
||||
const pathOverrides = [
|
||||
['physical', 'state', 'addedToPhysics'],
|
||||
['visible', 'state', 'isVisible'],
|
||||
// Ticking change last.
|
||||
['existent', 'state', 'isTicking'],
|
||||
]
|
||||
for (const pathOverride of pathOverrides) {
|
||||
steps.push({
|
||||
op: 'replace',
|
||||
path: entityAdd.path + '/' + pathOverride.join('/'),
|
||||
value: rememberedEntity.getIn(pathOverride),
|
||||
});
|
||||
}
|
||||
delete this._rememberedEntities[uuid];
|
||||
}
|
||||
// Remember state.
|
||||
this._state = reducedState;
|
||||
let steps = immutablediff(this._state, reducedState);
|
||||
if (0 === steps.length) {
|
||||
this._state = reducedState;
|
||||
return;
|
||||
}
|
||||
// Rewrite entity adds/removals.
|
||||
steps = this.rewriteEntityAdds(state, steps);
|
||||
steps = this.rewriteEntityRemovals(state, steps);
|
||||
// Remember state.
|
||||
this._state = reducedState;
|
||||
// Emit!
|
||||
const keys = this._packer.computeNewKeys(steps);
|
||||
if (0 !== keys[0].length) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user