perf: traits only tick/render/accept packets when necessary
This commit is contained in:
parent
9f7a37ad2a
commit
4781c73753
|
@ -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) {
|
||||
|
|
|
@ -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(),
|
||||
|
|
Loading…
Reference in New Issue
Block a user