perf: traits only tick/render/accept packets when necessary

This commit is contained in:
cha0s 2019-05-03 19:54:23 -05:00
parent 9f7a37ad2a
commit 4781c73753
2 changed files with 62 additions and 37 deletions

View File

@ -61,14 +61,18 @@ export class Entity extends decorate(Resource) {
this._hooks = {};
this.isDirty = true;
this._traits = {};
this._traitsFlat = [];
this._traitsToTick = [];
this._traitsToRenderTick = [];
this._traitsAcceptingPackets = [];
this.once('destroyed', () => {
this.removeAllTraits();
});
}
acceptPacket(packet) {
for (const type in this._traits) {
const instance = this._traits[type];
for (let i = 0; i < this._traitsAcceptingPackets.length; i++) {
const instance = this._traitsAcceptingPackets[i];
instance.acceptPacket(packet);
}
}
@ -125,6 +129,16 @@ export class Entity extends decorate(Resource) {
this.state = this._setInstanceState(this.state, type, instance);
// Track trait.
this._traits[type] = instance;
this._traitsFlat.push(instance);
if ('tick' in instance) {
this._traitsToTick.push(instance);
}
if ('renderTick' in instance) {
this._traitsToRenderTick.push(instance);
}
if ('acceptPacket' in instance) {
this._traitsAcceptingPackets.push(instance);
}
this.emit('traitAdded', type, instance);
}
@ -150,8 +164,8 @@ export class Entity extends decorate(Resource) {
hydrate() {
const promises = [];
for (const type in this._traits) {
const instance = this._traits[type];
for (let i = 0; i < this._traitsFlat.length; i++) {
const instance = this._traitsFlat[i];
promises.push(instance.hydrate());
}
return Promise.all(promises);
@ -200,8 +214,8 @@ export class Entity extends decorate(Resource) {
}
renderTick(elapsed) {
for (const type in this._traits) {
const instance = this._traits[type];
for (let i = 0; i < this._traitsToRenderTick.length; i++) {
const instance = this._traitsToRenderTick[i];
instance.renderTick(elapsed);
}
}
@ -244,6 +258,19 @@ export class Entity extends decorate(Resource) {
this.state = this.state.delete(type);
// Remove instance.
delete this._traits[type];
this._traitsFlat.splice(this._traitsFlat.indexOf(instance), 1);
const tickIndex = this._traitsToTick.indexOf(instance);
if (-1 !== tickIndex) {
this._traitsToTick.splice(tickIndex, 1);
}
const renderTickIndex = this._traitsToRenderTick.indexOf(instance);
if (-1 !== renderTickIndex) {
this._traitsToRenderTick.splice(renderTickIndex, 1);
}
const acceptPacketIndex = this._traitsAcceptingPackets.indexOf(instance);
if (-1 !== acceptPacketIndex) {
this._traitsAcceptingPackets.splice(acceptPacketIndex, 1);
}
// Unloop.
instance.entity = undefined;
}
@ -253,40 +280,44 @@ export class Entity extends decorate(Resource) {
}
_setInstanceState(state, type, instance) {
let map = state.has(type) ? state.get(type) : I.Map();
map = map.set('state', instance.state);
map = map.set('params', instance.params);
return state.set(type, map);
return state.setIn(
[type, 'state'], instance.state
).setIn(
[type, 'params'], instance.params
);
}
tick(elapsed) {
for (const type in this._traits) {
// If .destroy is called immediately when ticking a trait, then the next
// traits could go away.
if (!(type in this._traits)) {
continue;
const traitsToTick = this._traitsToTick.slice(0);
for (let i = 0; i < traitsToTick.length; i++) {
const instance = traitsToTick[i];
// If .destroy is called immediately when ticking a trait, then the
// entity will go away.
if (instance.entity) {
instance.tick(elapsed);
}
const instance = this._traits[type];
instance.tick(elapsed);
}
if (AVOCADO_SERVER) {
this.state = this.state.withMutations((state) => {
for (const type in this._traits) {
const instance = this._traits[type];
if (!instance.isDirty) {
continue;
}
instance.isDirty = false;
this.isDirty = true;
if (this.is('listed')) {
this.list.markEntityDirty(this);
}
this._setInstanceState(state, type, instance);
}
});
this.tickMutateState();
}
}
tickMutateState() {
this.state = this.state.withMutations((state) => {
for (let i = 0; i < this._traitsFlat.length; i++) {
const instance = this._traitsFlat[i];
if (!instance.isDirty) {
continue;
}
instance.isDirty = false;
if (this.is('listed')) {
this.list.markEntityDirty(this);
}
this._setInstanceState(state, instance.constructor.type(), instance);
}
});
}
toJSON() {
const json = {};
for (const type in this._traits) {

View File

@ -21,8 +21,6 @@ export class Trait extends decorate(class {}) {
this.state = I.fromJS(ctor.defaultState()).merge(I.fromJS(state));
}
acceptPacket(packet) {}
destroy() {}
hooks() {
@ -68,10 +66,6 @@ export class Trait extends decorate(class {}) {
}
}
renderTick(elapsed) {}
tick(elapsed) {}
toJSON() {
return {
params: this.params.toJS(),