feat: resource registration
This commit is contained in:
parent
d5fac99c7b
commit
305f421085
368
packages/entity/entity.resource.js
Normal file
368
packages/entity/entity.resource.js
Normal file
|
@ -0,0 +1,368 @@
|
||||||
|
import D from 'debug';
|
||||||
|
import without from 'lodash.without';
|
||||||
|
|
||||||
|
import {
|
||||||
|
compose,
|
||||||
|
EventEmitter,
|
||||||
|
fastApply,
|
||||||
|
merge,
|
||||||
|
mergeDiff,
|
||||||
|
} from '@avocado/core';
|
||||||
|
import {Resource} from '@avocado/resource';
|
||||||
|
import {Synchronized} from '@avocado/state';
|
||||||
|
|
||||||
|
import {EntityCreatePacket} from './packets/entity-create.packet';
|
||||||
|
import {hasTrait, lookupTrait} from './trait/registry';
|
||||||
|
|
||||||
|
const debug = D('@avocado:entity:traits');
|
||||||
|
|
||||||
|
const blacklistedAccessorKeys = [
|
||||||
|
'state',
|
||||||
|
];
|
||||||
|
|
||||||
|
// This really seems like a whole lot of complicated nonsense, but it's an
|
||||||
|
// unfortunate consequence of V8 (and maybe others) not optimizing mutable
|
||||||
|
// accessors in fast hidden classes.
|
||||||
|
const traitAccessorForPropertyMap = {};
|
||||||
|
function traitAccessorForProperty(type, property) {
|
||||||
|
if (!(type in traitAccessorForPropertyMap)) {
|
||||||
|
traitAccessorForPropertyMap[type] = {};
|
||||||
|
}
|
||||||
|
if (!(property in traitAccessorForPropertyMap[type])) {
|
||||||
|
traitAccessorForPropertyMap[type][property] = {
|
||||||
|
get: new Function('', `
|
||||||
|
return this._traits['${type}']['${property}'];
|
||||||
|
`),
|
||||||
|
set: new Function('value', `
|
||||||
|
this._traits['${type}']['${property}'] = value;
|
||||||
|
`),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return traitAccessorForPropertyMap[type][property];
|
||||||
|
}
|
||||||
|
function defineTraitAccessors(from, to, instance) {
|
||||||
|
const type = instance.constructor.type();
|
||||||
|
do {
|
||||||
|
Object.getOwnPropertyNames(from).forEach((accessorKey) => {
|
||||||
|
if (-1 !== blacklistedAccessorKeys.indexOf(accessorKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let descriptor = Object.getOwnPropertyDescriptor(from, accessorKey);
|
||||||
|
// Make sure it's actually an accessor.
|
||||||
|
if (!descriptor.get && !descriptor.set) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const accessor = traitAccessorForProperty(type, accessorKey);
|
||||||
|
if (descriptor.get) {
|
||||||
|
descriptor.get = accessor.get;
|
||||||
|
}
|
||||||
|
if (descriptor.set) {
|
||||||
|
descriptor.set = accessor.set;
|
||||||
|
}
|
||||||
|
Object.defineProperty(to, accessorKey, descriptor);
|
||||||
|
});
|
||||||
|
} while (Object.prototype !== (from = Object.getPrototypeOf(from)));
|
||||||
|
}
|
||||||
|
function enumerateTraitAccessorKeys(prototype) {
|
||||||
|
const keys = [];
|
||||||
|
do {
|
||||||
|
Object.getOwnPropertyNames(prototype).forEach((accessorKey) => {
|
||||||
|
if (-1 !== blacklistedAccessorKeys.indexOf(accessorKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let descriptor = Object.getOwnPropertyDescriptor(prototype, accessorKey);
|
||||||
|
// Make sure it's actually an accessor.
|
||||||
|
if (!descriptor.get && !descriptor.set) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
keys.push(accessorKey);
|
||||||
|
});
|
||||||
|
} while (Object.prototype !== (prototype = Object.getPrototypeOf(prototype)));
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
const decorate = compose(
|
||||||
|
EventEmitter,
|
||||||
|
Synchronized,
|
||||||
|
);
|
||||||
|
|
||||||
|
let numericUid = 1;
|
||||||
|
|
||||||
|
export class Entity extends decorate(Resource) {
|
||||||
|
|
||||||
|
constructor(json, jsonext) {
|
||||||
|
super();
|
||||||
|
this._hooks = {};
|
||||||
|
this._hydrationPromise = undefined;
|
||||||
|
this._json = Entity.jsonWithDefaults(json);
|
||||||
|
this._traits = {};
|
||||||
|
this._traitsFlat = [];
|
||||||
|
this._traitTickers = [];
|
||||||
|
this._traitRenderTickers = [];
|
||||||
|
this._traitsAcceptingPackets = [];
|
||||||
|
this.once('destroyed', () => {
|
||||||
|
this.removeAllTraits();
|
||||||
|
});
|
||||||
|
// Bind to prevent lookup overhead.
|
||||||
|
this.tick = this.tick.bind(this);
|
||||||
|
// Fast props.
|
||||||
|
this.numericUid = numericUid++;
|
||||||
|
this.position = [0, 0];
|
||||||
|
this.room = null;
|
||||||
|
this.visibleAabb = [0, 0, 0, 0];
|
||||||
|
// Fast path for instance.
|
||||||
|
if ('undefined' !== typeof json) {
|
||||||
|
this.fromJSON(merge({}, json, jsonext));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptPacket(packet) {
|
||||||
|
for (let i = 0; i < this._traitsAcceptingPackets.length; i++) {
|
||||||
|
const instance = this._traitsAcceptingPackets[i];
|
||||||
|
instance.acceptPacket(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addTrait(type, json = {}) {
|
||||||
|
if (this.is(type)) {
|
||||||
|
debug(`Tried to add trait "${type}" when it already exists!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!hasTrait(type)) {
|
||||||
|
debug(`Tried to add trait "${type}" which isn't registered!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const Trait = lookupTrait(type);
|
||||||
|
// Ensure dependencies.
|
||||||
|
const dependencies = Trait.dependencies();
|
||||||
|
const allTypes = this.allTraitTypes();
|
||||||
|
const lacking = without(dependencies, ...allTypes);
|
||||||
|
if (lacking.length > 0) {
|
||||||
|
debug(
|
||||||
|
`Tried to add trait "${type}" but lack one or more dependents: "${
|
||||||
|
lacking.join('", "')
|
||||||
|
}"!`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Instantiate.
|
||||||
|
const {params, state} = json;
|
||||||
|
const instance = new Trait(this, params, state);
|
||||||
|
// Proxy properties.
|
||||||
|
defineTraitAccessors(Trait.prototype, this, instance);
|
||||||
|
// Attach listeners.
|
||||||
|
const listeners = instance.memoizedListeners();
|
||||||
|
for (const eventName in listeners) {
|
||||||
|
this.on(eventName, listeners[eventName]);
|
||||||
|
}
|
||||||
|
// Proxy methods.
|
||||||
|
const methods = instance.methods();
|
||||||
|
for (const key in methods) {
|
||||||
|
this[key] = methods[key];
|
||||||
|
}
|
||||||
|
// Register hook listeners.
|
||||||
|
const hooks = instance.hooks();
|
||||||
|
for (const key in hooks) {
|
||||||
|
this._hooks[key] = this._hooks[key] || [];
|
||||||
|
this._hooks[key].push({
|
||||||
|
fn: hooks[key],
|
||||||
|
type: Trait.type(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Track trait.
|
||||||
|
this._traits[type] = instance;
|
||||||
|
this._traitsFlat.push(instance);
|
||||||
|
if ('tick' in instance) {
|
||||||
|
this._traitTickers.push(instance.tick);
|
||||||
|
}
|
||||||
|
if ('renderTick' in instance) {
|
||||||
|
this._traitRenderTickers.push(instance.renderTick);
|
||||||
|
}
|
||||||
|
if ('acceptPacket' in instance) {
|
||||||
|
this._traitsAcceptingPackets.push(instance);
|
||||||
|
}
|
||||||
|
this.emit('traitAdded', type, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
addTraits(traits) {
|
||||||
|
for (const type in traits) {
|
||||||
|
this.addTrait(type, traits[type]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allTraitInstances() {
|
||||||
|
return this._traits;
|
||||||
|
}
|
||||||
|
|
||||||
|
allTraitTypes() {
|
||||||
|
return Object.keys(this._traits);
|
||||||
|
}
|
||||||
|
|
||||||
|
fromJSON(json) {
|
||||||
|
super.fromJSON(json);
|
||||||
|
this.addTraits(json.traits);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
hydrate() {
|
||||||
|
if (!this._hydrationPromise) {
|
||||||
|
const promises = [];
|
||||||
|
for (let i = 0; i < this._traitsFlat.length; i++) {
|
||||||
|
promises.push(this._traitsFlat[i].hydrate());
|
||||||
|
}
|
||||||
|
this._hydrationPromise = Promise.all(promises);
|
||||||
|
}
|
||||||
|
return this._hydrationPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
invokeHook(hook, ...args) {
|
||||||
|
const results = {};
|
||||||
|
if (!(hook in this._hooks)) {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
for (const {fn, type} of this._hooks[hook]) {
|
||||||
|
results[type] = fastApply(null, fn, args);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
invokeHookFlat(hook, ...args) {
|
||||||
|
const invokeResults = this.invokeHook(hook, ...args);
|
||||||
|
const results = [];
|
||||||
|
for (const type in invokeResults) {
|
||||||
|
results.push(invokeResults[type]);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
is(type) {
|
||||||
|
return type in this._traits;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jsonWithDefaults(json) {
|
||||||
|
if ('undefined' === typeof json) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const pristine = JSON.parse(JSON.stringify(json));
|
||||||
|
for (const type in json.traits) {
|
||||||
|
if (!hasTrait(type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const Trait = lookupTrait(type);
|
||||||
|
pristine.traits[type] = merge({}, Trait.defaultJSON(), json.traits[type]);
|
||||||
|
}
|
||||||
|
return pristine;
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeDiff() {
|
||||||
|
if (!this.uri) {
|
||||||
|
return this.toJSON();
|
||||||
|
}
|
||||||
|
return mergeDiff(this._json, this.toJSON());
|
||||||
|
}
|
||||||
|
|
||||||
|
packetsForUpdate(force = false) {
|
||||||
|
const packets = [];
|
||||||
|
if (force) {
|
||||||
|
const packet = new EntityCreatePacket(this.mergeDiff(), this);
|
||||||
|
packets.push(packet);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (let i = 0; i < this._traitsFlat.length; i++) {
|
||||||
|
const traitPackets = this._traitsFlat[i].packetsForUpdate();
|
||||||
|
for (let j = 0; j < traitPackets.length; j++) {
|
||||||
|
packets.push(traitPackets[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packets;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTick(elapsed) {
|
||||||
|
for (let i = 0; i < this._traitRenderTickers.length; i++) {
|
||||||
|
this._traitRenderTickers[i](elapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAllTraits() {
|
||||||
|
const types = this.allTraitTypes();
|
||||||
|
this.removeTraits(types);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTrait(type) {
|
||||||
|
if (!this.is(type)) {
|
||||||
|
debug(`Tried to remove trait "${type}" when it doesn't exist!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Destroy instance.
|
||||||
|
const instance = this._traits[type];
|
||||||
|
instance.destroy();
|
||||||
|
// Remove methods, hooks, and properties.
|
||||||
|
const methods = instance.methods();
|
||||||
|
for (const key in methods) {
|
||||||
|
delete this[key];
|
||||||
|
}
|
||||||
|
const hooks = instance.hooks();
|
||||||
|
for (const key in hooks) {
|
||||||
|
const implementation = this._hooks[key].find(({type: hookType}) => {
|
||||||
|
return hookType === type;
|
||||||
|
});
|
||||||
|
this._hooks[key].splice(this._hooks[key].indexOf(implementation), 1);
|
||||||
|
}
|
||||||
|
const Trait = lookupTrait(type);
|
||||||
|
const properties = enumerateTraitAccessorKeys(Trait.prototype);
|
||||||
|
for (let i = 0; i < properties.length; ++i) {
|
||||||
|
const property = properties[i];
|
||||||
|
delete this[property];
|
||||||
|
}
|
||||||
|
// Remove all event listeners.
|
||||||
|
const listeners = instance.memoizedListeners();
|
||||||
|
for (const eventName in listeners) {
|
||||||
|
this.off(eventName, listeners[eventName]);
|
||||||
|
}
|
||||||
|
instance._memoizedListeners = {};
|
||||||
|
// Remove instance.
|
||||||
|
delete this._traits[type];
|
||||||
|
this._traitsFlat.splice(this._traitsFlat.indexOf(instance), 1);
|
||||||
|
if ('tick' in instance) {
|
||||||
|
this._traitTickers.splice(
|
||||||
|
this._traitTickers.indexOf(instance.tick),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ('renderTick' in instance) {
|
||||||
|
this._traitRenderTickers.splice(
|
||||||
|
this._traitRenderTickers.indexOf(instance.renderTick),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const acceptPacketIndex = this._traitsAcceptingPackets.indexOf(instance);
|
||||||
|
if (-1 !== acceptPacketIndex) {
|
||||||
|
this._traitsAcceptingPackets.splice(acceptPacketIndex, 1);
|
||||||
|
}
|
||||||
|
// Unloop.
|
||||||
|
instance.entity = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTraits(types) {
|
||||||
|
types.forEach((type) => this.removeTrait(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
tick(elapsed) {
|
||||||
|
for (let i = 0; i < this._traitTickers.length; i++) {
|
||||||
|
this._traitTickers[i](elapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
const json = {};
|
||||||
|
for (const type in this._traits) {
|
||||||
|
json[type] = this._traits[type].toJSON();
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...super.toJSON(),
|
||||||
|
traits: json,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,371 +1,4 @@
|
||||||
import D from 'debug';
|
export {Entity} from './entity.resource';
|
||||||
import without from 'lodash.without';
|
|
||||||
|
|
||||||
import {
|
|
||||||
compose,
|
|
||||||
EventEmitter,
|
|
||||||
fastApply,
|
|
||||||
merge,
|
|
||||||
mergeDiff,
|
|
||||||
} from '@avocado/core';
|
|
||||||
import {Resource} from '@avocado/resource';
|
|
||||||
import {Synchronized} from '@avocado/state';
|
|
||||||
|
|
||||||
import {EntityCreatePacket} from './packets/entity-create.packet';
|
|
||||||
import {hasTrait, lookupTrait} from './trait/registry';
|
|
||||||
|
|
||||||
const debug = D('@avocado:entity:traits');
|
|
||||||
|
|
||||||
const blacklistedAccessorKeys = [
|
|
||||||
'state',
|
|
||||||
];
|
|
||||||
|
|
||||||
// This really seems like a whole lot of complicated nonsense, but it's an
|
|
||||||
// unfortunate consequence of V8 (and maybe others) not optimizing mutable
|
|
||||||
// accessors in fast hidden classes.
|
|
||||||
const traitAccessorForPropertyMap = {};
|
|
||||||
function traitAccessorForProperty(type, property) {
|
|
||||||
if (!(type in traitAccessorForPropertyMap)) {
|
|
||||||
traitAccessorForPropertyMap[type] = {};
|
|
||||||
}
|
|
||||||
if (!(property in traitAccessorForPropertyMap[type])) {
|
|
||||||
traitAccessorForPropertyMap[type][property] = {
|
|
||||||
get: new Function('', `
|
|
||||||
return this._traits['${type}']['${property}'];
|
|
||||||
`),
|
|
||||||
set: new Function('value', `
|
|
||||||
this._traits['${type}']['${property}'] = value;
|
|
||||||
`),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return traitAccessorForPropertyMap[type][property];
|
|
||||||
}
|
|
||||||
function defineTraitAccessors(from, to, instance) {
|
|
||||||
const type = instance.constructor.type();
|
|
||||||
do {
|
|
||||||
Object.getOwnPropertyNames(from).forEach((accessorKey) => {
|
|
||||||
if (-1 !== blacklistedAccessorKeys.indexOf(accessorKey)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let descriptor = Object.getOwnPropertyDescriptor(from, accessorKey);
|
|
||||||
// Make sure it's actually an accessor.
|
|
||||||
if (!descriptor.get && !descriptor.set) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const accessor = traitAccessorForProperty(type, accessorKey);
|
|
||||||
if (descriptor.get) {
|
|
||||||
descriptor.get = accessor.get;
|
|
||||||
}
|
|
||||||
if (descriptor.set) {
|
|
||||||
descriptor.set = accessor.set;
|
|
||||||
}
|
|
||||||
Object.defineProperty(to, accessorKey, descriptor);
|
|
||||||
});
|
|
||||||
} while (Object.prototype !== (from = Object.getPrototypeOf(from)));
|
|
||||||
}
|
|
||||||
function enumerateTraitAccessorKeys(prototype) {
|
|
||||||
const keys = [];
|
|
||||||
do {
|
|
||||||
Object.getOwnPropertyNames(prototype).forEach((accessorKey) => {
|
|
||||||
if (-1 !== blacklistedAccessorKeys.indexOf(accessorKey)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let descriptor = Object.getOwnPropertyDescriptor(prototype, accessorKey);
|
|
||||||
// Make sure it's actually an accessor.
|
|
||||||
if (!descriptor.get && !descriptor.set) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
keys.push(accessorKey);
|
|
||||||
});
|
|
||||||
} while (Object.prototype !== (prototype = Object.getPrototypeOf(prototype)));
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
const decorate = compose(
|
|
||||||
EventEmitter,
|
|
||||||
Synchronized,
|
|
||||||
);
|
|
||||||
|
|
||||||
let numericUid = 1;
|
|
||||||
|
|
||||||
export class Entity extends decorate(Resource) {
|
|
||||||
|
|
||||||
constructor(json, jsonext) {
|
|
||||||
super();
|
|
||||||
this._hooks = {};
|
|
||||||
this._hydrationPromise = undefined;
|
|
||||||
this._json = Entity.jsonWithDefaults(json);
|
|
||||||
this._traits = {};
|
|
||||||
this._traitsFlat = [];
|
|
||||||
this._traitTickers = [];
|
|
||||||
this._traitRenderTickers = [];
|
|
||||||
this._traitsAcceptingPackets = [];
|
|
||||||
this.once('destroyed', () => {
|
|
||||||
this.removeAllTraits();
|
|
||||||
});
|
|
||||||
// Bind to prevent lookup overhead.
|
|
||||||
this.tick = this.tick.bind(this);
|
|
||||||
// Fast props.
|
|
||||||
this.numericUid = numericUid++;
|
|
||||||
this.position = [0, 0];
|
|
||||||
this.room = null;
|
|
||||||
this.visibleAabb = [0, 0, 0, 0];
|
|
||||||
// Fast path for instance.
|
|
||||||
if ('undefined' !== typeof json) {
|
|
||||||
this.fromJSON(merge({}, json, jsonext));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
acceptPacket(packet) {
|
|
||||||
for (let i = 0; i < this._traitsAcceptingPackets.length; i++) {
|
|
||||||
const instance = this._traitsAcceptingPackets[i];
|
|
||||||
instance.acceptPacket(packet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addTrait(type, json = {}) {
|
|
||||||
if (this.is(type)) {
|
|
||||||
debug(`Tried to add trait "${type}" when it already exists!`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!hasTrait(type)) {
|
|
||||||
debug(`Tried to add trait "${type}" which isn't registered!`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const Trait = lookupTrait(type);
|
|
||||||
// Ensure dependencies.
|
|
||||||
const dependencies = Trait.dependencies();
|
|
||||||
const allTypes = this.allTraitTypes();
|
|
||||||
const lacking = without(dependencies, ...allTypes);
|
|
||||||
if (lacking.length > 0) {
|
|
||||||
debug(
|
|
||||||
`Tried to add trait "${type}" but lack one or more dependents: "${
|
|
||||||
lacking.join('", "')
|
|
||||||
}"!`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Instantiate.
|
|
||||||
const {params, state} = json;
|
|
||||||
const instance = new Trait(this, params, state);
|
|
||||||
// Proxy properties.
|
|
||||||
defineTraitAccessors(Trait.prototype, this, instance);
|
|
||||||
// Attach listeners.
|
|
||||||
const listeners = instance.memoizedListeners();
|
|
||||||
for (const eventName in listeners) {
|
|
||||||
this.on(eventName, listeners[eventName]);
|
|
||||||
}
|
|
||||||
// Proxy methods.
|
|
||||||
const methods = instance.methods();
|
|
||||||
for (const key in methods) {
|
|
||||||
this[key] = methods[key];
|
|
||||||
}
|
|
||||||
// Register hook listeners.
|
|
||||||
const hooks = instance.hooks();
|
|
||||||
for (const key in hooks) {
|
|
||||||
this._hooks[key] = this._hooks[key] || [];
|
|
||||||
this._hooks[key].push({
|
|
||||||
fn: hooks[key],
|
|
||||||
type: Trait.type(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Track trait.
|
|
||||||
this._traits[type] = instance;
|
|
||||||
this._traitsFlat.push(instance);
|
|
||||||
if ('tick' in instance) {
|
|
||||||
this._traitTickers.push(instance.tick);
|
|
||||||
}
|
|
||||||
if ('renderTick' in instance) {
|
|
||||||
this._traitRenderTickers.push(instance.renderTick);
|
|
||||||
}
|
|
||||||
if ('acceptPacket' in instance) {
|
|
||||||
this._traitsAcceptingPackets.push(instance);
|
|
||||||
}
|
|
||||||
this.emit('traitAdded', type, instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
addTraits(traits) {
|
|
||||||
for (const type in traits) {
|
|
||||||
this.addTrait(type, traits[type]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allTraitInstances() {
|
|
||||||
return this._traits;
|
|
||||||
}
|
|
||||||
|
|
||||||
allTraitTypes() {
|
|
||||||
return Object.keys(this._traits);
|
|
||||||
}
|
|
||||||
|
|
||||||
fromJSON(json) {
|
|
||||||
super.fromJSON(json);
|
|
||||||
this.addTraits(json.traits);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
hydrate() {
|
|
||||||
if (!this._hydrationPromise) {
|
|
||||||
const promises = [];
|
|
||||||
for (let i = 0; i < this._traitsFlat.length; i++) {
|
|
||||||
promises.push(this._traitsFlat[i].hydrate());
|
|
||||||
}
|
|
||||||
this._hydrationPromise = Promise.all(promises);
|
|
||||||
}
|
|
||||||
return this._hydrationPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
invokeHook(hook, ...args) {
|
|
||||||
const results = {};
|
|
||||||
if (!(hook in this._hooks)) {
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
for (const {fn, type} of this._hooks[hook]) {
|
|
||||||
results[type] = fastApply(null, fn, args);
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
invokeHookFlat(hook, ...args) {
|
|
||||||
const invokeResults = this.invokeHook(hook, ...args);
|
|
||||||
const results = [];
|
|
||||||
for (const type in invokeResults) {
|
|
||||||
results.push(invokeResults[type]);
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
is(type) {
|
|
||||||
return type in this._traits;
|
|
||||||
}
|
|
||||||
|
|
||||||
static jsonWithDefaults(json) {
|
|
||||||
if ('undefined' === typeof json) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const pristine = JSON.parse(JSON.stringify(json));
|
|
||||||
for (const type in json.traits) {
|
|
||||||
if (!hasTrait(type)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const Trait = lookupTrait(type);
|
|
||||||
pristine.traits[type] = merge({}, Trait.defaultJSON(), json.traits[type]);
|
|
||||||
}
|
|
||||||
return pristine;
|
|
||||||
}
|
|
||||||
|
|
||||||
mergeDiff() {
|
|
||||||
if (!this.uri) {
|
|
||||||
return this.toJSON();
|
|
||||||
}
|
|
||||||
return mergeDiff(this._json, this.toJSON());
|
|
||||||
}
|
|
||||||
|
|
||||||
packetsForUpdate(force = false) {
|
|
||||||
const packets = [];
|
|
||||||
if (force) {
|
|
||||||
const packet = new EntityCreatePacket(this.mergeDiff(), this);
|
|
||||||
packets.push(packet);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (let i = 0; i < this._traitsFlat.length; i++) {
|
|
||||||
const traitPackets = this._traitsFlat[i].packetsForUpdate();
|
|
||||||
for (let j = 0; j < traitPackets.length; j++) {
|
|
||||||
packets.push(traitPackets[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return packets;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTick(elapsed) {
|
|
||||||
for (let i = 0; i < this._traitRenderTickers.length; i++) {
|
|
||||||
this._traitRenderTickers[i](elapsed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeAllTraits() {
|
|
||||||
const types = this.allTraitTypes();
|
|
||||||
this.removeTraits(types);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeTrait(type) {
|
|
||||||
if (!this.is(type)) {
|
|
||||||
debug(`Tried to remove trait "${type}" when it doesn't exist!`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Destroy instance.
|
|
||||||
const instance = this._traits[type];
|
|
||||||
instance.destroy();
|
|
||||||
// Remove methods, hooks, and properties.
|
|
||||||
const methods = instance.methods();
|
|
||||||
for (const key in methods) {
|
|
||||||
delete this[key];
|
|
||||||
}
|
|
||||||
const hooks = instance.hooks();
|
|
||||||
for (const key in hooks) {
|
|
||||||
const implementation = this._hooks[key].find(({type: hookType}) => {
|
|
||||||
return hookType === type;
|
|
||||||
});
|
|
||||||
this._hooks[key].splice(this._hooks[key].indexOf(implementation), 1);
|
|
||||||
}
|
|
||||||
const Trait = lookupTrait(type);
|
|
||||||
const properties = enumerateTraitAccessorKeys(Trait.prototype);
|
|
||||||
for (let i = 0; i < properties.length; ++i) {
|
|
||||||
const property = properties[i];
|
|
||||||
delete this[property];
|
|
||||||
}
|
|
||||||
// Remove all event listeners.
|
|
||||||
const listeners = instance.memoizedListeners();
|
|
||||||
for (const eventName in listeners) {
|
|
||||||
this.off(eventName, listeners[eventName]);
|
|
||||||
}
|
|
||||||
instance._memoizedListeners = {};
|
|
||||||
// Remove instance.
|
|
||||||
delete this._traits[type];
|
|
||||||
this._traitsFlat.splice(this._traitsFlat.indexOf(instance), 1);
|
|
||||||
if ('tick' in instance) {
|
|
||||||
this._traitTickers.splice(
|
|
||||||
this._traitTickers.indexOf(instance.tick),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ('renderTick' in instance) {
|
|
||||||
this._traitRenderTickers.splice(
|
|
||||||
this._traitRenderTickers.indexOf(instance.renderTick),
|
|
||||||
1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const acceptPacketIndex = this._traitsAcceptingPackets.indexOf(instance);
|
|
||||||
if (-1 !== acceptPacketIndex) {
|
|
||||||
this._traitsAcceptingPackets.splice(acceptPacketIndex, 1);
|
|
||||||
}
|
|
||||||
// Unloop.
|
|
||||||
instance.entity = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeTraits(types) {
|
|
||||||
types.forEach((type) => this.removeTrait(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
tick(elapsed) {
|
|
||||||
for (let i = 0; i < this._traitTickers.length; i++) {
|
|
||||||
this._traitTickers[i](elapsed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON() {
|
|
||||||
const json = {};
|
|
||||||
for (const type in this._traits) {
|
|
||||||
json[type] = this._traits[type].toJSON();
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...super.toJSON(),
|
|
||||||
traits: json,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export {EntityCreatePacket} from './packets/entity-create.packet';
|
export {EntityCreatePacket} from './packets/entity-create.packet';
|
||||||
export {EntityRemovePacket} from './packets/entity-remove.packet';
|
export {EntityRemovePacket} from './packets/entity-remove.packet';
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {Synchronized} from '@avocado/state';
|
||||||
|
|
||||||
import {EntityCreatePacket} from '../packets/entity-create.packet';
|
import {EntityCreatePacket} from '../packets/entity-create.packet';
|
||||||
import {EntityRemovePacket} from '../packets/entity-remove.packet';
|
import {EntityRemovePacket} from '../packets/entity-remove.packet';
|
||||||
import {Entity} from '../index';
|
import {Entity} from '../entity.resource';
|
||||||
|
|
||||||
const decorate = compose(
|
const decorate = compose(
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
|
|
|
@ -3,7 +3,7 @@ const PIXI = 'undefined' !== typeof window ? require('pixi.js') : undefined;
|
||||||
import {compose} from '@avocado/core';
|
import {compose} from '@avocado/core';
|
||||||
import {Vector} from '@avocado/math';
|
import {Vector} from '@avocado/math';
|
||||||
|
|
||||||
import {Image} from './image';
|
import {Image} from './image.resource';
|
||||||
|
|
||||||
const decorate = compose(
|
const decorate = compose(
|
||||||
Vector.Mixin('size', 'width', 'height', {
|
Vector.Mixin('size', 'width', 'height', {
|
||||||
|
|
|
@ -4,7 +4,7 @@ export {Canvas} from './canvas';
|
||||||
export {Color} from './color';
|
export {Color} from './color';
|
||||||
export {Container} from './container';
|
export {Container} from './container';
|
||||||
export {hasGraphics} from './has-graphics';
|
export {hasGraphics} from './has-graphics';
|
||||||
export {Image} from './image';
|
export {Image} from './image.resource';
|
||||||
export {Primitives} from './primitives';
|
export {Primitives} from './primitives';
|
||||||
export {Renderable} from './renderable';
|
export {Renderable} from './renderable';
|
||||||
export {Renderer} from './renderer';
|
export {Renderer} from './renderer';
|
||||||
|
|
|
@ -5,7 +5,7 @@ import * as THREE from 'three';
|
||||||
import {compose, Property} from '@avocado/core';
|
import {compose, Property} from '@avocado/core';
|
||||||
|
|
||||||
import {hasGraphics} from '../has-graphics';
|
import {hasGraphics} from '../has-graphics';
|
||||||
import {Image} from '../image';
|
import {Image} from '../image.resource';
|
||||||
import {Renderable} from '../renderable';
|
import {Renderable} from '../renderable';
|
||||||
import {Sprite} from '../sprite';
|
import {Sprite} from '../sprite';
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {compose} from '@avocado/core';
|
||||||
import {StateProperty, Trait} from '@avocado/entity';
|
import {StateProperty, Trait} from '@avocado/entity';
|
||||||
import {Rectangle, Vector} from '@avocado/math';
|
import {Rectangle, Vector} from '@avocado/math';
|
||||||
|
|
||||||
import {Image} from '../image';
|
import {Image} from '../image.resource';
|
||||||
import {Sprite} from '../sprite';
|
import {Sprite} from '../sprite';
|
||||||
|
|
||||||
const decorate = compose(
|
const decorate = compose(
|
||||||
|
|
37
packages/net/packet/synchronized.js
Normal file
37
packages/net/packet/synchronized.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
export function SynchronizedMixin(Superclass) {
|
||||||
|
|
||||||
|
return class Synchronized extends Superclass {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._idempotentPackets = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanPackets() {
|
||||||
|
this._idempotentPackets = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
packetsAreIdempotent() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
packets(informed) {
|
||||||
|
if (this._idempotentPackets.length > 0) {
|
||||||
|
return this._idempotentPackets;
|
||||||
|
}
|
||||||
|
let packets = this.packetsForTick(informed);
|
||||||
|
if (!packets) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
packets = Array.isArray(packets) ? packets : [packets];
|
||||||
|
if (this.packetsAreIdempotent()) {
|
||||||
|
this._idempotentPackets = packets;
|
||||||
|
}
|
||||||
|
return packets;
|
||||||
|
}
|
||||||
|
|
||||||
|
packetsForTick(informed) {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,2 +1,10 @@
|
||||||
export {ResourceRegistry, globalRegistry} from './registry';
|
export {
|
||||||
|
allResources,
|
||||||
|
idFromResource,
|
||||||
|
resourceFromId,
|
||||||
|
registerResource,
|
||||||
|
} from './registry';
|
||||||
export {Resource} from './resource';
|
export {Resource} from './resource';
|
||||||
|
export {ResourceRegistry, globalRegistry} from './store';
|
||||||
|
|
||||||
|
|
||||||
|
|
13
packages/resource/packet/resource.packet.js
Normal file
13
packages/resource/packet/resource.packet.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import {Packet} from '@avocado/net';
|
||||||
|
|
||||||
|
export class ResourcePacket extends Packet {
|
||||||
|
|
||||||
|
static get schema() {
|
||||||
|
return {
|
||||||
|
...super.schema,
|
||||||
|
resourceType: 'string',
|
||||||
|
uuid: 'string',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
21
packages/resource/registry.js
Normal file
21
packages/resource/registry.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
let resourceId = 0;
|
||||||
|
const resourceToIdMap = new Map();
|
||||||
|
const idToResourceMap = new Map();
|
||||||
|
export function allResources() {
|
||||||
|
return Array.from(idToResourceMap.values());
|
||||||
|
}
|
||||||
|
export function idFromResource(Resource) {
|
||||||
|
return resourceToIdMap.get(Resource);
|
||||||
|
}
|
||||||
|
export function resourceFromId(id) {
|
||||||
|
return idToResourceMap.get(id);
|
||||||
|
}
|
||||||
|
export function registerResource(Resource) {
|
||||||
|
if (resourceToIdMap.has(Resource)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const id = resourceId++;
|
||||||
|
resourceToIdMap.set(Resource, id);
|
||||||
|
idToResourceMap.set(id, Resource);
|
||||||
|
return id;
|
||||||
|
}
|
|
@ -1,106 +1 @@
|
||||||
import {Howl, Howler} from 'howler';
|
export {Sound} from './sound.resource';
|
||||||
|
|
||||||
import {Resource} from '@avocado/resource';
|
|
||||||
|
|
||||||
export class Sound extends Resource {
|
|
||||||
|
|
||||||
static load(uri) {
|
|
||||||
if (!this.loadCache) {
|
|
||||||
this.loadCache = {};
|
|
||||||
}
|
|
||||||
if (!this.refCache) {
|
|
||||||
this.refCache = {};
|
|
||||||
}
|
|
||||||
if (!(uri in this.refCache)) {
|
|
||||||
this.refCache[uri] = 0;
|
|
||||||
}
|
|
||||||
this.refCache[uri] += 1;
|
|
||||||
if (this.loadCache[uri]) {
|
|
||||||
return this.loadCache[uri];
|
|
||||||
}
|
|
||||||
return this.loadCache[uri] = this.read(uri).then((json) => {
|
|
||||||
const instance = new Sound(json);
|
|
||||||
let lastTime = performance.now();
|
|
||||||
instance.tickHandle = setInterval(() => {
|
|
||||||
const now = performance.now();
|
|
||||||
const elapsed = (now - lastTime) / 1000;
|
|
||||||
lastTime = now;
|
|
||||||
instance.tick(elapsed);
|
|
||||||
}, 20);
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
instance.sound.once('load', () => {
|
|
||||||
resolve(instance);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(json) {
|
|
||||||
super();
|
|
||||||
this.interval = 0;
|
|
||||||
this.lastPlayHandle = undefined;
|
|
||||||
this.locked = false;
|
|
||||||
this.originalVolume = 1;
|
|
||||||
this.remaining = 0;
|
|
||||||
this.sound = undefined;
|
|
||||||
this.tickHandle = undefined;
|
|
||||||
if ('undefined' !== typeof json) {
|
|
||||||
this.fromJSON(json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
clearInterval(instance.tickHandle);
|
|
||||||
if (this.sound) {
|
|
||||||
this.sound.unload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fromJSON(json) {
|
|
||||||
super.fromJSON(json);
|
|
||||||
if (json.interval) {
|
|
||||||
this.interval = json.interval;
|
|
||||||
}
|
|
||||||
if (json.volume) {
|
|
||||||
this.originalVolume = json.volume;
|
|
||||||
}
|
|
||||||
this.sound = new Howl(json);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
play() {
|
|
||||||
if (this.locked) {
|
|
||||||
const volume = this.sound.volume(this.lastPlayHandle);
|
|
||||||
this.sound.volume(
|
|
||||||
Math.min(1, Math.min(volume, this.originalVolume * 20)),
|
|
||||||
this.lastPlayHandle
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.lastPlayHandle = this.sound.play();
|
|
||||||
if (this.interval > 0) {
|
|
||||||
this.locked = true;
|
|
||||||
this.remaining = this.interval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
release() {
|
|
||||||
const ctor = this.constructor;
|
|
||||||
const uri = this.uri;
|
|
||||||
ctor.refCache[uri] -= 1;
|
|
||||||
if (0 === ctor.refCache[uri]) {
|
|
||||||
delete ctor.loadCache[uri];
|
|
||||||
this.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tick(elapsed) {
|
|
||||||
if (this.locked && this.interval > 0) {
|
|
||||||
this.remaining -= elapsed;
|
|
||||||
if (this.remaining <= 0) {
|
|
||||||
this.locked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
106
packages/sound/sound.resource.js
Normal file
106
packages/sound/sound.resource.js
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
import {Howl, Howler} from 'howler';
|
||||||
|
|
||||||
|
import {Resource} from '@avocado/resource';
|
||||||
|
|
||||||
|
export class Sound extends Resource {
|
||||||
|
|
||||||
|
static load(uri) {
|
||||||
|
if (!this.loadCache) {
|
||||||
|
this.loadCache = {};
|
||||||
|
}
|
||||||
|
if (!this.refCache) {
|
||||||
|
this.refCache = {};
|
||||||
|
}
|
||||||
|
if (!(uri in this.refCache)) {
|
||||||
|
this.refCache[uri] = 0;
|
||||||
|
}
|
||||||
|
this.refCache[uri] += 1;
|
||||||
|
if (this.loadCache[uri]) {
|
||||||
|
return this.loadCache[uri];
|
||||||
|
}
|
||||||
|
return this.loadCache[uri] = this.read(uri).then((json) => {
|
||||||
|
const instance = new Sound(json);
|
||||||
|
let lastTime = performance.now();
|
||||||
|
instance.tickHandle = setInterval(() => {
|
||||||
|
const now = performance.now();
|
||||||
|
const elapsed = (now - lastTime) / 1000;
|
||||||
|
lastTime = now;
|
||||||
|
instance.tick(elapsed);
|
||||||
|
}, 20);
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
instance.sound.once('load', () => {
|
||||||
|
resolve(instance);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(json) {
|
||||||
|
super();
|
||||||
|
this.interval = 0;
|
||||||
|
this.lastPlayHandle = undefined;
|
||||||
|
this.locked = false;
|
||||||
|
this.originalVolume = 1;
|
||||||
|
this.remaining = 0;
|
||||||
|
this.sound = undefined;
|
||||||
|
this.tickHandle = undefined;
|
||||||
|
if ('undefined' !== typeof json) {
|
||||||
|
this.fromJSON(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
clearInterval(instance.tickHandle);
|
||||||
|
if (this.sound) {
|
||||||
|
this.sound.unload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fromJSON(json) {
|
||||||
|
super.fromJSON(json);
|
||||||
|
if (json.interval) {
|
||||||
|
this.interval = json.interval;
|
||||||
|
}
|
||||||
|
if (json.volume) {
|
||||||
|
this.originalVolume = json.volume;
|
||||||
|
}
|
||||||
|
this.sound = new Howl(json);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
play() {
|
||||||
|
if (this.locked) {
|
||||||
|
const volume = this.sound.volume(this.lastPlayHandle);
|
||||||
|
this.sound.volume(
|
||||||
|
Math.min(1, Math.min(volume, this.originalVolume * 20)),
|
||||||
|
this.lastPlayHandle
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.lastPlayHandle = this.sound.play();
|
||||||
|
if (this.interval > 0) {
|
||||||
|
this.locked = true;
|
||||||
|
this.remaining = this.interval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
release() {
|
||||||
|
const ctor = this.constructor;
|
||||||
|
const uri = this.uri;
|
||||||
|
ctor.refCache[uri] -= 1;
|
||||||
|
if (0 === ctor.refCache[uri]) {
|
||||||
|
delete ctor.loadCache[uri];
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tick(elapsed) {
|
||||||
|
if (this.locked && this.interval > 0) {
|
||||||
|
this.remaining -= elapsed;
|
||||||
|
if (this.remaining <= 0) {
|
||||||
|
this.locked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
export {Animation} from './animation';
|
export {Animation} from './animation.resource';
|
||||||
export {AnimationView} from './animation-view';
|
export {AnimationView} from './animation-view';
|
||||||
export {
|
export {
|
||||||
cancelAnimationFrame,
|
cancelAnimationFrame,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import {compose} from '@avocado/core';
|
||||||
import {StateProperty, Trait} from '@avocado/entity';
|
import {StateProperty, Trait} from '@avocado/entity';
|
||||||
import {Rectangle, Vector} from '@avocado/math';
|
import {Rectangle, Vector} from '@avocado/math';
|
||||||
|
|
||||||
import {Animation} from '../animation';
|
import {Animation} from '../animation.resource';
|
||||||
import {AnimationView} from '../animation-view';
|
import {AnimationView} from '../animation-view';
|
||||||
import {TraitAnimatedPacket} from '../packets/trait-animated.packet';
|
import {TraitAnimatedPacket} from '../packets/trait-animated.packet';
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
export {Camera} from './camera';
|
export {Camera} from './camera';
|
||||||
export {Layer} from './layer';
|
export {Layer} from './layer';
|
||||||
export {LayerView} from './layer-view';
|
export {LayerView} from './layer-view';
|
||||||
export {Room} from './room';
|
export {Room} from './room.resource';
|
||||||
export {RoomView} from './room-view';
|
export {RoomView} from './room-view';
|
||||||
export {TilesRenderer} from './tiles-renderer';
|
export {TilesRenderer} from './tiles-renderer';
|
||||||
export {Tiles} from './tiles';
|
export {Tiles} from './tiles';
|
||||||
export {Tileset} from './tileset';
|
export {Tileset} from './tileset.resource';
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {ShapeList} from '@avocado/physics';
|
||||||
import {Synchronized} from '@avocado/state';
|
import {Synchronized} from '@avocado/state';
|
||||||
|
|
||||||
import {Tiles} from './tiles';
|
import {Tiles} from './tiles';
|
||||||
import {Tileset} from './tileset';
|
import {Tileset} from './tileset.resource';
|
||||||
import {LayerCreatePacket} from './packets/layer-create.packet';
|
import {LayerCreatePacket} from './packets/layer-create.packet';
|
||||||
import {TileUpdatePacket} from './packets/tile-update.packet';
|
import {TileUpdatePacket} from './packets/tile-update.packet';
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user