dcl-npc-toolkit-ai-version
Version:
A collection of tools for creating Non-Player-Characters (NPCs). These are capable of having conversations with the player, and play different animations. AI usage is added atop of it
291 lines • 10.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.defineTypes = exports.deprecated = exports.filterChildren = exports.filter = exports.type = exports.globalContext = exports.Context = exports.hasFilter = exports.SchemaDefinition = void 0;
const Schema_1 = require("./Schema");
const ArraySchema_1 = require("./types/ArraySchema");
const MapSchema_1 = require("./types/MapSchema");
const typeRegistry_1 = require("./types/typeRegistry");
class SchemaDefinition {
constructor() {
//
// TODO: use a "field" structure combining all these properties per-field.
//
this.indexes = {};
this.fieldsByIndex = {};
this.deprecated = {};
this.descriptors = {};
}
static create(parent) {
const definition = new SchemaDefinition();
// support inheritance
definition.schema = Object.assign({}, parent && parent.schema || {});
definition.indexes = Object.assign({}, parent && parent.indexes || {});
definition.fieldsByIndex = Object.assign({}, parent && parent.fieldsByIndex || {});
definition.descriptors = Object.assign({}, parent && parent.descriptors || {});
definition.deprecated = Object.assign({}, parent && parent.deprecated || {});
return definition;
}
addField(field, type) {
const index = this.getNextFieldIndex();
this.fieldsByIndex[index] = field;
this.indexes[field] = index;
this.schema[field] = (Array.isArray(type))
? { array: type[0] }
: type;
}
hasField(field) {
return this.indexes[field] !== undefined;
}
addFilter(field, cb) {
if (!this.filters) {
this.filters = {};
this.indexesWithFilters = [];
}
this.filters[this.indexes[field]] = cb;
this.indexesWithFilters.push(this.indexes[field]);
return true;
}
addChildrenFilter(field, cb) {
const index = this.indexes[field];
const type = this.schema[field];
if ((0, typeRegistry_1.getType)(Object.keys(type)[0])) {
if (!this.childFilters) {
this.childFilters = {};
}
this.childFilters[index] = cb;
return true;
}
else {
console.warn(`@filterChildren: field '${field}' can't have children. Ignoring filter.`);
}
}
getChildrenFilter(field) {
return this.childFilters && this.childFilters[this.indexes[field]];
}
getNextFieldIndex() {
return Object.keys(this.schema || {}).length;
}
}
exports.SchemaDefinition = SchemaDefinition;
function hasFilter(klass) {
return klass._context && klass._context.useFilters;
}
exports.hasFilter = hasFilter;
class Context {
constructor() {
this.types = {};
this.schemas = new Map();
this.useFilters = false;
}
has(schema) {
return this.schemas.has(schema);
}
get(typeid) {
return this.types[typeid];
}
add(schema, typeid = this.schemas.size) {
// FIXME: move this to somewhere else?
// support inheritance
schema._definition = SchemaDefinition.create(schema._definition);
schema._typeid = typeid;
this.types[typeid] = schema;
this.schemas.set(schema, typeid);
}
static create(options = {}) {
return function (definition) {
if (!options.context) {
options.context = new Context();
}
return type(definition, options);
};
}
}
exports.Context = Context;
exports.globalContext = new Context();
/**
* [See documentation](https://docs.colyseus.io/state/schema/)
*
* Annotate a Schema property to be serializeable.
* \@type()'d fields are automatically flagged as "dirty" for the next patch.
*
* @example Standard usage, with automatic change tracking.
* ```
* \@type("string") propertyName: string;
* ```
*
* @example You can provide the "manual" option if you'd like to manually control your patches via .setDirty().
* ```
* \@type("string", { manual: true })
* ```
*/
function type(type, options = {}) {
return function (target, field) {
const context = options.context || exports.globalContext;
const constructor = target.constructor;
constructor._context = context;
if (!type) {
throw new Error(`${constructor.name}: @type() reference provided for "${field}" is undefined. Make sure you don't have any circular dependencies.`);
}
/*
* static schema
*/
if (!context.has(constructor)) {
context.add(constructor);
}
const definition = constructor._definition;
definition.addField(field, type);
/**
* skip if descriptor already exists for this field (`@deprecated()`)
*/
if (definition.descriptors[field]) {
if (definition.deprecated[field]) {
// do not create accessors for deprecated properties.
return;
}
else {
// trying to define same property multiple times across inheritance.
// https://github.com/colyseus/colyseus-unity3d/issues/131#issuecomment-814308572
try {
throw new Error(`@colyseus/schema: Duplicate '${field}' definition on '${constructor.name}'.\nCheck @type() annotation`);
}
catch (e) {
const definitionAtLine = e.stack.split("\n")[4].trim();
throw new Error(`${e.message} ${definitionAtLine}`);
}
}
}
const isArray = ArraySchema_1.ArraySchema.is(type);
const isMap = !isArray && MapSchema_1.MapSchema.is(type);
// TODO: refactor me.
// Allow abstract intermediary classes with no fields to be serialized
// (See "should support an inheritance with a Schema type without fields" test)
if (typeof (type) !== "string" && !Schema_1.Schema.is(type)) {
const childType = Object.values(type)[0];
if (typeof (childType) !== "string" && !context.has(childType)) {
context.add(childType);
}
}
if (options.manual) {
// do not declare getter/setter descriptor
definition.descriptors[field] = {
enumerable: true,
configurable: true,
writable: true,
};
return;
}
const fieldCached = `_${field}`;
definition.descriptors[fieldCached] = {
enumerable: false,
configurable: false,
writable: true,
};
definition.descriptors[field] = {
get: function () {
return this[fieldCached];
},
set: function (value) {
/**
* Create Proxy for array or map items
*/
// skip if value is the same as cached.
if (value === this[fieldCached]) {
return;
}
if (value !== undefined &&
value !== null) {
// automaticallty transform Array into ArraySchema
if (isArray && !(value instanceof ArraySchema_1.ArraySchema)) {
value = new ArraySchema_1.ArraySchema(...value);
}
// automaticallty transform Map into MapSchema
if (isMap && !(value instanceof MapSchema_1.MapSchema)) {
value = new MapSchema_1.MapSchema(value);
}
// try to turn provided structure into a Proxy
if (value['$proxy'] === undefined) {
if (isMap) {
value = (0, MapSchema_1.getMapProxy)(value);
}
else if (isArray) {
value = (0, ArraySchema_1.getArrayProxy)(value);
}
}
// flag the change for encoding.
this.$changes.change(field);
//
// call setParent() recursively for this and its child
// structures.
//
if (value['$changes']) {
value['$changes'].setParent(this, this.$changes.root, this._definition.indexes[field]);
}
}
else if (this[fieldCached]) {
//
// Setting a field to `null` or `undefined` will delete it.
//
this.$changes.delete(field);
}
this[fieldCached] = value;
},
enumerable: true,
configurable: true
};
};
}
exports.type = type;
/**
* `@filter()` decorator for defining data filters per client
*/
function filter(cb) {
return function (target, field) {
const constructor = target.constructor;
const definition = constructor._definition;
if (definition.addFilter(field, cb)) {
constructor._context.useFilters = true;
}
};
}
exports.filter = filter;
function filterChildren(cb) {
return function (target, field) {
const constructor = target.constructor;
const definition = constructor._definition;
if (definition.addChildrenFilter(field, cb)) {
constructor._context.useFilters = true;
}
};
}
exports.filterChildren = filterChildren;
/**
* `@deprecated()` flag a field as deprecated.
* The previous `@type()` annotation should remain along with this one.
*/
function deprecated(throws = true) {
return function (target, field) {
const constructor = target.constructor;
const definition = constructor._definition;
definition.deprecated[field] = true;
if (throws) {
definition.descriptors[field] = {
get: function () { throw new Error(`${field} is deprecated.`); },
set: function (value) { },
enumerable: false,
configurable: true
};
}
};
}
exports.deprecated = deprecated;
function defineTypes(target, fields, options = {}) {
if (!options.context) {
options.context = target._context || options.context || exports.globalContext;
}
for (let field in fields) {
type(fields[field], options)(target.prototype, field);
}
return target;
}
exports.defineTypes = defineTypes;
//# sourceMappingURL=annotations.js.map