Skip to content
This repository has been archived by the owner on Jun 1, 2022. It is now read-only.

Component: add AbstractComponent to separate Schema-constructable and raw component #251

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions src/Component.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,43 @@ import { PropType } from "./Types";
* Base class for components.
*/

export type ComponentSchemaProp = {
default?: any;
type: PropType<any, any>;
export type ComponentSchemaProp<T> = {
default?: T;
type: PropType<T, T>;
};

export type ComponentSchema = {
[propName: string]: ComponentSchemaProp;
[propName: string]: ComponentSchemaProp<any>;
};

export class Component<C> {
static schema: ComponentSchema;
export type SchemaProperties<Schema> =
Partial<Omit<Schema, keyof Component<Schema>>>;

export class AbstractComponent<Properties> {
static isComponent: true;
constructor(props?: Partial<Omit<C, keyof Component<any>>> | false);
copy(source: this): this;
clone(): this;
static getName(): string;
constructor(props?: Properties);
copy(source: this | Properties): this;
clone(): this
setProperties(props?: Properties): this;
reset(): void;
dispose(): void;
}

export interface ComponentConstructor<C extends Component<any>> {
schema: ComponentSchema;
export class Component<Schema>
extends AbstractComponent<SchemaProperties<Schema>> {
static schema: ComponentSchema;
static isSchemaComponent: true;
constructor(props?: SchemaProperties<Schema>);
}

export interface ComponentConstructor<C extends AbstractComponent<any>> {
isComponent: true;
new (props?: Partial<Omit<C, keyof Component<any>>> | false): C;
new (...args: any): C;
}

export interface SchemaConstructable<C extends Component<C>> {
schema: ComponentSchema;
isSchemaComponent: true;
new (props?: SchemaProperties<C>): C;
}
95 changes: 63 additions & 32 deletions src/Component.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,47 @@
export class Component {
constructor(props) {
if (props !== false) {
const schema = this.constructor.schema;
export class AbstractComponent {
copy() {
if (process.env.NODE_ENV !== "production") {
throw new Error(`'.copy()' method unimplemented`);
}
return this;
}

for (const key in schema) {
if (props && props.hasOwnProperty(key)) {
this[key] = props[key];
} else {
const schemaProp = schema[key];
if (schemaProp.hasOwnProperty("default")) {
this[key] = schemaProp.type.clone(schemaProp.default);
} else {
const type = schemaProp.type;
this[key] = type.clone(type.default);
}
}
}
clone() {
return new this.constructor().copy(this);
}

if (process.env.NODE_ENV !== "production" && props !== undefined) {
this.checkUndefinedAttributes(props);
}
setProperties() {
if (process.env.NODE_ENV !== "production") {
throw new Error(`'.setProperties()' method unimplemented`);
}
return this;
}

reset() {
if (process.env.NODE_ENV !== "production") {
throw new Error(`'.reset()' method unimplemented`);
}
}

dispose() {
if (this._pool) {
this._pool.release(this);
}
}

getName() {
return this.constructor.getName();
}
}
AbstractComponent.getName = function() {
return this.displayName || this.name;
};
AbstractComponent.isComponent = true;

export class Component extends AbstractComponent {
constructor(props) {
super();
this.setProperties(props);
this._pool = null;
}

Expand All @@ -44,8 +64,28 @@ export class Component {
return this;
}

clone() {
return new this.constructor().copy(this);
setProperties(props) {
const schema = this.constructor.schema;

for (const key in schema) {
if (props && props.hasOwnProperty(key)) {
this[key] = props[key];
} else {
const schemaProp = schema[key];
if (schemaProp.hasOwnProperty("default")) {
this[key] = schemaProp.type.clone(schemaProp.default);
} else {
const type = schemaProp.type;
this[key] = type.clone(type.default);
}
}
}

if (process.env.NODE_ENV !== "production" && props !== undefined) {
this.checkUndefinedAttributes(props);
}

return this;
}

reset() {
Expand All @@ -63,12 +103,6 @@ export class Component {
}
}

dispose() {
if (this._pool) {
this._pool.release(this);
}
}

getName() {
return this.constructor.getName();
}
Expand All @@ -88,7 +122,4 @@ export class Component {
}

Component.schema = {};
Component.isComponent = true;
Component.getName = function () {
return this.displayName || this.name;
};
Component.isSchemaComponent = true;
25 changes: 14 additions & 11 deletions src/ComponentManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,26 @@ export class ComponentManager {
}

const schema = Component.schema;
if (schema) {
for (const propName in schema) {
const prop = schema[propName];
if (!prop.type) {
throw new Error(
`Invalid schema for component "${Component.getName()}". Missing type for "${propName}" property.`
);
}
}
}

if (!schema) {
console.log(Component.getName());
console.log(Component.isSchemaComponent);

if (Component.isSchemaComponent && !schema) {
throw new Error(
`Component "${Component.getName()}" has no schema property.`
);
}

for (const propName in schema) {
const prop = schema[propName];

if (!prop.type) {
throw new Error(
`Invalid schema for component "${Component.getName()}". Missing type for "${propName}" property.`
);
}
}

Component._typeId = this.nextComponentId++;
this.Components.push(Component);
this._ComponentsMap[Component._typeId] = Component;
Expand Down
12 changes: 6 additions & 6 deletions src/Entity.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ export class Entity {
* @param Component Type of component to get
* @param includeRemoved Whether a component that is staled to be removed should be also considered
*/
getComponent?<C extends Component<any>>(
getComponent<C extends Component<any>>(
Component: ComponentConstructor<C>,
includeRemoved?: boolean
): Readonly<C>;
): Readonly<C> | undefined;

/**
* Get a component that is slated to be removed from this entity.
*/
getRemovedComponent?<C extends Component<any>>(
getRemovedComponent<C extends Component<any>>(
Component: ComponentConstructor<C>
): Readonly<C>;
): Readonly<C> | undefined;

/**
* Get an object containing all the components on this entity, where the object keys are the component types.
Expand All @@ -50,9 +50,9 @@ export class Entity {
* Get a mutable reference to a component on this entity.
* @param Component Type of component to get
*/
getMutableComponent?<C extends Component<any>>(
getMutableComponent<C extends Component<any>>(
Component: ComponentConstructor<C>
): C;
): C | undefined;

/**
* Add a component to the entity.
Expand Down
12 changes: 9 additions & 3 deletions src/TagComponent.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { Component } from "./Component";
import { AbstractComponent } from "./Component";

export class TagComponent extends Component {
export class TagComponent extends AbstractComponent {
constructor() {
super(false);
super();
}

copy() {}

reset() {}

setProperties() {}
}

TagComponent.isTagComponent = true;