From 93560130eba540b9f40e2560df740fb6c14847c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2020 22:32:21 +0000 Subject: [PATCH 01/56] Bump lodash from 3.10.1 to 4.17.19 Bumps [lodash](https://github.com/lodash/lodash) from 3.10.1 to 4.17.19. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/3.10.1...4.17.19) Signed-off-by: dependabot[bot] --- package-lock.json | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4c23f469..bea3c9ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2275,11 +2275,6 @@ "pump": "^3.0.0" } }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" - }, "lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -6520,9 +6515,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "lodash.zip": { From 0e4a0fb7e1634caef9517ad7b380edd9dd9cfe5e Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Fri, 24 Jul 2020 17:59:35 +0100 Subject: [PATCH 02/56] Fix and enable Entity tests --- test/unit/entity.test.js | 41 +++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/test/unit/entity.test.js b/test/unit/entity.test.js index a233d113..285c5465 100644 --- a/test/unit/entity.test.js +++ b/test/unit/entity.test.js @@ -19,7 +19,10 @@ test("adding/removing components sync", async t => { t.is(entity.getComponentTypes().length, 1); t.true(entity.hasComponent(FooComponent)); t.false(entity.hasComponent(BarComponent)); - t.deepEqual(Object.keys(entity.getComponents()), ["FooComponent"]); + t.deepEqual( + Object.values(entity.getComponents()).map(comp => comp.constructor), + [FooComponent] + ); // Entity doesn't contain BarComponent t.false(entity.hasAllComponents([FooComponent, BarComponent])); @@ -29,16 +32,20 @@ test("adding/removing components sync", async t => { t.true(entity.hasComponent(FooComponent)); t.true(entity.hasComponent(BarComponent)); t.true(entity.hasAllComponents([FooComponent, BarComponent])); - t.deepEqual(Object.keys(entity.getComponents()), [ - "FooComponent", - "BarComponent" - ]); + t.deepEqual( + Object.values(entity.getComponents()).map(comp => comp.constructor), + [FooComponent, BarComponent] + ); + entity.removeComponent(FooComponent, true); t.is(entity.getComponentTypes().length, 1); t.false(entity.hasComponent(FooComponent)); t.true(entity.hasComponent(BarComponent)); t.false(entity.hasAllComponents([FooComponent, BarComponent])); - t.deepEqual(Object.keys(entity.getComponents()), ["BarComponent"]); + t.deepEqual( + Object.values(entity.getComponents()).map(comp => comp.constructor), + [BarComponent] + ); entity.addComponent(FooComponent); entity.removeAllComponents(true); @@ -46,10 +53,13 @@ test("adding/removing components sync", async t => { t.false(entity.hasComponent(FooComponent)); t.false(entity.hasComponent(BarComponent)); t.false(entity.hasAllComponents([FooComponent, BarComponent])); - t.deepEqual(Object.keys(entity.getComponents()), []); + t.deepEqual( + Object.values(entity.getComponents()).map(comp => comp.constructor), + [] + ); }); -test.only("clearing pooled components", async t => { +test("clearing pooled components", async t => { var world, entity; // Component with no constructor @@ -151,13 +161,22 @@ test("removing components deferred", async t => { t.false(entity.hasComponent(FooComponent)); t.false(entity.hasComponent(FooComponent)); t.false(entity.hasComponent(BarComponent)); - t.deepEqual(Object.keys(entity.getComponents()), []); - t.deepEqual(Object.keys(entity.getComponentsToRemove()), ["FooComponent"]); + t.deepEqual( + Object.values(entity.getComponents()).map(comp => comp.constructor), + [] + ); + t.deepEqual( + Object.values(entity.getComponentsToRemove()).map(comp => comp.constructor), + [FooComponent] + ); world.entityManager.processDeferredRemoval(); t.is(entity.getComponentTypes().length, 0); t.false(entity.hasComponent(FooComponent)); - t.deepEqual(Object.keys(entity.getComponents()), []); + t.deepEqual( + Object.values(entity.getComponents()).map(comp => comp.constructor), + [] + ); }); test("remove entity", async t => { From 13d6541f80f2cb258e662153ff3dddd9901f0c9d Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Fri, 24 Jul 2020 18:58:36 +0100 Subject: [PATCH 03/56] Add @rollup/plugin-replace --- package-lock.json | 25 +++++++++++++++++++++++++ package.json | 1 + 2 files changed, 26 insertions(+) diff --git a/package-lock.json b/package-lock.json index 364cbefc..0182a7fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -836,6 +836,16 @@ } } }, + "@rollup/plugin-replace": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.3.3.tgz", + "integrity": "sha512-XPmVXZ7IlaoWaJLkSCDaa0Y6uVo5XQYHhiMFzOd5qSv5rE+t/UJToPIOE56flKIxBFQI27ONsxb7dqHnwSsjKQ==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.0.8", + "magic-string": "^0.25.5" + } + }, "@rollup/pluginutils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", @@ -6589,6 +6599,15 @@ "integrity": "sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg==", "dev": true }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -9874,6 +9893,12 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, "spawn-command": { "version": "0.0.2-1", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", diff --git a/package.json b/package.json index 087ff900..5a57eb36 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@babel/core": "^7.10.2", "@babel/plugin-transform-modules-commonjs": "^7.10.1", "@rollup/plugin-node-resolve": "^8.0.1", + "@rollup/plugin-replace": "^2.3.3", "ava": "^3.9.0", "babel-eslint": "^10.0.3", "benchmarker-js": "0.0.3", From 401f5446e057752643d7046600bf5c93e9e73738 Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Fri, 24 Jul 2020 18:59:59 +0100 Subject: [PATCH 04/56] Use replace in Rollup --- rollup.config.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index a9a8a1bb..caff24d2 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,11 +1,17 @@ import json from "rollup-plugin-json"; import resolve from "@rollup/plugin-node-resolve"; import { terser } from "rollup-plugin-terser"; +import replace from "@rollup/plugin-replace"; export default [ { input: "src/index.js", - plugins: [json({ exclude: ["node_modules/**"] })], + plugins: [ + replace({ + _DEBUG_: true + }), + json({ exclude: ["node_modules/**"] }) + ], output: [ { format: "umd", @@ -35,7 +41,13 @@ export default [ }, { input: "src/index.js", - plugins: [json({ exclude: ["node_modules/**"] }), terser()], + plugins: [ + replace({ + _DEBUG_: false + }), + json({ exclude: ["node_modules/**"] }), + terser() + ], output: [ { format: "umd", From d00051bbe1a2a8f9ed5f4f6a08b68519fc534dc2 Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Fri, 24 Jul 2020 19:00:28 +0100 Subject: [PATCH 05/56] Switch to using NODE_ENV --- rollup.config.js | 6 ++++-- site/docs/manual/Architecture.md | 2 +- src/Entity.js | 7 +++---- src/EntityManager.js | 13 +++++++------ 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index caff24d2..ed3938a3 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -8,7 +8,8 @@ export default [ input: "src/index.js", plugins: [ replace({ - _DEBUG_: true + "process.env.NODE_ENV": JSON.stringify("development"), + delimiters: ["", ""] }), json({ exclude: ["node_modules/**"] }) ], @@ -43,7 +44,8 @@ export default [ input: "src/index.js", plugins: [ replace({ - _DEBUG_: false + "process.env.NODE_ENV": JSON.stringify("production"), + delimiters: ["", ""] }), json({ exclude: ["node_modules/**"] }), terser() diff --git a/site/docs/manual/Architecture.md b/site/docs/manual/Architecture.md index 249123e7..04e28691 100644 --- a/site/docs/manual/Architecture.md +++ b/site/docs/manual/Architecture.md @@ -393,7 +393,7 @@ Components can be accessed from an entity in two ways: - `getComponent(Component)`: Get the component for read only operations. - `getMutableComponent(Component)`: Get the component to modify its values. -If `DEBUG` mode is enabled it will throw an error if you try to modify a component accessed by `getComponent`, but that error will not be thrown on release mode because of performance reasons. +If `development` mode is enabled it will throw an error if you try to modify a component accessed by `getComponent`, but that error will not be thrown on release mode because of performance reasons. These two access modes help to implement `reactive queries`([more info](/manual/Architecture?id=reactive-queries)), which are basically lists of entities populated with components that have mutated somehow, without much overhead on the execution as we avoid using custom setters or proxies. This means every time you request a mutable component, it will get marked as modified and systems listening for that will get notified accordingly. diff --git a/src/Entity.js b/src/Entity.js index b7c8b030..2f9c4057 100644 --- a/src/Entity.js +++ b/src/Entity.js @@ -1,9 +1,6 @@ import Query from "./Query.js"; import wrapImmutableComponent from "./WrapImmutableComponent.js"; -// @todo Take this out from there or use ENV -const DEBUG = false; - export class Entity { constructor(entityManager) { this._entityManager = entityManager || null; @@ -40,7 +37,9 @@ export class Entity { component = this._componentsToRemove[Component._typeId]; } - return DEBUG ? wrapImmutableComponent(Component, component) : component; + return process.env.NODE_ENV !== "production" + ? wrapImmutableComponent(Component, component) + : component; } getRemovedComponent(Component) { diff --git a/src/EntityManager.js b/src/EntityManager.js index 147f6daf..272426da 100644 --- a/src/EntityManager.js +++ b/src/EntityManager.js @@ -96,12 +96,13 @@ export class EntityManager { } if (~entity._ComponentTypes.indexOf(Component)) { - // @todo Just on debug mode - console.warn( - "Component type already exists on entity.", - entity, - Component.getName() - ); + if (process.env.NODE_ENV !== "production") { + console.warn( + "Component type already exists on entity.", + entity, + Component.getName() + ); + } return; } From b1533e36a0827bec58d46c213e69850d3c3c9cb8 Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Sat, 25 Jul 2020 17:05:00 +0100 Subject: [PATCH 06/56] Add docs about development mode --- site/docs/manual/Architecture.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/site/docs/manual/Architecture.md b/site/docs/manual/Architecture.md index 04e28691..80fc2b06 100644 --- a/site/docs/manual/Architecture.md +++ b/site/docs/manual/Architecture.md @@ -40,6 +40,13 @@ And finally we define the systems that will add the logic to the game: ![Wolves and dragons example](https://ecsy.io/docs/manual/images/dragons.svg) +## Debug mode +ECSY will output some debug messages when in development mode. Development mode is active depending on the environment you are running ECSY in. + +In CommonJS environments it is controlled by the value of the `NODE_ENV` environment variable. This means Webpack and similar tools can change the value for development and production builds. This ensures you get helpful messages during development and a smaller bundle size in production. + +When using the UMD or ES Module builds then the unminified builds will have development mode on and the minified builds will have it turned off. + ## World By default your application should have at least one `world`. A world is basically a container for `entities`, `components` and `systems`. Even so, you can have multiple worlds running at the same time and enable or disable them as you need. [API Reference](/api/classes/world). From f5715446bcf01ec57f5246875441d720ce0be454 Mon Sep 17 00:00:00 2001 From: Scott Bailey Date: Sat, 25 Jul 2020 13:51:47 -0700 Subject: [PATCH 07/56] Update World.getSystem() typescript type to return the requested System type instead of System. --- src/World.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/World.d.ts b/src/World.d.ts index 699e89a3..a9c3938c 100644 --- a/src/World.d.ts +++ b/src/World.d.ts @@ -44,7 +44,7 @@ export class World { * Get a system registered in this world. * @param System Type of system to get. */ - getSystem(System: SystemConstructor): System; + getSystem(System: SystemConstructor): S; /** * Get a list of systems registered in this world. From 758c668ab7ea9c95d1c5d0cf95f1929a552aeb6e Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Tue, 23 Jun 2020 01:32:23 +0200 Subject: [PATCH 08/56] Check component attributes not defined on schema --- src/Component.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Component.js b/src/Component.js index a0b2c35b..8b22dc33 100644 --- a/src/Component.js +++ b/src/Component.js @@ -32,6 +32,16 @@ export class Component { } } + // @todo DEBUG + // Check that the attributes defined in source are also defined in the schema + Object.keys(source).forEach(srcKey => { + if (!schema.hasOwnProperty(srcKey)) { + console.warn( + `Trying to set attribute '${srcKey}' not defined in the '${this.constructor.name}' schema.` + ); + } + }); + return this; } From 72451da43380b2042b755ce79f2ad0a2cdbff646 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Sat, 4 Jul 2020 00:21:51 +0200 Subject: [PATCH 09/56] checkUndefinedAttributes on schema --- src/Component.js | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Component.js b/src/Component.js index 8b22dc33..cd8b0829 100644 --- a/src/Component.js +++ b/src/Component.js @@ -16,6 +16,9 @@ export class Component { } } } + + // @DEBUG + this.checkUndefinedAttributes(props); } this._pool = null; @@ -32,15 +35,8 @@ export class Component { } } - // @todo DEBUG - // Check that the attributes defined in source are also defined in the schema - Object.keys(source).forEach(srcKey => { - if (!schema.hasOwnProperty(srcKey)) { - console.warn( - `Trying to set attribute '${srcKey}' not defined in the '${this.constructor.name}' schema.` - ); - } - }); + // @DEBUG + this.checkUndefinedAttributes(source); return this; } @@ -73,6 +69,19 @@ export class Component { getName() { return this.constructor.getName(); } + + checkUndefinedAttributes(src) { + const schema = this.constructor.schema; + + // Check that the attributes defined in source are also defined in the schema + Object.keys(src).forEach(srcKey => { + if (!schema.hasOwnProperty(srcKey)) { + console.warn( + `Trying to set attribute '${srcKey}' not defined in the '${this.constructor.name}' schema. Please fix the schema, the attribute value won't be set` + ); + } + }); + } } Component.schema = {}; From fe0bef1cb648693e61d6ef73674d157d19026c21 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Sat, 4 Jul 2020 00:41:20 +0200 Subject: [PATCH 10/56] Fix tests --- src/Component.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Component.js b/src/Component.js index cd8b0829..06893e24 100644 --- a/src/Component.js +++ b/src/Component.js @@ -18,7 +18,9 @@ export class Component { } // @DEBUG - this.checkUndefinedAttributes(props); + if (props !== undefined) { + this.checkUndefinedAttributes(props); + } } this._pool = null; From 883cfb39d83a3f0e34cb3bbe8fea48780dd7266e Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Thu, 30 Jul 2020 00:07:44 +0200 Subject: [PATCH 11/56] Add production check in checkUndefinedAttributes --- src/Component.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Component.js b/src/Component.js index 06893e24..7a4e336f 100644 --- a/src/Component.js +++ b/src/Component.js @@ -17,8 +17,7 @@ export class Component { } } - // @DEBUG - if (props !== undefined) { + if (process.env.NODE_ENV !== "production" && props !== undefined) { this.checkUndefinedAttributes(props); } } @@ -38,7 +37,9 @@ export class Component { } // @DEBUG - this.checkUndefinedAttributes(source); + if (process.env.NODE_ENV !== "production") { + this.checkUndefinedAttributes(source); + } return this; } From 2a6521234c22c1b02ed0bc327cc00f6bd7d75796 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2020 22:32:21 +0000 Subject: [PATCH 12/56] Bump lodash from 3.10.1 to 4.17.19 Bumps [lodash](https://github.com/lodash/lodash) from 3.10.1 to 4.17.19. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/3.10.1...4.17.19) Signed-off-by: dependabot[bot] --- package-lock.json | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index e80fe23e..364cbefc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2275,11 +2275,6 @@ "pump": "^3.0.0" } }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" - }, "lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -6520,9 +6515,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "lodash.zip": { From fe6dc11d5cb752e7481b82894ecf963c8c323661 Mon Sep 17 00:00:00 2001 From: Scott Bailey Date: Sun, 19 Jul 2020 14:10:21 -0700 Subject: [PATCH 13/56] Updates queryKey to use the Component typeId. Adds a regression test. Note that this adds an implict dependency that components are registered before systems, so alternatives should still be investigated. --- src/Utils.js | 4 ++-- test/unit/Queries.test.js | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/Utils.js b/src/Utils.js index 0e98229c..846525f6 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -27,9 +27,9 @@ export function queryKey(Components) { var T = Components[n]; if (typeof T === "object") { var operator = T.operator === "not" ? "!" : T.operator; - names.push(operator + getName(T.Component)); + names.push(operator + T.Component._typeId); } else { - names.push(getName(T)); + names.push(T._typeId); } } diff --git a/test/unit/Queries.test.js b/test/unit/Queries.test.js index e072ad9a..787cff48 100644 --- a/test/unit/Queries.test.js +++ b/test/unit/Queries.test.js @@ -1,5 +1,5 @@ import test from "ava"; -import { World, System, Not } from "../../src/index.js"; +import { World, System, Not, Component } from "../../src/index.js"; import { FooComponent, BarComponent } from "../helpers/components"; function queriesLength(queries) { @@ -209,3 +209,39 @@ test("Entity living just within the frame", t => { results: 0 }); }); + +test("Two components with the same name get unique queries", t => { + const world = new World(); + + // Create two components that have the same name. + function createComponentClass() { + return class TestComponent extends Component {}; + } + const Component1 = createComponentClass(); + const Component2 = createComponentClass(); + world.registerComponent(Component1); + world.registerComponent(Component2); + t.is(Component1.name, Component2.name); + + // Create an entity for each component. + const entity1 = world.createEntity().addComponent(Component1); + const entity2 = world.createEntity().addComponent(Component2); + + // Define two queries, one for each entity. + class SystemTest extends System { + execute() {} + } + SystemTest.queries = { + comp1: { components: [Component1] }, + comp2: { components: [Component2] }, + }; + world.registerSystem(SystemTest); + + // Verify that the query system can identify them as unique components. + const system = world.systemManager.getSystem(SystemTest); + const query1Entity = system.queries.comp1.results[0]; + const query2Entity = system.queries.comp2.results[0]; + + t.is(query1Entity.id, entity1.id); + t.is(query2Entity.id, entity2.id); +}); From 91d6851ee669d7a28e9a789c378663c5b11a0323 Mon Sep 17 00:00:00 2001 From: Scott Bailey Date: Sun, 19 Jul 2020 14:42:42 -0700 Subject: [PATCH 14/56] Add a simple test for _typeId --- test/unit/Component.test.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/unit/Component.test.js b/test/unit/Component.test.js index 2f1628db..7b19a1b7 100644 --- a/test/unit/Component.test.js +++ b/test/unit/Component.test.js @@ -1,4 +1,5 @@ import test from "ava"; +import { World } from "../../src/World"; import { Component } from "../../src/Component"; import { createType, @@ -124,3 +125,23 @@ test("clone component", t => { t.is(destComponent.jsonWithDefault.value, "test 5"); t.true(new Vector3(7, 8, 9).equals(destComponent.vector3WithDefault)); }); + +test("unique type ids", t => { + class ComponentA extends Component {} + class ComponentB extends Component {} + + t.assert(ComponentA._typeId === undefined); + t.assert(ComponentB._typeId === undefined); + + let world = new World(); + world.registerComponent(ComponentA).registerComponent(ComponentB); + + t.assert(ComponentA._typeId !== undefined); + t.assert(ComponentB._typeId !== undefined); + + // Verify unique between components. + t.not(ComponentA._typeId, ComponentB._typeId); + + // Verify multiple calls return the same id. + t.is(ComponentA._typeId, ComponentA._typeId); +}); From 9c7af44297e9015cc6a3ba6df05c7ef5ae31b611 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Thu, 30 Jul 2020 00:54:27 +0200 Subject: [PATCH 15/56] Change order on the three example --- site/examples/ball-example/three/index.html | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/site/examples/ball-example/three/index.html b/site/examples/ball-example/three/index.html index 3380e664..fa3086e2 100644 --- a/site/examples/ball-example/three/index.html +++ b/site/examples/ball-example/three/index.html @@ -24,14 +24,6 @@ var world = new World(); - world - .registerSystem(RotatingSystem) - .registerSystem(PulsatingColorSystem) - .registerSystem(PulsatingScaleSystem) - .registerSystem(TimeoutSystem) - .registerSystem(ColliderSystem) - .registerSystem(MovingSystem); - world .registerComponent(Object3D) .registerComponent(Collidable) @@ -44,6 +36,14 @@ .registerComponent(Colliding) .registerComponent(Rotating); + world + .registerSystem(RotatingSystem) + .registerSystem(PulsatingColorSystem) + .registerSystem(PulsatingScaleSystem) + .registerSystem(TimeoutSystem) + .registerSystem(ColliderSystem) + .registerSystem(MovingSystem); + var camera, scene, renderer, parent; var clock = new THREE.Clock(); @@ -104,7 +104,7 @@ var geometry = new THREE.BoxBufferGeometry( size, size, size ); - for (var i = 0;i < numObjects; i++) { + for (var i = 0; i < numObjects; i++) { var material = new THREE.MeshStandardMaterial({color: new THREE.Color(1,0,0)}); var mesh = new THREE.Mesh( geometry, material ); From c44b97a7ea04d863f71eaa1fa01c8ff96fb28542 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Thu, 30 Jul 2020 01:57:58 +0200 Subject: [PATCH 16/56] Add check for Components registered before registering a system --- src/System.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/System.js b/src/System.js index c2cfc734..15c8718d 100644 --- a/src/System.js +++ b/src/System.js @@ -46,7 +46,24 @@ export class System { if (!Components || Components.length === 0) { throw new Error("'components' attribute can't be empty in a query"); } + + // Detect if the components have already been registered + let unregisteredComponents = Components.filter( + Component => Component._typeId === undefined + ); + + if (unregisteredComponents.length > 0) { + throw new Error( + `Trying to create a query '${ + this.constructor.name + }.${queryName}' with unregistered components: [${unregisteredComponents + .map(c => c.getName()) + .join(", ")}]` + ); + } + var query = this.world.entityManager.queryComponents(Components); + this._queries[queryName] = query; if (queryConfig.mandatory === true) { this._mandatoryQueries.push(query); From 392fb3fe213d040617513a79eca73766db6524b0 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Thu, 30 Jul 2020 03:18:13 +0200 Subject: [PATCH 17/56] Fix ball example --- site/examples/ball-example/three/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/examples/ball-example/three/index.html b/site/examples/ball-example/three/index.html index fa3086e2..0dd52335 100644 --- a/site/examples/ball-example/three/index.html +++ b/site/examples/ball-example/three/index.html @@ -104,7 +104,7 @@ var geometry = new THREE.BoxBufferGeometry( size, size, size ); - for (var i = 0; i < numObjects; i++) { + for (var i = 0;i < numObjects; i++) { var material = new THREE.MeshStandardMaterial({color: new THREE.Color(1,0,0)}); var mesh = new THREE.Mesh( geometry, material ); From 95c93e5d1cb700a2bc41536c68186c7ab4c25a89 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Thu, 30 Jul 2020 03:18:34 +0200 Subject: [PATCH 18/56] Introduce throw when creating a query or registering a system with unregistered components --- src/System.js | 5 +++-- src/Utils.js | 12 ++++++++++++ test/unit/Component.test.js | 29 +++++++++++++++++++++++++++++ test/unit/system.test.js | 1 + 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/System.js b/src/System.js index 15c8718d..eecf8a1c 100644 --- a/src/System.js +++ b/src/System.js @@ -1,4 +1,5 @@ import Query from "./Query.js"; +import { componentRegistered } from "./Utils.js"; export class System { canExecute() { @@ -49,12 +50,12 @@ export class System { // Detect if the components have already been registered let unregisteredComponents = Components.filter( - Component => Component._typeId === undefined + Component => !componentRegistered(Component) ); if (unregisteredComponents.length > 0) { throw new Error( - `Trying to create a query '${ + `Tried to create a query '${ this.constructor.name }.${queryName}' with unregistered components: [${unregisteredComponents .map(c => c.getName()) diff --git a/src/Utils.js b/src/Utils.js index 846525f6..08629bd0 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -25,6 +25,11 @@ export function queryKey(Components) { var names = []; for (var n = 0; n < Components.length; n++) { var T = Components[n]; + + if (!componentRegistered(T)) { + throw new Error(`Tried to create a query with an unregistered component`); + } + if (typeof T === "object") { var operator = T.operator === "not" ? "!" : T.operator; names.push(operator + T.Component._typeId); @@ -44,3 +49,10 @@ export const now = hasWindow && typeof window.performance !== "undefined" ? performance.now.bind(performance) : Date.now.bind(Date); + +export function componentRegistered(T) { + return ( + (typeof T === "object" && T.Component._typeId !== undefined) || + (T.isComponent && T._typeId !== undefined) + ); +} diff --git a/test/unit/Component.test.js b/test/unit/Component.test.js index 7b19a1b7..8aa9f602 100644 --- a/test/unit/Component.test.js +++ b/test/unit/Component.test.js @@ -1,5 +1,6 @@ import test from "ava"; import { World } from "../../src/World"; +import { System } from "../../src/System"; import { Component } from "../../src/Component"; import { createType, @@ -145,3 +146,31 @@ test("unique type ids", t => { // Verify multiple calls return the same id. t.is(ComponentA._typeId, ComponentA._typeId); }); + +test("registering components before systems", t => { + class ComponentA extends Component {} + class ComponentB extends Component {} + + class SystemA extends System {} + SystemA.queries = { S: { components: [ComponentA, ComponentB] } }; + + let world = new World(); + + const error1 = t.throws(() => { + world.registerSystem(SystemA); + }); + t.is( + error1.message, + "Tried to create a query 'SystemA.S' with unregistered components: [ComponentA, ComponentB]" + ); + + world.registerComponent(ComponentA); + + const error2 = t.throws(() => { + world.registerSystem(SystemA); + }); + t.is( + error2.message, + "Tried to create a query 'SystemA.S' with unregistered components: [ComponentB]" + ); +}); diff --git a/test/unit/system.test.js b/test/unit/system.test.js index 4cf0d7f1..cdd44266 100644 --- a/test/unit/system.test.js +++ b/test/unit/system.test.js @@ -170,6 +170,7 @@ test("Queries with 'Not' operator", t => { }; const error = t.throws(() => { + debugger; world.registerSystem(SystemNotNot); }); From 3cbbac8f92e2e5960b49a2c0e881e86e10e35aa8 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Thu, 30 Jul 2020 03:19:32 +0200 Subject: [PATCH 19/56] Remove debugger --- test/unit/system.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit/system.test.js b/test/unit/system.test.js index cdd44266..4cf0d7f1 100644 --- a/test/unit/system.test.js +++ b/test/unit/system.test.js @@ -170,7 +170,6 @@ test("Queries with 'Not' operator", t => { }; const error = t.throws(() => { - debugger; world.registerSystem(SystemNotNot); }); From be8455fb99dbd87e37961c69d27d318ef8ad2da3 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Thu, 30 Jul 2020 12:24:16 +0200 Subject: [PATCH 20/56] Fix linter --- test/unit/Queries.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/Queries.test.js b/test/unit/Queries.test.js index 787cff48..7303630d 100644 --- a/test/unit/Queries.test.js +++ b/test/unit/Queries.test.js @@ -233,7 +233,7 @@ test("Two components with the same name get unique queries", t => { } SystemTest.queries = { comp1: { components: [Component1] }, - comp2: { components: [Component2] }, + comp2: { components: [Component2] } }; world.registerSystem(SystemTest); From e534c9572ed3a973e38a404106ffd73e457471eb Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Thu, 30 Jul 2020 12:29:22 +0200 Subject: [PATCH 21/56] Change elapsed time and delta if no values are provided --- src/World.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/World.js b/src/World.js index 4f594c68..0d5e1383 100644 --- a/src/World.js +++ b/src/World.js @@ -29,7 +29,7 @@ export class World { window.dispatchEvent(event); } - this.lastTime = now(); + this.lastTime = now() / 1000; } registerComponent(Component, objectPool) { @@ -57,7 +57,7 @@ export class World { execute(delta, time) { if (!delta) { - time = now(); + time = now() / 1000; delta = time - this.lastTime; this.lastTime = time; } From 3a09e1d5a0ac02b28ab310cd95ba3939125e97b3 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Thu, 30 Jul 2020 14:10:40 +0200 Subject: [PATCH 22/56] Add world.stats test --- src/EntityManager.js | 2 +- src/World.js | 2 +- test/unit/stats.test.js | 73 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 test/unit/stats.test.js diff --git a/src/EntityManager.js b/src/EntityManager.js index 272426da..2af88564 100644 --- a/src/EntityManager.js +++ b/src/EntityManager.js @@ -299,7 +299,7 @@ export class EntityManager { for (var ecsyComponentId in this.componentsManager._componentPool) { var pool = this.componentsManager._componentPool[ecsyComponentId]; - stats.componentPool[ecsyComponentId] = { + stats.componentPool[pool.T.getName()] = { used: pool.totalUsed(), size: pool.count }; diff --git a/src/World.js b/src/World.js index 0d5e1383..20ff7f46 100644 --- a/src/World.js +++ b/src/World.js @@ -86,6 +86,6 @@ export class World { system: this.systemManager.stats() }; - console.log(JSON.stringify(stats, null, 2)); + return stats; } } diff --git a/test/unit/stats.test.js b/test/unit/stats.test.js new file mode 100644 index 00000000..3fb5709f --- /dev/null +++ b/test/unit/stats.test.js @@ -0,0 +1,73 @@ +import test from "ava"; +import { World, System } from "../../src/index.js"; +import { FooComponent, BarComponent } from "../helpers/components"; + +test("Stats", async t => { + var world = new World(); + + class SystemA extends System {} + SystemA.queries = { + compFoo: { components: [FooComponent] }, + compBar: { components: [BarComponent] }, + compBtoh: { components: [FooComponent, BarComponent] } + }; + + world + .registerComponent(FooComponent) + .registerComponent(BarComponent) + .registerSystem(SystemA); + + // Add a new component and check it exist + for (var i = 0; i < 10; i++) { + let entity = world.createEntity(); + entity.addComponent(FooComponent); + if (i > 5) { + entity.addComponent(BarComponent); + } + } + + t.deepEqual(world.stats(), { + entities: { + numEntities: 10, + numQueries: 3, + queries: { + FooComponent: { + numComponents: 1, + numEntities: 10 + }, + BarComponent: { + numComponents: 1, + numEntities: 4 + }, + "BarComponent-FooComponent": { + numComponents: 2, + numEntities: 4 + } + }, + numComponentPool: 2, + componentPool: { + FooComponent: { + used: 10, + size: 12 + }, + BarComponent: { + used: 4, + size: 5 + } + }, + eventDispatcher: { + fired: 24, + handled: 0 + } + }, + system: { + numSystems: 1, + systems: { + SystemA: { + queries: {}, + executeTime: 0 + } + } + } + }); +}); From 696c8b459954c896dde770617ae7694148a0f2c3 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Thu, 30 Jul 2020 20:31:24 +0200 Subject: [PATCH 23/56] Fixes #165, added missing docs --- site/docs/manual/Architecture.md | 93 ++++++++++++++++++++++++++--- site/docs/manual/Getting-started.md | 7 ++- 2 files changed, 89 insertions(+), 11 deletions(-) diff --git a/site/docs/manual/Architecture.md b/site/docs/manual/Architecture.md index 80fc2b06..1791bcd4 100644 --- a/site/docs/manual/Architecture.md +++ b/site/docs/manual/Architecture.md @@ -40,13 +40,6 @@ And finally we define the systems that will add the logic to the game: ![Wolves and dragons example](https://ecsy.io/docs/manual/images/dragons.svg) -## Debug mode -ECSY will output some debug messages when in development mode. Development mode is active depending on the environment you are running ECSY in. - -In CommonJS environments it is controlled by the value of the `NODE_ENV` environment variable. This means Webpack and similar tools can change the value for development and production builds. This ensures you get helpful messages during development and a smaller bundle size in production. - -When using the UMD or ES Module builds then the unminified builds will have development mode on and the minified builds will have it turned off. - ## World By default your application should have at least one `world`. A world is basically a container for `entities`, `components` and `systems`. Even so, you can have multiple worlds running at the same time and enable or disable them as you need. [API Reference](/api/classes/world). @@ -55,6 +48,15 @@ By default your application should have at least one `world`. A world is basical world = new World(); ``` +The `World` constructor accept an option object with the following parameters: +- ***entityClass***: Provide the base class for entities that implements or extends `Entity`. +- ***entityPoolSize***: Define the initial entity pool size for entities. It can help to avoid GC during execution if the application keep extending the pool dynamically at execution time. + +```javascript +// We know we will have around 10k enemies in our game initially so let's reserve that size initially instead of keep extending the pool as we need more entities +world = new World({ entityPoolSize: 10000 }); +``` + ## Components A `Component` ([API Reference](/api/classes/component)) is an object that can store data but should have not behaviour (As that should be handled by systems). There is not a mandatory way to define a component. @@ -515,11 +517,41 @@ class SystemName extends System { If there is a `reactive query` (A query that *listens* for entities added or removed to it or which components has changed, [more info](/manual/Architecture?id=reactive-queries)) on the list of queries defined by a system, this system is called `reactive system` as it will react to changes on the entities and its components. +It's important to notice that if you plan to mutate the results of a query while you are iterating it (eg: adding or removing components that will make not match the query structure anymore, or removing the entity itself) you should traverse the results in reverse order: +```javascript +let results = this.queries.queryA.results; +for (var i = 0; i < results.length; i++) { + let entity = results[i]; + if (i === 1) { + // This will cause the results list to mutate and results.length will be decremented and you won't reach the end elements in there. + entity.remove(); + } +} + +// The correct way to do it +let results = this.queries.queryA.results; +for (var i = 0; i < results.length; i++) { + let entity = results[i]; + if (i === 1) { + // This will modify the length of the results but as we are moving backward it won't affect us + entity.remove(); + } +} +``` + ### Registering a system Systems should be registered in a world in order to initialize them and add them to the default scheduler that will execute them on each frame. ```javascript -world.registerSystem(SystemName); +world.registerSystem(SystemClass); +``` + +### Unregistering a system + +Systems can be unregistered, and they will get removed from the execution queue and the world. So if you want to use them again you need to register them again. +If you just want to temporaly disable its execution, you must use `System.stop()/play()` instead. +```javascript +world.unregisterSystem(SystemClass); ``` ### Execution order @@ -792,3 +824,48 @@ But if `SystemB` does the same, just `SystemC` will be able to react to it, as t Because of that is important that you define an appropriate execution order based on the needs for your reactive systems. There is one special use case when removing components and entities. When using `System State Components` they should be removed explicitly and they will not get removed if `entity.remove` is being called. [More info](/manual/Architecture?id=system-state-components) + +## Extending core functionality + +It is possible to provide a custom `Entity` class to modify the default behaviour. +To do so you need to import `_Entity` from `ecsy` and extends it in your class definition: +```javascript +import { _Entity, World } from "entity"; + +class MyEntity extends _Entity { + customMethod() {} +} + +// Use the new entity class +let world = new World({ entityClass: MyEntity }); +let entity = world.createEntity(); + +// Call our custom method on our entity class +entity.customMethod(); +``` +You can see an example of this extensibility in `ecsy-three`. In `ecsy-three` we extend both the entity class: https://github.com/MozillaReality/ecsy-three/blob/dev/src/core/entity.js and also the world class: https://github.com/MozillaReality/ecsy-three/blob/dev/src/core/world.js + +## Developing + +### Debug mode +ECSY will output some debug messages when in development mode. Development mode is active depending on the environment you are running ECSY in. + +In CommonJS environments it is controlled by the value of the `NODE_ENV` environment variable. This means Webpack and similar tools can change the value for development and production builds. This ensures you get helpful messages during development and a smaller bundle size in production. + +When using the UMD or ES Module builds then the unminified builds will have development mode on and the minified builds will have it turned off. + +### Benchmarks + +ECSY includes benchmarks (https://github.com/MozillaReality/ecsy/tree/dev/benchmarks) to test the performance and detect regressions. +To run the benchmarks locally you need to execute `npm run benchmarks`: +It will dump a JSON with the results of all the benchmarks and it will write a `benchmark_result.json` file. + +You can use that file to compare against other executions by using `benchmarker` (`https://github.com/fernandojsg/benchmarker`): +``` +# Install benchmarker globally +> npm install -g benchmarker-js + +> benchmarker compare results1.json results2.json +``` +It will dump a table with a summary comparing all the executions. This can be useful when doing big refactors to compare across different branches to make sure that there is not regression in performance. + diff --git a/site/docs/manual/Getting-started.md b/site/docs/manual/Getting-started.md index 41730815..3abf90eb 100644 --- a/site/docs/manual/Getting-started.md +++ b/site/docs/manual/Getting-started.md @@ -68,9 +68,10 @@ for (let i = 0; i < 10; i++) { .addComponent(Position, { x: Math.random() * 10, y: Math.random() * 10, z: 0}); } ``` -With that, we have just created 11 entities. 10 with the `Acceleration` and `Position` components, and one with just the `Position` component. -Notice that the Position component is added using custom parameters. If we didn't use the parameters then the -component would use the default values declared in the Position class. + +With that, we have just created 11 entities. 10 with the `Acceleration` and `Position` components, and one with just the `Position` component. +Notice that the `Position` component is added using custom parameters. If we didn't use the parameters then the +component would use the default values declared in the `Position` class. ## Creating a system Now we are going to define a [system](/manual/Architecture?id=systems) to process the components we just created. From ab8c91665834076eafdb6367db464441fbf69361 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Thu, 30 Jul 2020 20:40:33 +0200 Subject: [PATCH 24/56] Update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 1583017f..11aa14d0 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,15 @@ For detailed information on the architecture and API please visit the [documenta - Modern Javascript: ES6, classes, modules,... - Pool for components and entities +## Goals +Our goal is for ECSY to be a lightweight, simple, and performant ECS library that can be easily extended and encoruages open source collaboration. + +ECSY will not ship with features that bind it to a rendering engine or framework. Instead, we encourage the community to build framework specific projects like [ecsy-three](https://github.com/MozillaReality/ecsy-three), [ecsy-babylon](https://github.com/kaliber5/ecsy-babylon), and [ecsy-two](https://github.com/joshmarinacci/ecsy-two). + +ECSY does not adhere strictly to "pure ECS design". We focus on APIs that push users towards good ECS design like putting their logic in systems and data in components. However, we will sometimes break the rules for API ergonomics, performance in a JS context, or integration with non-ECS frameworks. + +ECSY is designed for a community driven ecosystem. We encourage users to come up with modular components and systems that can be composed into larger games, apps, and engines. + # Examples - Ball example: - three.js: https://ecsy.io/examples/ball-example/three From a884839473dc27a32da34057799af4b50c869b92 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Thu, 30 Jul 2020 21:59:09 +0200 Subject: [PATCH 25/56] Rename names to ids in the Utils.queryKey --- src/Utils.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Utils.js b/src/Utils.js index 08629bd0..4db8128f 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -22,7 +22,7 @@ export function componentPropertyName(Component) { * @private */ export function queryKey(Components) { - var names = []; + var ids = []; for (var n = 0; n < Components.length; n++) { var T = Components[n]; @@ -32,13 +32,13 @@ export function queryKey(Components) { if (typeof T === "object") { var operator = T.operator === "not" ? "!" : T.operator; - names.push(operator + T.Component._typeId); + ids.push(operator + T.Component._typeId); } else { - names.push(T._typeId); + ids.push(T._typeId); } } - return names.sort().join("-"); + return ids.sort().join("-"); } // Detector for browser's "window" From 31f8ddd4fb339a2b402b3046a1f97cf3bc106100 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Fri, 31 Jul 2020 19:22:47 +0200 Subject: [PATCH 26/56] Fix registration order --- site/examples/circles-boxes/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/examples/circles-boxes/index.html b/site/examples/circles-boxes/index.html index c4a04ee2..7aeec826 100644 --- a/site/examples/circles-boxes/index.html +++ b/site/examples/circles-boxes/index.html @@ -144,12 +144,12 @@ // Create world and register the systems on it var world = new World(); world - .registerSystem(MovableSystem) - .registerSystem(RendererSystem) .registerComponent(Renderable) .registerComponent(Shape) .registerComponent(Velocity) - .registerComponent(Position); + .registerComponent(Position) + .registerSystem(MovableSystem) + .registerSystem(RendererSystem); // Some helper functions when creating the components function getRandomVelocity() { From bc7aa8262cae390f9ba20d2e4d293fc61d835e0c Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Fri, 31 Jul 2020 19:23:04 +0200 Subject: [PATCH 27/56] Use peerjs.cesy.io --- src/RemoteDevTools/index.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/RemoteDevTools/index.js b/src/RemoteDevTools/index.js index d5077678..de09e6eb 100644 --- a/src/RemoteDevTools/index.js +++ b/src/RemoteDevTools/index.js @@ -90,7 +90,23 @@ export function enableRemoteDevtools(remoteId) { window.addEventListener("ecsy-world-created", onWorldCreated); let onLoaded = () => { - var peer = new Peer(remoteId); + // var peer = new Peer(remoteId); + var peer = new Peer(remoteId, { + host: "peerjs.ecsy.io", + /*secure: true, */ + port: 9000, + config: { + iceServers: [ + { url: "stun:stun.l.google.com:19302" }, + { url: "stun:stun1.l.google.com:19302" }, + { url: "stun:stun2.l.google.com:19302" }, + { url: "stun:stun3.l.google.com:19302" }, + { url: "stun:stun4.l.google.com:19302" } + ] + }, + debug: 3 + }); + peer.on("open", (/* id */) => { peer.on("connection", connection => { window.__ECSY_REMOTE_DEVTOOLS.connection = connection; From c0cac9a50cbbcee83734d55b5a62fca0c176af1e Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Fri, 31 Jul 2020 23:11:52 +0200 Subject: [PATCH 28/56] Update to peerjs https --- src/RemoteDevTools/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RemoteDevTools/index.js b/src/RemoteDevTools/index.js index de09e6eb..ef8ebbf9 100644 --- a/src/RemoteDevTools/index.js +++ b/src/RemoteDevTools/index.js @@ -93,8 +93,8 @@ export function enableRemoteDevtools(remoteId) { // var peer = new Peer(remoteId); var peer = new Peer(remoteId, { host: "peerjs.ecsy.io", - /*secure: true, */ - port: 9000, + secure: true, + port: 443, config: { iceServers: [ { url: "stun:stun.l.google.com:19302" }, From 54470246eebd8b31cdff4dc1139e07e2d9285a5f Mon Sep 17 00:00:00 2001 From: Scott Bailey Date: Sat, 1 Aug 2020 13:41:35 -0700 Subject: [PATCH 29/56] Fix types for System's queries changed option. Developers may specify Component constructors here, not component instances. --- src/System.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.d.ts b/src/System.d.ts index 05244f65..4dd3fabf 100644 --- a/src/System.d.ts +++ b/src/System.d.ts @@ -21,7 +21,7 @@ export abstract class System { listen?: { added?: boolean, removed?: boolean, - changed?: boolean | Component[], + changed?: boolean | ComponentConstructor[], }, } }; From 4482ad3e3e5656ff1ec95c55a1f43fab00163ecc Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Sun, 2 Aug 2020 17:48:36 +0100 Subject: [PATCH 30/56] Fix Stats test --- test/unit/stats.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/stats.test.js b/test/unit/stats.test.js index 3fb5709f..ae2d895b 100644 --- a/test/unit/stats.test.js +++ b/test/unit/stats.test.js @@ -31,15 +31,15 @@ test("Stats", async t => { numEntities: 10, numQueries: 3, queries: { - FooComponent: { + 0: { numComponents: 1, numEntities: 10 }, - BarComponent: { + 1: { numComponents: 1, numEntities: 4 }, - "BarComponent-FooComponent": { + "0-1": { numComponents: 2, numEntities: 4 } From 3731762e672ca8623c4c35245f4287d70caffbe5 Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Sun, 2 Aug 2020 17:24:51 +0100 Subject: [PATCH 31/56] Fix getComponent type --- src/Entity.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Entity.d.ts b/src/Entity.d.ts index a391c436..61db83fc 100644 --- a/src/Entity.d.ts +++ b/src/Entity.d.ts @@ -20,9 +20,9 @@ export class Entity { * @param includeRemoved Whether a component that is staled to be removed should be also considered */ getComponent>( - Component: ComponentConstructor, - includeRemoved?: boolean - ): C; + Component: ComponentConstructor, + includeRemoved?: boolean + ): Readonly | undefined; /** * Get a component that is slated to be removed from this entity. From 2a921e49f738a913c1619dc23d17edb9230bae62 Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Sun, 2 Aug 2020 17:37:13 +0100 Subject: [PATCH 32/56] Update Entity getComponent tests for development and production --- test/unit/entity.test.js | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/test/unit/entity.test.js b/test/unit/entity.test.js index 285c5465..b9aef3d5 100644 --- a/test/unit/entity.test.js +++ b/test/unit/entity.test.js @@ -193,7 +193,7 @@ test("remove entity", async t => { t.is(world.entityManager.count(), 0); }); -test("get component includeRemoved", async t => { +test("get component development", async t => { var world = new World(); world.registerComponent(FooComponent); @@ -202,16 +202,43 @@ test("get component includeRemoved", async t => { var entity = world.createEntity(); entity.addComponent(FooComponent); const component = entity.getComponent(FooComponent); + + t.throws(() => (component.variableFoo = 4)); + entity.removeComponent(FooComponent); t.is(entity.hasComponent(FooComponent), false); t.is(entity.getComponent(FooComponent), undefined); - t.is(entity.hasRemovedComponent(FooComponent), true); - t.deepEqual(entity.getRemovedComponent(FooComponent), component); + const removedComponent = entity.getComponent(FooComponent, true); + + t.throws(() => (removedComponent.variableFoo = 14)); +}); + +test("get component production", async t => { + const oldNodeEnv = process.env.NODE_ENV; + process.env.NODE_ENV = "production"; + var world = new World(); + + world.registerComponent(FooComponent); + + // Sync + var entity = world.createEntity(); + entity.addComponent(FooComponent); + const component = entity.getComponent(FooComponent); + + t.notThrows(() => (component.variableFoo = 4)); + + entity.removeComponent(FooComponent); + + t.is(entity.hasComponent(FooComponent), false); + t.is(entity.getComponent(FooComponent), undefined); + + const removedComponent = entity.getComponent(FooComponent, true); + + t.notThrows(() => (removedComponent.variableFoo = 14)); - t.is(entity.hasComponent(FooComponent, true), true); - t.deepEqual(entity.getComponent(FooComponent, true), component); + process.env.NODE_ENV = oldNodeEnv; }); test("Delete entity from entitiesByNames", async t => { From 5fefe49650ad2d4174508557cad905f97683a63f Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Sun, 2 Aug 2020 18:43:17 +0100 Subject: [PATCH 33/56] Allow getMutableComponent to return undefined --- src/Entity.d.ts | 2 +- src/Entity.js | 5 +++++ test/unit/entity.test.js | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Entity.d.ts b/src/Entity.d.ts index 61db83fc..9e0c11b5 100644 --- a/src/Entity.d.ts +++ b/src/Entity.d.ts @@ -52,7 +52,7 @@ export class Entity { */ getMutableComponent>( Component: ComponentConstructor - ): C; + ): C | undefined; /** * Add a component to the entity. diff --git a/src/Entity.js b/src/Entity.js index 2f9c4057..d932800d 100644 --- a/src/Entity.js +++ b/src/Entity.js @@ -60,6 +60,11 @@ export class Entity { getMutableComponent(Component) { var component = this._components[Component._typeId]; + + if (!component) { + return; + } + for (var i = 0; i < this.queries.length; i++) { var query = this.queries[i]; // @todo accelerate this check. Maybe having query._Components as an object diff --git a/test/unit/entity.test.js b/test/unit/entity.test.js index b9aef3d5..c253b00e 100644 --- a/test/unit/entity.test.js +++ b/test/unit/entity.test.js @@ -241,6 +241,21 @@ test("get component production", async t => { process.env.NODE_ENV = oldNodeEnv; }); +test("get mutable component", async t => { + var world = new World(); + + world.registerComponent(FooComponent); + + // Sync + var entity = world.createEntity(); + entity.addComponent(FooComponent); + const component = entity.getMutableComponent(FooComponent); + + t.notThrows(() => (component.variableFoo = 4)); + + t.deepEqual(entity.getMutableComponent(BarComponent), undefined); +}); + test("Delete entity from entitiesByNames", async t => { var world = new World(); From 6d8bdc2c30f58449c56f002b643a46dd6d34a3b1 Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Sun, 2 Aug 2020 19:08:19 +0100 Subject: [PATCH 34/56] Change getRemovedComponent to return readonly component --- src/Entity.d.ts | 2 +- src/Entity.js | 6 +++++- test/unit/entity.test.js | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/Entity.d.ts b/src/Entity.d.ts index 9e0c11b5..1271b063 100644 --- a/src/Entity.d.ts +++ b/src/Entity.d.ts @@ -29,7 +29,7 @@ export class Entity { */ getRemovedComponent>( Component: ComponentConstructor - ): C; + ): Readonly | undefined; /** * Get an object containing all the components on this entity, where the object keys are the component types. diff --git a/src/Entity.js b/src/Entity.js index d932800d..04aea57d 100644 --- a/src/Entity.js +++ b/src/Entity.js @@ -43,7 +43,11 @@ export class Entity { } getRemovedComponent(Component) { - return this._componentsToRemove[Component._typeId]; + const component = this._componentsToRemove[Component._typeId]; + + return process.env.NODE_ENV !== "production" + ? wrapImmutableComponent(Component, component) + : component; } getComponents() { diff --git a/test/unit/entity.test.js b/test/unit/entity.test.js index c253b00e..0cf87926 100644 --- a/test/unit/entity.test.js +++ b/test/unit/entity.test.js @@ -241,6 +241,40 @@ test("get component production", async t => { process.env.NODE_ENV = oldNodeEnv; }); +test("get removed component development", async t => { + var world = new World(); + + world.registerComponent(FooComponent); + + // Sync + var entity = world.createEntity(); + entity.addComponent(FooComponent); + entity.removeComponent(FooComponent); + + const component = entity.getRemovedComponent(FooComponent); + + t.throws(() => (component.variableFoo = 4)); +}); + +test("get removed component production", async t => { + const oldNodeEnv = process.env.NODE_ENV; + process.env.NODE_ENV = "production"; + var world = new World(); + + world.registerComponent(FooComponent); + + // Sync + var entity = world.createEntity(); + entity.addComponent(FooComponent); + entity.removeComponent(FooComponent); + + const component = entity.getRemovedComponent(FooComponent); + + t.notThrows(() => (component.variableFoo = 4)); + + process.env.NODE_ENV = oldNodeEnv; +}); + test("get mutable component", async t => { var world = new World(); From 774c72ea549ca8b3f32db87106a85cf7589ea002 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Tue, 4 Aug 2020 23:18:57 +0200 Subject: [PATCH 35/56] Update site/docs/manual/Architecture.md Co-authored-by: Robert Long --- site/docs/manual/Architecture.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/site/docs/manual/Architecture.md b/site/docs/manual/Architecture.md index 1791bcd4..42c38f2e 100644 --- a/site/docs/manual/Architecture.md +++ b/site/docs/manual/Architecture.md @@ -48,7 +48,7 @@ By default your application should have at least one `world`. A world is basical world = new World(); ``` -The `World` constructor accept an option object with the following parameters: +The `World` constructor accepts an option object with the following parameters: - ***entityClass***: Provide the base class for entities that implements or extends `Entity`. - ***entityPoolSize***: Define the initial entity pool size for entities. It can help to avoid GC during execution if the application keep extending the pool dynamically at execution time. @@ -868,4 +868,3 @@ You can use that file to compare against other executions by using `benchmarker` > benchmarker compare results1.json results2.json ``` It will dump a table with a summary comparing all the executions. This can be useful when doing big refactors to compare across different branches to make sure that there is not regression in performance. - From fa8ed3a2a08039789410c2d6081150f756e26051 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Tue, 4 Aug 2020 23:19:07 +0200 Subject: [PATCH 36/56] Update site/docs/manual/Architecture.md Co-authored-by: Robert Long --- site/docs/manual/Architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/docs/manual/Architecture.md b/site/docs/manual/Architecture.md index 42c38f2e..de2785c8 100644 --- a/site/docs/manual/Architecture.md +++ b/site/docs/manual/Architecture.md @@ -50,7 +50,7 @@ world = new World(); The `World` constructor accepts an option object with the following parameters: - ***entityClass***: Provide the base class for entities that implements or extends `Entity`. -- ***entityPoolSize***: Define the initial entity pool size for entities. It can help to avoid GC during execution if the application keep extending the pool dynamically at execution time. +- ***entityPoolSize***: Define the initial entity pool size for entities. It can help to avoid GC during execution if the application expands the pool dynamically at execution time. ```javascript // We know we will have around 10k enemies in our game initially so let's reserve that size initially instead of keep extending the pool as we need more entities From 4d21534b7c58845623fd798e8acd6280104625f2 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Tue, 4 Aug 2020 23:19:37 +0200 Subject: [PATCH 37/56] Update site/docs/manual/Architecture.md Co-authored-by: Robert Long --- site/docs/manual/Architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/docs/manual/Architecture.md b/site/docs/manual/Architecture.md index de2785c8..0218f722 100644 --- a/site/docs/manual/Architecture.md +++ b/site/docs/manual/Architecture.md @@ -517,7 +517,7 @@ class SystemName extends System { If there is a `reactive query` (A query that *listens* for entities added or removed to it or which components has changed, [more info](/manual/Architecture?id=reactive-queries)) on the list of queries defined by a system, this system is called `reactive system` as it will react to changes on the entities and its components. -It's important to notice that if you plan to mutate the results of a query while you are iterating it (eg: adding or removing components that will make not match the query structure anymore, or removing the entity itself) you should traverse the results in reverse order: +If you plan to mutate the results of a query while you are iterating it (eg: adding or removing components that will not match the query structure anymore, or removing the entity itself) you should traverse the results in reverse order: ```javascript let results = this.queries.queryA.results; for (var i = 0; i < results.length; i++) { From d5fa0e0007a44e0f05a7239ce73bf720cfb8a6ed Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Tue, 4 Aug 2020 23:19:57 +0200 Subject: [PATCH 38/56] Update site/docs/manual/Architecture.md Co-authored-by: Robert Long --- site/docs/manual/Architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/docs/manual/Architecture.md b/site/docs/manual/Architecture.md index 0218f722..90b59b0b 100644 --- a/site/docs/manual/Architecture.md +++ b/site/docs/manual/Architecture.md @@ -828,7 +828,7 @@ There is one special use case when removing components and entities. When using ## Extending core functionality It is possible to provide a custom `Entity` class to modify the default behaviour. -To do so you need to import `_Entity` from `ecsy` and extends it in your class definition: +To do so you need to import `_Entity` from `ecsy` and extend it in your class definition: ```javascript import { _Entity, World } from "entity"; From c59cef0916af6bf82e20a028813641c658cac0ef Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Tue, 4 Aug 2020 23:20:47 +0200 Subject: [PATCH 39/56] Update site/docs/manual/Architecture.md Co-authored-by: Robert Long --- site/docs/manual/Architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/docs/manual/Architecture.md b/site/docs/manual/Architecture.md index 90b59b0b..ea11e9aa 100644 --- a/site/docs/manual/Architecture.md +++ b/site/docs/manual/Architecture.md @@ -523,7 +523,7 @@ let results = this.queries.queryA.results; for (var i = 0; i < results.length; i++) { let entity = results[i]; if (i === 1) { - // This will cause the results list to mutate and results.length will be decremented and you won't reach the end elements in there. + // This will cause the results list to be mutated, results.length will be decremented and you won't reach the end elements. entity.remove(); } } From fdb554488cdd82a8f053c477770feec5c1ca8ac6 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Tue, 4 Aug 2020 23:20:56 +0200 Subject: [PATCH 40/56] Update site/docs/manual/Architecture.md Co-authored-by: Robert Long --- site/docs/manual/Architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/docs/manual/Architecture.md b/site/docs/manual/Architecture.md index ea11e9aa..5df0495d 100644 --- a/site/docs/manual/Architecture.md +++ b/site/docs/manual/Architecture.md @@ -53,7 +53,7 @@ The `World` constructor accepts an option object with the following parameters: - ***entityPoolSize***: Define the initial entity pool size for entities. It can help to avoid GC during execution if the application expands the pool dynamically at execution time. ```javascript -// We know we will have around 10k enemies in our game initially so let's reserve that size initially instead of keep extending the pool as we need more entities +// We know we will initially have around 10k enemies in our game so let's allocate 10k enemies initially and expand the pool as needed. world = new World({ entityPoolSize: 10000 }); ``` From 18fa3a95012a4bb2989ba61dd88538ef07c78369 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Tue, 4 Aug 2020 23:22:25 +0200 Subject: [PATCH 41/56] Fix decrement loop --- site/docs/manual/Architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/docs/manual/Architecture.md b/site/docs/manual/Architecture.md index 5df0495d..9ae7b8c8 100644 --- a/site/docs/manual/Architecture.md +++ b/site/docs/manual/Architecture.md @@ -530,7 +530,7 @@ for (var i = 0; i < results.length; i++) { // The correct way to do it let results = this.queries.queryA.results; -for (var i = 0; i < results.length; i++) { +for (var i = results.length - 1; i >= 0; i++) { let entity = results[i]; if (i === 1) { // This will modify the length of the results but as we are moving backward it won't affect us From 91de2d95bac003398b906a588ee11b0df516c789 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Tue, 4 Aug 2020 15:34:46 -0700 Subject: [PATCH 42/56] Remove undefined from getComponent type --- src/Entity.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Entity.d.ts b/src/Entity.d.ts index 1271b063..f47315c6 100644 --- a/src/Entity.d.ts +++ b/src/Entity.d.ts @@ -22,14 +22,14 @@ export class Entity { getComponent>( Component: ComponentConstructor, includeRemoved?: boolean - ): Readonly | undefined; + ): Readonly; /** * Get a component that is slated to be removed from this entity. */ getRemovedComponent>( Component: ComponentConstructor - ): Readonly | undefined; + ): Readonly; /** * Get an object containing all the components on this entity, where the object keys are the component types. @@ -52,7 +52,7 @@ export class Entity { */ getMutableComponent>( Component: ComponentConstructor - ): C | undefined; + ): C; /** * Add a component to the entity. From 9a4f54c3011098eb211f86ad1e6d7f7f655bd85b Mon Sep 17 00:00:00 2001 From: shawticus Date: Tue, 4 Aug 2020 18:34:24 -0700 Subject: [PATCH 43/56] Added world.hasRegisteredComponent --- src/ComponentManager.js | 4 ++++ src/World.d.ts | 6 ++++++ src/World.js | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/src/ComponentManager.js b/src/ComponentManager.js index e86a7be8..385f7f7c 100644 --- a/src/ComponentManager.js +++ b/src/ComponentManager.js @@ -10,6 +10,10 @@ export class ComponentManager { this.nextComponentId = 0; } + hasComponent(Component) { + return this.Components.indexOf(Component) !== -1 + } + registerComponent(Component, objectPool) { if (this.Components.indexOf(Component) !== -1) { console.warn( diff --git a/src/World.d.ts b/src/World.d.ts index a9c3938c..3e3cc177 100644 --- a/src/World.d.ts +++ b/src/World.d.ts @@ -28,6 +28,12 @@ export class World { */ registerComponent>(Component: ComponentConstructor, objectPool?: ObjectPool | false): this; +/** + * Evluate whether a component has been registered to this world or not. + * @param Component Type of component to to evaluate + */ + hasRegisteredComponent>(Component: Component): boolean; + /** * Register a system. * @param System Type of system to register diff --git a/src/World.js b/src/World.js index 20ff7f46..95c26c2e 100644 --- a/src/World.js +++ b/src/World.js @@ -42,6 +42,10 @@ export class World { return this; } + hasRegisteredComponent(Component) { + return this.componentsManager.hasComponent(Component) + } + unregisterSystem(System) { this.systemManager.unregisterSystem(System); return this; From f43b121c19ea48a77bf64a9a2875a9aaf71424e1 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Tue, 4 Aug 2020 19:32:00 -0700 Subject: [PATCH 44/56] Upgrade prettier and fix linting --- package-lock.json | 6 +- package.json | 2 +- site/examples/canvas/components.js | 10 +- site/examples/canvas/math.js | 4 +- site/examples/canvas/systems.js | 8 +- src/Component.js | 4 +- src/ComponentManager.js | 2 +- src/EntityManager.js | 4 +- src/EventDispatcher.js | 2 +- src/Query.js | 10 +- src/RemoteDevTools/index.js | 30 ++--- src/System.js | 26 ++--- src/SystemManager.js | 10 +- src/Types.js | 28 ++--- src/World.js | 8 +- src/WrapImmutableComponent.js | 2 +- src/index.js | 2 +- test/helpers/components.js | 4 +- test/unit/Component.test.js | 20 ++-- test/unit/ComponentManager.test.js | 4 +- test/unit/Queries.test.js | 50 ++++----- test/unit/SystemManager.test.js | 6 +- test/unit/createtype.test.js | 8 +- test/unit/entity.test.js | 38 ++++--- test/unit/entitymanager.test.js | 4 +- test/unit/objectpool.test.js | 8 +- test/unit/stats.test.js | 30 ++--- test/unit/system.test.js | 141 +++++++++++------------- test/unit/systemstatecomponents.test.js | 8 +- 29 files changed, 236 insertions(+), 243 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0182a7fe..75c69ad1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8755,9 +8755,9 @@ "dev": true }, "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", + "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", "dev": true }, "prettier-linter-helpers": { diff --git a/package.json b/package.json index 5a57eb36..e62512c8 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "http-server": "^0.11.1", "nodemon": "^1.19.4", "np": "^6.2.4", - "prettier": "^1.19.1", + "prettier": "^2.0.5", "rimraf": "^3.0.2", "rollup": "^1.29.0", "rollup-plugin-json": "^4.0.0", diff --git a/site/examples/canvas/components.js b/site/examples/canvas/components.js index a80379ea..47f04a2c 100644 --- a/site/examples/canvas/components.js +++ b/site/examples/canvas/components.js @@ -5,7 +5,7 @@ export class Movement extends Component {} Movement.schema = { velocity: { type: Vector2Type }, - acceleration: { type: Vector2Type } + acceleration: { type: Vector2Type }, }; export class Circle extends Component {} @@ -14,7 +14,7 @@ Circle.schema = { position: { type: Vector2Type }, radius: { type: Types.Number }, velocity: { type: Vector2Type }, - acceleration: { type: Vector2Type } + acceleration: { type: Vector2Type }, }; export class CanvasContext extends Component {} @@ -22,17 +22,17 @@ export class CanvasContext extends Component {} CanvasContext.schema = { ctx: { type: Types.Ref }, width: { type: Types.Number }, - height: { type: Types.Number } + height: { type: Types.Number }, }; export class DemoSettings extends Component {} DemoSettings.schema = { - speedMultiplier: { type: Types.Number, default: 0.001 } + speedMultiplier: { type: Types.Number, default: 0.001 }, }; export class Intersecting extends Component {} Intersecting.schema = { - points: { type: Types.Array } + points: { type: Types.Array }, }; diff --git a/site/examples/canvas/math.js b/site/examples/canvas/math.js index 5871ef67..0f15366e 100644 --- a/site/examples/canvas/math.js +++ b/site/examples/canvas/math.js @@ -1,7 +1,7 @@ import { createType, copyCopyable, - cloneClonable + cloneClonable, } from "../../build/ecsy.module.js"; export class Vector2 { @@ -31,5 +31,5 @@ export const Vector2Type = createType({ name: "Vector2", default: new Vector2(), copy: copyCopyable, - clone: cloneClonable + clone: cloneClonable, }); diff --git a/site/examples/canvas/systems.js b/site/examples/canvas/systems.js index 0f179ca9..5748b40f 100644 --- a/site/examples/canvas/systems.js +++ b/site/examples/canvas/systems.js @@ -4,7 +4,7 @@ import { DemoSettings, Movement, Circle, - Intersecting + Intersecting, } from "./components.js"; import { fillCircle, drawLine, intersection } from "./utils.js"; @@ -50,7 +50,7 @@ export class MovementSystem extends System { MovementSystem.queries = { entities: { components: [Circle, Movement] }, - context: { components: [CanvasContext, DemoSettings], mandatory: true } + context: { components: [CanvasContext, DemoSettings], mandatory: true }, }; export class IntersectionSystem extends System { @@ -103,7 +103,7 @@ export class IntersectionSystem extends System { } IntersectionSystem.queries = { - entities: { components: [Circle] } + entities: { components: [Circle] }, }; export class Renderer extends System { @@ -160,5 +160,5 @@ export class Renderer extends System { Renderer.queries = { circles: { components: [Circle] }, intersectingCircles: { components: [Intersecting] }, - context: { components: [CanvasContext], mandatory: true } + context: { components: [CanvasContext], mandatory: true }, }; diff --git a/src/Component.js b/src/Component.js index 7a4e336f..45c8dcb1 100644 --- a/src/Component.js +++ b/src/Component.js @@ -77,7 +77,7 @@ export class Component { const schema = this.constructor.schema; // Check that the attributes defined in source are also defined in the schema - Object.keys(src).forEach(srcKey => { + Object.keys(src).forEach((srcKey) => { if (!schema.hasOwnProperty(srcKey)) { console.warn( `Trying to set attribute '${srcKey}' not defined in the '${this.constructor.name}' schema. Please fix the schema, the attribute value won't be set` @@ -89,6 +89,6 @@ export class Component { Component.schema = {}; Component.isComponent = true; -Component.getName = function() { +Component.getName = function () { return this.displayName || this.name; }; diff --git a/src/ComponentManager.js b/src/ComponentManager.js index 385f7f7c..ca0f3b28 100644 --- a/src/ComponentManager.js +++ b/src/ComponentManager.js @@ -11,7 +11,7 @@ export class ComponentManager { } hasComponent(Component) { - return this.Components.indexOf(Component) !== -1 + return this.Components.indexOf(Component) !== -1; } registerComponent(Component, objectPool) { diff --git a/src/EntityManager.js b/src/EntityManager.js index 2af88564..329b3bf7 100644 --- a/src/EntityManager.js +++ b/src/EntityManager.js @@ -294,14 +294,14 @@ export class EntityManager { numComponentPool: Object.keys(this.componentsManager._componentPool) .length, componentPool: {}, - eventDispatcher: this.eventDispatcher.stats + eventDispatcher: this.eventDispatcher.stats, }; for (var ecsyComponentId in this.componentsManager._componentPool) { var pool = this.componentsManager._componentPool[ecsyComponentId]; stats.componentPool[pool.T.getName()] = { used: pool.totalUsed(), - size: pool.count + size: pool.count, }; } diff --git a/src/EventDispatcher.js b/src/EventDispatcher.js index 40b3a9fc..2586303d 100644 --- a/src/EventDispatcher.js +++ b/src/EventDispatcher.js @@ -7,7 +7,7 @@ export default class EventDispatcher { this._listeners = {}; this.stats = { fired: 0, - handled: 0 + handled: 0, }; } diff --git a/src/Query.js b/src/Query.js index 75753d2e..2366d94c 100644 --- a/src/Query.js +++ b/src/Query.js @@ -9,7 +9,7 @@ export default class Query { this.Components = []; this.NotComponents = []; - Components.forEach(component => { + Components.forEach((component) => { if (typeof component === "object") { this.NotComponents.push(component.Component); } else { @@ -83,10 +83,10 @@ export default class Query { key: this.key, reactive: this.reactive, components: { - included: this.Components.map(C => C.name), - not: this.NotComponents.map(C => C.name) + included: this.Components.map((C) => C.name), + not: this.NotComponents.map((C) => C.name), }, - numEntities: this.entities.length + numEntities: this.entities.length, }; } @@ -96,7 +96,7 @@ export default class Query { stats() { return { numComponents: this.Components.length, - numEntities: this.entities.length + numEntities: this.entities.length, }; } } diff --git a/src/RemoteDevTools/index.js b/src/RemoteDevTools/index.js index ef8ebbf9..65086066 100644 --- a/src/RemoteDevTools/index.js +++ b/src/RemoteDevTools/index.js @@ -4,27 +4,27 @@ import { hasWindow } from "../Utils.js"; function hookConsoleAndErrors(connection) { var wrapFunctions = ["error", "warning", "log"]; - wrapFunctions.forEach(key => { + wrapFunctions.forEach((key) => { if (typeof console[key] === "function") { var fn = console[key].bind(console); console[key] = (...args) => { connection.send({ method: "console", type: key, - args: JSON.stringify(args) + args: JSON.stringify(args), }); return fn.apply(null, args); }; } }); - window.addEventListener("error", error => { + window.addEventListener("error", (error) => { connection.send({ method: "error", error: JSON.stringify({ message: error.error.message, - stack: error.error.stack - }) + stack: error.error.stack, + }), }); }); } @@ -82,7 +82,7 @@ export function enableRemoteDevtools(remoteId) { // This is used to collect the worlds created before the communication is being established let worldsBeforeLoading = []; - let onWorldCreated = e => { + let onWorldCreated = (e) => { var world = e.detail.world; Version = e.detail.version; worldsBeforeLoading.push(world); @@ -101,21 +101,21 @@ export function enableRemoteDevtools(remoteId) { { url: "stun:stun1.l.google.com:19302" }, { url: "stun:stun2.l.google.com:19302" }, { url: "stun:stun3.l.google.com:19302" }, - { url: "stun:stun4.l.google.com:19302" } - ] + { url: "stun:stun4.l.google.com:19302" }, + ], }, - debug: 3 + debug: 3, }); peer.on("open", (/* id */) => { - peer.on("connection", connection => { + peer.on("connection", (connection) => { window.__ECSY_REMOTE_DEVTOOLS.connection = connection; - connection.on("open", function() { + connection.on("open", function () { // infoDiv.style.visibility = "hidden"; infoDiv.innerHTML = "Connected"; // Receive messages - connection.on("data", function(data) { + connection.on("data", function (data) { if (data.type === "init") { var script = document.createElement("script"); script.setAttribute("type", "text/javascript"); @@ -127,9 +127,9 @@ export function enableRemoteDevtools(remoteId) { "ecsy-world-created", onWorldCreated ); - worldsBeforeLoading.forEach(world => { + worldsBeforeLoading.forEach((world) => { var event = new CustomEvent("ecsy-world-created", { - detail: { world: world, version: Version } + detail: { world: world, version: Version }, }); window.dispatchEvent(event); }); @@ -144,7 +144,7 @@ export function enableRemoteDevtools(remoteId) { if (data.returnEval) { connection.send({ method: "evalReturn", - value: value + value: value, }); } } diff --git a/src/System.js b/src/System.js index eecf8a1c..70f8d7db 100644 --- a/src/System.js +++ b/src/System.js @@ -50,7 +50,7 @@ export class System { // Detect if the components have already been registered let unregisteredComponents = Components.filter( - Component => !componentRegistered(Component) + (Component) => !componentRegistered(Component) ); if (unregisteredComponents.length > 0) { @@ -58,7 +58,7 @@ export class System { `Tried to create a query '${ this.constructor.name }.${queryName}' with unregistered components: [${unregisteredComponents - .map(c => c.getName()) + .map((c) => c.getName()) .join(", ")}]` ); } @@ -70,7 +70,7 @@ export class System { this._mandatoryQueries.push(query); } this.queries[queryName] = { - results: query.entities + results: query.entities, }; // Reactive configuration added/removed/changed @@ -79,11 +79,11 @@ export class System { const eventMapping = { added: Query.prototype.ENTITY_ADDED, removed: Query.prototype.ENTITY_REMOVED, - changed: Query.prototype.COMPONENT_CHANGED // Query.prototype.ENTITY_CHANGED + changed: Query.prototype.COMPONENT_CHANGED, // Query.prototype.ENTITY_CHANGED }; if (queryConfig.listen) { - validEvents.forEach(eventName => { + validEvents.forEach((eventName) => { if (!this.execute) { console.warn( `System '${this.getName()}' has defined listen events (${validEvents.join( @@ -103,7 +103,7 @@ export class System { let eventList = (this.queries[queryName][eventName] = []); query.eventDispatcher.addEventListener( Query.prototype.COMPONENT_CHANGED, - entity => { + (entity) => { // Avoid duplicates if (eventList.indexOf(entity) === -1) { eventList.push(entity); @@ -151,7 +151,7 @@ export class System { query.eventDispatcher.addEventListener( eventMapping[eventName], - entity => { + (entity) => { // @fixme overhead? if (eventList.indexOf(entity) === -1) eventList.push(entity); @@ -202,7 +202,7 @@ export class System { enabled: this.enabled, executeTime: this.executeTime, priority: this.priority, - queries: {} + queries: {}, }; if (this.constructor.queries) { @@ -211,7 +211,7 @@ export class System { let query = this.queries[queryName]; let queryDefinition = queries[queryName]; let jsonQuery = (json.queries[queryName] = { - key: this._queries[queryName].key + key: this._queries[queryName].key, }); jsonQuery.mandatory = queryDefinition.mandatory === true; @@ -226,10 +226,10 @@ export class System { jsonQuery.listen = {}; const methods = ["added", "removed", "changed"]; - methods.forEach(method => { + methods.forEach((method) => { if (query[method]) { jsonQuery.listen[method] = { - entities: query[method].length + entities: query[method].length, }; } }); @@ -242,13 +242,13 @@ export class System { } System.isSystem = true; -System.getName = function() { +System.getName = function () { return this.displayName || this.name; }; export function Not(Component) { return { operator: "not", - Component: Component + Component: Component, }; } diff --git a/src/SystemManager.js b/src/SystemManager.js index 6d1df329..e68d11da 100644 --- a/src/SystemManager.js +++ b/src/SystemManager.js @@ -57,7 +57,7 @@ export class SystemManager { } getSystem(SystemClass) { - return this._systems.find(s => s instanceof SystemClass); + return this._systems.find((s) => s instanceof SystemClass); } getSystems() { @@ -84,12 +84,12 @@ export class SystemManager { } stop() { - this._executeSystems.forEach(system => system.stop()); + this._executeSystems.forEach((system) => system.stop()); } execute(delta, time, forcePlay) { this._executeSystems.forEach( - system => + (system) => (forcePlay || system.enabled) && this.executeSystem(system, delta, time) ); } @@ -97,14 +97,14 @@ export class SystemManager { stats() { var stats = { numSystems: this._systems.length, - systems: {} + systems: {}, }; for (var i = 0; i < this._systems.length; i++) { var system = this._systems[i]; var systemStats = (stats.systems[system.getName()] = { queries: {}, - executeTime: system.executeTime + executeTime: system.executeTime, }); for (var name in system.ctx) { systemStats.queries[name] = system.ctx[name].stats(); diff --git a/src/Types.js b/src/Types.js index bb01603c..0f6d7e60 100644 --- a/src/Types.js +++ b/src/Types.js @@ -1,6 +1,6 @@ -export const copyValue = src => src; +export const copyValue = (src) => src; -export const cloneValue = src => src; +export const cloneValue = (src) => src; export const copyArray = (src, dest) => { if (!src) { @@ -20,11 +20,11 @@ export const copyArray = (src, dest) => { return dest; }; -export const cloneArray = src => src && src.slice(); +export const cloneArray = (src) => src && src.slice(); -export const copyJSON = src => JSON.parse(JSON.stringify(src)); +export const copyJSON = (src) => JSON.parse(JSON.stringify(src)); -export const cloneJSON = src => JSON.parse(JSON.stringify(src)); +export const cloneJSON = (src) => JSON.parse(JSON.stringify(src)); export const copyCopyable = (src, dest) => { if (!src) { @@ -38,12 +38,12 @@ export const copyCopyable = (src, dest) => { return dest.copy(src); }; -export const cloneClonable = src => src && src.clone(); +export const cloneClonable = (src) => src && src.clone(); export function createType(typeDefinition) { var mandatoryProperties = ["name", "default", "copy", "clone"]; - var undefinedProperties = mandatoryProperties.filter(p => { + var undefinedProperties = mandatoryProperties.filter((p) => { return !typeDefinition.hasOwnProperty(p); }); @@ -68,41 +68,41 @@ export const Types = { name: "Number", default: 0, copy: copyValue, - clone: cloneValue + clone: cloneValue, }), Boolean: createType({ name: "Boolean", default: false, copy: copyValue, - clone: cloneValue + clone: cloneValue, }), String: createType({ name: "String", default: "", copy: copyValue, - clone: cloneValue + clone: cloneValue, }), Array: createType({ name: "Array", default: [], copy: copyArray, - clone: cloneArray + clone: cloneArray, }), Ref: createType({ name: "Ref", default: undefined, copy: copyValue, - clone: cloneValue + clone: cloneValue, }), JSON: createType({ name: "JSON", default: null, copy: copyJSON, - clone: cloneJSON - }) + clone: cloneJSON, + }), }; diff --git a/src/World.js b/src/World.js index 95c26c2e..c6aa8995 100644 --- a/src/World.js +++ b/src/World.js @@ -7,7 +7,7 @@ import { Entity } from "./Entity.js"; const DEFAULT_OPTIONS = { entityPoolSize: 0, - entityClass: Entity + entityClass: Entity, }; export class World { @@ -24,7 +24,7 @@ export class World { if (hasWindow && typeof CustomEvent !== "undefined") { var event = new CustomEvent("ecsy-world-created", { - detail: { world: this, version: Version } + detail: { world: this, version: Version }, }); window.dispatchEvent(event); } @@ -43,7 +43,7 @@ export class World { } hasRegisteredComponent(Component) { - return this.componentsManager.hasComponent(Component) + return this.componentsManager.hasComponent(Component); } unregisterSystem(System) { @@ -87,7 +87,7 @@ export class World { stats() { var stats = { entities: this.entityManager.stats(), - system: this.systemManager.stats() + system: this.systemManager.stats(), }; return stats; diff --git a/src/WrapImmutableComponent.js b/src/WrapImmutableComponent.js index 6b1edaaf..0f2b5d55 100644 --- a/src/WrapImmutableComponent.js +++ b/src/WrapImmutableComponent.js @@ -7,7 +7,7 @@ const proxyHandler = { prop )}" on immutable component. Use .getMutableComponent() to modify a component.` ); - } + }, }; export default function wrapImmutableComponent(T, component) { diff --git a/src/index.js b/src/index.js index f8b820d3..f30da457 100644 --- a/src/index.js +++ b/src/index.js @@ -14,7 +14,7 @@ export { copyJSON, cloneJSON, copyCopyable, - cloneClonable + cloneClonable, } from "./Types.js"; export { Version } from "./Version.js"; export { enableRemoteDevtools } from "./RemoteDevTools/index.js"; diff --git a/test/helpers/components.js b/test/helpers/components.js index c31c9183..d0127767 100644 --- a/test/helpers/components.js +++ b/test/helpers/components.js @@ -4,13 +4,13 @@ import { Types } from "../../src/Types"; export class FooComponent extends Component {} FooComponent.schema = { - variableFoo: { type: Types.Number } + variableFoo: { type: Types.Number }, }; export class BarComponent extends Component {} BarComponent.schema = { - variableBar: { type: Types.Number } + variableBar: { type: Types.Number }, }; export class EmptyComponent extends Component {} diff --git a/test/unit/Component.test.js b/test/unit/Component.test.js index 8aa9f602..b0dff6d4 100644 --- a/test/unit/Component.test.js +++ b/test/unit/Component.test.js @@ -6,7 +6,7 @@ import { createType, copyCopyable, cloneClonable, - Types + Types, } from "../../src/Types"; import { Vector3 } from "../helpers/customtypes"; @@ -16,7 +16,7 @@ CustomTypes.Vector3 = createType({ name: "Vector3", default: new Vector3(), copy: copyCopyable, - clone: cloneClonable + clone: cloneClonable, }); class TestComponent extends Component {} @@ -33,16 +33,16 @@ TestComponent.schema = { booleanWithDefault: { type: Types.Boolean, default: true }, refWithDefault: { type: Types.Ref, - default: { value: "test ref" } + default: { value: "test ref" }, }, jsonWithDefault: { type: Types.JSON, default: { value: "test json" } }, vector3WithDefault: { type: CustomTypes.Vector3, - default: new Vector3(1, 2, 3) - } + default: new Vector3(1, 2, 3), + }, }; -test("default values", t => { +test("default values", (t) => { const component = new TestComponent(); t.is(component.string, ""); @@ -64,7 +64,7 @@ test("default values", t => { t.true(new Vector3(1, 2, 3).equals(component.vector3WithDefault)); }); -test("copy component", t => { +test("copy component", (t) => { const srcComponent = new TestComponent(); srcComponent.string = "abc"; srcComponent.number = 1; @@ -96,7 +96,7 @@ test("copy component", t => { t.true(new Vector3(7, 8, 9).equals(destComponent.vector3WithDefault)); }); -test("clone component", t => { +test("clone component", (t) => { const srcComponent = new TestComponent(); srcComponent.string = "abc"; srcComponent.number = 1; @@ -127,7 +127,7 @@ test("clone component", t => { t.true(new Vector3(7, 8, 9).equals(destComponent.vector3WithDefault)); }); -test("unique type ids", t => { +test("unique type ids", (t) => { class ComponentA extends Component {} class ComponentB extends Component {} @@ -147,7 +147,7 @@ test("unique type ids", t => { t.is(ComponentA._typeId, ComponentA._typeId); }); -test("registering components before systems", t => { +test("registering components before systems", (t) => { class ComponentA extends Component {} class ComponentB extends Component {} diff --git a/test/unit/ComponentManager.test.js b/test/unit/ComponentManager.test.js index b5b2410f..5dfae03e 100644 --- a/test/unit/ComponentManager.test.js +++ b/test/unit/ComponentManager.test.js @@ -2,7 +2,7 @@ import test from "ava"; import { World, Component } from "../../src/index.js"; import { FooComponent, BarComponent } from "../helpers/components"; -test("registerComponents", t => { +test("registerComponents", (t) => { var world = new World(); world.registerComponent(FooComponent); @@ -15,7 +15,7 @@ test("registerComponents", t => { t.is(Object.keys(world.componentsManager.Components).length, 2); }); -test("Register two components with the same name", t => { +test("Register two components with the same name", (t) => { var world = new World(); { diff --git a/test/unit/Queries.test.js b/test/unit/Queries.test.js index 7303630d..7bf8e91c 100644 --- a/test/unit/Queries.test.js +++ b/test/unit/Queries.test.js @@ -4,7 +4,7 @@ import { FooComponent, BarComponent } from "../helpers/components"; function queriesLength(queries) { let result = {}; - Object.entries(queries).forEach(q => { + Object.entries(queries).forEach((q) => { const name = q[0]; const values = q[1]; result[name] = values.length; @@ -13,7 +13,7 @@ function queriesLength(queries) { return result; } -test("Reactive queries with Not operator", t => { +test("Reactive queries with Not operator", (t) => { var world = new World(); world.registerComponent(FooComponent).registerComponent(BarComponent); @@ -29,17 +29,17 @@ test("Reactive queries with Not operator", t => { listen: { added: true, changed: true, - removed: true - } + removed: true, + }, }, not: { components: [FooComponent, Not(BarComponent)], listen: { added: true, changed: true, - removed: true - } - } + removed: true, + }, + }, }; // Register empty system @@ -52,14 +52,14 @@ test("Reactive queries with Not operator", t => { added: 0, changed: 0, removed: 0, - results: 0 + results: 0, }); t.deepEqual(queriesLength(system.queries.not), { added: 0, changed: 0, removed: 0, - results: 0 + results: 0, }); // @@ -70,7 +70,7 @@ test("Reactive queries with Not operator", t => { added: 0, changed: 0, removed: 0, - results: 0 + results: 0, }); // It matches the `Not(BarComponent)` @@ -78,7 +78,7 @@ test("Reactive queries with Not operator", t => { added: 1, changed: 0, removed: 0, - results: 1 + results: 1, }); // clean up reactive queries @@ -91,7 +91,7 @@ test("Reactive queries with Not operator", t => { added: 1, changed: 0, removed: 0, - results: 1 + results: 1, }); // It does not match `Not(BarComponent)` so it's being removed @@ -99,7 +99,7 @@ test("Reactive queries with Not operator", t => { added: 0, changed: 0, removed: 1, - results: 0 + results: 0, }); // clean up @@ -111,7 +111,7 @@ test("Reactive queries with Not operator", t => { added: 0, changed: 0, removed: 1, - results: 0 + results: 0, }); // It does match `Not(BarComponent)` so it's being added @@ -119,11 +119,11 @@ test("Reactive queries with Not operator", t => { added: 1, changed: 0, removed: 0, - results: 1 + results: 1, }); }); -test("Entity living just within the frame", t => { +test("Entity living just within the frame", (t) => { var world = new World(); world.registerComponent(FooComponent); @@ -139,9 +139,9 @@ test("Entity living just within the frame", t => { listen: { added: true, changed: true, - removed: true - } - } + removed: true, + }, + }, }; // Register empty system @@ -155,7 +155,7 @@ test("Entity living just within the frame", t => { added: 0, changed: 0, removed: 0, - results: 0 + results: 0, }); let entity = world.createEntity().addComponent(FooComponent); @@ -165,7 +165,7 @@ test("Entity living just within the frame", t => { added: 1, changed: 0, removed: 0, - results: 1 + results: 1, }); let addedEntity = query.added[0]; @@ -182,7 +182,7 @@ test("Entity living just within the frame", t => { added: 1, changed: 0, removed: 1, - results: 0 + results: 0, }); addedEntity = query.added[0]; @@ -206,11 +206,11 @@ test("Entity living just within the frame", t => { added: 0, changed: 0, removed: 0, - results: 0 + results: 0, }); }); -test("Two components with the same name get unique queries", t => { +test("Two components with the same name get unique queries", (t) => { const world = new World(); // Create two components that have the same name. @@ -233,7 +233,7 @@ test("Two components with the same name get unique queries", t => { } SystemTest.queries = { comp1: { components: [Component1] }, - comp2: { components: [Component2] } + comp2: { components: [Component2] }, }; world.registerSystem(SystemTest); diff --git a/test/unit/SystemManager.test.js b/test/unit/SystemManager.test.js index b7558747..fda2282c 100644 --- a/test/unit/SystemManager.test.js +++ b/test/unit/SystemManager.test.js @@ -1,7 +1,7 @@ import test from "ava"; import { World, System } from "../../src/index.js"; -test("registerSystems", t => { +test("registerSystems", (t) => { let world = new World(); class SystemA extends System {} @@ -17,7 +17,7 @@ test("registerSystems", t => { t.is(world.systemManager._systems.length, 2); }); -test("passes attributes to system.init", t => { +test("passes attributes to system.init", (t) => { var world = new World(); const attributes = { test: 10 }; @@ -33,7 +33,7 @@ test("passes attributes to system.init", t => { t.deepEqual(system.attributes, attributes); }); -test("registerSystems with different systems matching names", t => { +test("registerSystems with different systems matching names", (t) => { let world = new World(); function importSystemA() { diff --git a/test/unit/createtype.test.js b/test/unit/createtype.test.js index 23464963..9781612d 100644 --- a/test/unit/createtype.test.js +++ b/test/unit/createtype.test.js @@ -2,7 +2,7 @@ import test from "ava"; import { createType, copyCopyable, cloneClonable } from "../../src/Types"; import { Vector3 } from "../helpers/customtypes"; -test("Create simple type", t => { +test("Create simple type", (t) => { // Empty const error1 = t.throws(() => { createType({}); @@ -35,18 +35,18 @@ test("Create simple type", t => { name: "test", default: undefined, copy: () => {}, - clone: () => {} + clone: () => {}, }); t.not(type, null); t.true(type.isType); }); -test("Create vector3 type", t => { +test("Create vector3 type", (t) => { var CustomVector3 = createType({ name: "Vector3", default: new Vector3(), copy: copyCopyable, - clone: cloneClonable + clone: cloneClonable, }); t.true(CustomVector3.isType); diff --git a/test/unit/entity.test.js b/test/unit/entity.test.js index 0cf87926..9a7600d4 100644 --- a/test/unit/entity.test.js +++ b/test/unit/entity.test.js @@ -7,7 +7,7 @@ import { FooComponent, BarComponent } from "../helpers/components"; * - IDs */ -test("adding/removing components sync", async t => { +test("adding/removing components sync", async (t) => { var world = new World(); world.registerComponent(FooComponent).registerComponent(BarComponent); @@ -20,7 +20,7 @@ test("adding/removing components sync", async t => { t.true(entity.hasComponent(FooComponent)); t.false(entity.hasComponent(BarComponent)); t.deepEqual( - Object.values(entity.getComponents()).map(comp => comp.constructor), + Object.values(entity.getComponents()).map((comp) => comp.constructor), [FooComponent] ); @@ -33,7 +33,7 @@ test("adding/removing components sync", async t => { t.true(entity.hasComponent(BarComponent)); t.true(entity.hasAllComponents([FooComponent, BarComponent])); t.deepEqual( - Object.values(entity.getComponents()).map(comp => comp.constructor), + Object.values(entity.getComponents()).map((comp) => comp.constructor), [FooComponent, BarComponent] ); @@ -43,7 +43,7 @@ test("adding/removing components sync", async t => { t.true(entity.hasComponent(BarComponent)); t.false(entity.hasAllComponents([FooComponent, BarComponent])); t.deepEqual( - Object.values(entity.getComponents()).map(comp => comp.constructor), + Object.values(entity.getComponents()).map((comp) => comp.constructor), [BarComponent] ); @@ -54,12 +54,12 @@ test("adding/removing components sync", async t => { t.false(entity.hasComponent(BarComponent)); t.false(entity.hasAllComponents([FooComponent, BarComponent])); t.deepEqual( - Object.values(entity.getComponents()).map(comp => comp.constructor), + Object.values(entity.getComponents()).map((comp) => comp.constructor), [] ); }); -test("clearing pooled components", async t => { +test("clearing pooled components", async (t) => { var world, entity; // Component with no constructor @@ -67,7 +67,7 @@ test("clearing pooled components", async t => { class BazComponent extends Component {} BazComponent.schema = { - spam: { type: Types.String } + spam: { type: Types.String }, }; world = new World(); @@ -145,7 +145,7 @@ test("clearing pooled components", async t => { ); }); -test("removing components deferred", async t => { +test("removing components deferred", async (t) => { var world = new World(); world.registerComponent(FooComponent).registerComponent(BarComponent); @@ -162,11 +162,13 @@ test("removing components deferred", async t => { t.false(entity.hasComponent(FooComponent)); t.false(entity.hasComponent(BarComponent)); t.deepEqual( - Object.values(entity.getComponents()).map(comp => comp.constructor), + Object.values(entity.getComponents()).map((comp) => comp.constructor), [] ); t.deepEqual( - Object.values(entity.getComponentsToRemove()).map(comp => comp.constructor), + Object.values(entity.getComponentsToRemove()).map( + (comp) => comp.constructor + ), [FooComponent] ); @@ -174,12 +176,12 @@ test("removing components deferred", async t => { t.is(entity.getComponentTypes().length, 0); t.false(entity.hasComponent(FooComponent)); t.deepEqual( - Object.values(entity.getComponents()).map(comp => comp.constructor), + Object.values(entity.getComponents()).map((comp) => comp.constructor), [] ); }); -test("remove entity", async t => { +test("remove entity", async (t) => { var world = new World(); // Sync @@ -193,7 +195,7 @@ test("remove entity", async t => { t.is(world.entityManager.count(), 0); }); -test("get component development", async t => { +test("get component development", async (t) => { var world = new World(); world.registerComponent(FooComponent); @@ -215,7 +217,7 @@ test("get component development", async t => { t.throws(() => (removedComponent.variableFoo = 14)); }); -test("get component production", async t => { +test("get component production", async (t) => { const oldNodeEnv = process.env.NODE_ENV; process.env.NODE_ENV = "production"; var world = new World(); @@ -241,7 +243,7 @@ test("get component production", async t => { process.env.NODE_ENV = oldNodeEnv; }); -test("get removed component development", async t => { +test("get removed component development", async (t) => { var world = new World(); world.registerComponent(FooComponent); @@ -256,7 +258,7 @@ test("get removed component development", async t => { t.throws(() => (component.variableFoo = 4)); }); -test("get removed component production", async t => { +test("get removed component production", async (t) => { const oldNodeEnv = process.env.NODE_ENV; process.env.NODE_ENV = "production"; var world = new World(); @@ -275,7 +277,7 @@ test("get removed component production", async t => { process.env.NODE_ENV = oldNodeEnv; }); -test("get mutable component", async t => { +test("get mutable component", async (t) => { var world = new World(); world.registerComponent(FooComponent); @@ -290,7 +292,7 @@ test("get mutable component", async t => { t.deepEqual(entity.getMutableComponent(BarComponent), undefined); }); -test("Delete entity from entitiesByNames", async t => { +test("Delete entity from entitiesByNames", async (t) => { var world = new World(); // Sync diff --git a/test/unit/entitymanager.test.js b/test/unit/entitymanager.test.js index 352bb02a..479d368c 100644 --- a/test/unit/entitymanager.test.js +++ b/test/unit/entitymanager.test.js @@ -1,7 +1,7 @@ import test from "ava"; import { World } from "../../src"; -test("entity id", t => { +test("entity id", (t) => { var world = new World(); for (var i = 0; i < 10; i++) { @@ -13,7 +13,7 @@ test("entity id", t => { // @todo Check ids }); -test("deferred entity remove", t => { +test("deferred entity remove", (t) => { var world = new World(); for (let i = 0; i < 10; i++) { diff --git a/test/unit/objectpool.test.js b/test/unit/objectpool.test.js index c424f76d..5e545d11 100644 --- a/test/unit/objectpool.test.js +++ b/test/unit/objectpool.test.js @@ -4,16 +4,16 @@ import { TagComponent, World, Types, - ObjectPool + ObjectPool, } from "../../src/index"; -test("Detecting Pool", t => { +test("Detecting Pool", (t) => { var world = new World(); class NoPoolComponent extends Component {} class PoolComponent extends Component {} PoolComponent.schema = { - num: { type: Types.Number } + num: { type: Types.Number }, }; class PoolTagComponent extends TagComponent {} class CustomPoolComponent extends Component {} @@ -42,7 +42,7 @@ test("Detecting Pool", t => { ); }); -test("ObjectPool", t => { +test("ObjectPool", (t) => { var id = 0; class T extends Component { diff --git a/test/unit/stats.test.js b/test/unit/stats.test.js index ae2d895b..501c590c 100644 --- a/test/unit/stats.test.js +++ b/test/unit/stats.test.js @@ -2,14 +2,14 @@ import test from "ava"; import { World, System } from "../../src/index.js"; import { FooComponent, BarComponent } from "../helpers/components"; -test("Stats", async t => { +test("Stats", async (t) => { var world = new World(); class SystemA extends System {} SystemA.queries = { compFoo: { components: [FooComponent] }, compBar: { components: [BarComponent] }, - compBtoh: { components: [FooComponent, BarComponent] } + compBtoh: { components: [FooComponent, BarComponent] }, }; world @@ -33,41 +33,41 @@ test("Stats", async t => { queries: { 0: { numComponents: 1, - numEntities: 10 + numEntities: 10, }, 1: { numComponents: 1, - numEntities: 4 + numEntities: 4, }, "0-1": { numComponents: 2, - numEntities: 4 - } + numEntities: 4, + }, }, numComponentPool: 2, componentPool: { FooComponent: { used: 10, - size: 12 + size: 12, }, BarComponent: { used: 4, - size: 5 - } + size: 5, + }, }, eventDispatcher: { fired: 24, - handled: 0 - } + handled: 0, + }, }, system: { numSystems: 1, systems: { SystemA: { queries: {}, - executeTime: 0 - } - } - } + executeTime: 0, + }, + }, + }, }); }); diff --git a/test/unit/system.test.js b/test/unit/system.test.js index 4cf0d7f1..9f227694 100644 --- a/test/unit/system.test.js +++ b/test/unit/system.test.js @@ -3,7 +3,7 @@ import { World, System, Not, Component } from "../../src/index.js"; import { FooComponent, BarComponent, - EmptyComponent + EmptyComponent, } from "../helpers/components"; /* test("Initialize", t => { @@ -48,7 +48,7 @@ test("Initialize", t => { }); */ -test("Empty queries", t => { +test("Empty queries", (t) => { var world = new World(); // System 1 @@ -63,14 +63,14 @@ test("Empty queries", t => { class SystemEmpty3 extends System {} SystemEmpty3.queries = { - entities: {} + entities: {}, }; // System 4 class SystemEmpty4 extends System {} SystemEmpty4.queries = { - entities: { components: [] } + entities: { components: [] }, }; // Register empty system @@ -90,7 +90,7 @@ test("Empty queries", t => { t.is(error2.message, "'components' attribute can't be empty in a query"); }); -test("Queries", t => { +test("Queries", (t) => { var world = new World(); world @@ -108,19 +108,19 @@ test("Queries", t => { class SystemFoo extends System {} SystemFoo.queries = { - entities: { components: [FooComponent] } + entities: { components: [FooComponent] }, }; class SystemBar extends System {} SystemBar.queries = { - entities: { components: [BarComponent] } + entities: { components: [BarComponent] }, }; class SystemBoth extends System {} SystemBoth.queries = { - entities: { components: [FooComponent, BarComponent] } + entities: { components: [FooComponent, BarComponent] }, }; world @@ -145,7 +145,7 @@ test("Queries", t => { ); }); -test("Queries with 'Not' operator", t => { +test("Queries with 'Not' operator", (t) => { var world = new World(); world @@ -166,7 +166,7 @@ test("Queries with 'Not' operator", t => { class SystemNotNot extends System {} SystemNotNot.queries = { - notFoo: { components: [Not(FooComponent), Not(BarComponent)] } + notFoo: { components: [Not(FooComponent), Not(BarComponent)] }, }; const error = t.throws(() => { @@ -181,8 +181,8 @@ test("Queries with 'Not' operator", t => { fooNotBar: { components: [FooComponent, Not(BarComponent)] }, emptyNotBar: { components: [EmptyComponent, Not(BarComponent)] }, emptyNotBarFoo: { - components: [EmptyComponent, Not(BarComponent), Not(FooComponent)] - } + components: [EmptyComponent, Not(BarComponent), Not(FooComponent)], + }, }; world.registerSystem(SystemNotBar); @@ -203,7 +203,7 @@ test("Queries with 'Not' operator", t => { t.is(queries.emptyNotBar.results.length, 5); }); -test("Queries with sync removal", t => { +test("Queries with sync removal", (t) => { var world = new World(); world.registerComponent(FooComponent).registerComponent(BarComponent); @@ -228,9 +228,9 @@ test("Queries with sync removal", t => { entities: { components: [FooComponent], listen: { - removed: true - } - } + removed: true, + }, + }, }; class SystemB extends System { @@ -246,9 +246,9 @@ test("Queries with sync removal", t => { entities: { components: [FooComponent], listen: { - removed: true - } - } + removed: true, + }, + }, }; world.registerSystem(SystemA).registerSystem(SystemB); @@ -283,7 +283,7 @@ test("Queries with sync removal", t => { t.is(entitiesRemovedB.length, 8); }); -test("Queries with deferred removal", t => { +test("Queries with deferred removal", (t) => { var world = new World(); world @@ -308,9 +308,9 @@ test("Queries with deferred removal", t => { entities: { components: [FooComponent], listen: { - removed: true - } - } + removed: true, + }, + }, }; class SystemFB extends System { @@ -327,9 +327,9 @@ test("Queries with deferred removal", t => { entities: { components: [FooComponent, BarComponent], listen: { - removed: true - } - } + removed: true, + }, + }, }; class SystemB extends System {} @@ -338,9 +338,9 @@ test("Queries with deferred removal", t => { entities: { components: [BarComponent], listen: { - removed: true - } - } + removed: true, + }, + }, }; world @@ -399,7 +399,7 @@ test("Queries with deferred removal", t => { t.is(world.entityManager._entities.length, 2); }); -test("Queries removing multiple components", t => { +test("Queries removing multiple components", (t) => { var world = new World(); world @@ -414,7 +414,7 @@ test("Queries removing multiple components", t => { class SystemA extends System { execute() { - this.queries.entities.removed.forEach(entity => { + this.queries.entities.removed.forEach((entity) => { t.false(entity.hasComponent(FooComponent)); t.true(entity.hasRemovedComponent(FooComponent)); }); @@ -428,12 +428,12 @@ test("Queries removing multiple components", t => { entities: { components: [FooComponent, BarComponent], listen: { - removed: true - } + removed: true, + }, }, notTest: { - components: [Not(FooComponent), BarComponent, EmptyComponent] - } + components: [Not(FooComponent), BarComponent, EmptyComponent], + }, }; world.registerSystem(SystemA); @@ -485,7 +485,7 @@ test("Queries removing multiple components", t => { t.is(world.entityManager.entitiesToRemove.length, 0); }); -test("Querries removing deferred components", t => { +test("Querries removing deferred components", (t) => { var world = new World(); world.registerComponent(FooComponent).registerComponent(BarComponent); @@ -506,9 +506,9 @@ test("Querries removing deferred components", t => { entities: { components: [FooComponent], listen: { - removed: true - } - } + removed: true, + }, + }, }; class SystemFB extends System { @@ -525,9 +525,9 @@ test("Querries removing deferred components", t => { entities: { components: [FooComponent, BarComponent], listen: { - removed: true - } - } + removed: true, + }, + }, }; class SystemB extends System {} @@ -536,9 +536,9 @@ test("Querries removing deferred components", t => { entities: { components: [BarComponent], listen: { - removed: true - } - } + removed: true, + }, + }, }; world @@ -599,7 +599,7 @@ test("Querries removing deferred components", t => { t.is(world.entityManager.entitiesWithComponentsToRemove.length, 0); }); -test("Reactive", t => { +test("Reactive", (t) => { var world = new World(); class ReactiveSystem extends System { @@ -612,9 +612,9 @@ test("Reactive", t => { listen: { added: true, removed: true, - changed: [FooComponent, BarComponent] - } - } + changed: [FooComponent, BarComponent], + }, + }, }; // Register empty system @@ -623,10 +623,7 @@ test("Reactive", t => { world.registerComponent(FooComponent).registerComponent(BarComponent); for (var i = 0; i < 15; i++) { - world - .createEntity() - .addComponent(FooComponent) - .addComponent(BarComponent); + world.createEntity().addComponent(FooComponent).addComponent(BarComponent); } var system = world.systemManager.getSystems()[0]; @@ -643,10 +640,7 @@ test("Reactive", t => { system.clearEvents(); // Add a new one - world - .createEntity() - .addComponent(FooComponent) - .addComponent(BarComponent); + world.createEntity().addComponent(FooComponent).addComponent(BarComponent); t.is(query.added.length, 1); world.execute(); // After execute, events should be cleared @@ -714,11 +708,11 @@ test("Reactive", t => { t.is(query.removed.length, 0); }); -test("Queries with 'mandatory' parameter", t => { +test("Queries with 'mandatory' parameter", (t) => { var counter = { a: 0, b: 0, - c: 0 + c: 0, }; class SystemA extends System { @@ -728,7 +722,7 @@ test("Queries with 'mandatory' parameter", t => { } SystemA.queries = { - entities: { components: [FooComponent], mandatory: false } + entities: { components: [FooComponent], mandatory: false }, }; class SystemB extends System { @@ -738,7 +732,7 @@ test("Queries with 'mandatory' parameter", t => { } SystemB.queries = { - entities: { components: [FooComponent], mandatory: true } + entities: { components: [FooComponent], mandatory: true }, }; class SystemC extends System { @@ -748,7 +742,7 @@ test("Queries with 'mandatory' parameter", t => { } SystemC.queries = { - entities: { components: [BarComponent], mandatory: true } + entities: { components: [BarComponent], mandatory: true }, }; // ------- @@ -782,7 +776,7 @@ test("Queries with 'mandatory' parameter", t => { t.deepEqual(counter, { a: 4, b: 2, c: 2 }); }); -test("Get Systems", t => { +test("Get Systems", (t) => { var world = new World(); class SystemA extends System {} @@ -800,7 +794,7 @@ test("Get Systems", t => { t.deepEqual(systems, world.systemManager._systems); }); -test("Systems without queries", t => { +test("Systems without queries", (t) => { var world = new World(); var counter = 0; @@ -820,7 +814,7 @@ test("Systems without queries", t => { t.is(counter, 10); }); -test("Systems with component case sensitive", t => { +test("Systems with component case sensitive", (t) => { var world = new World(); class A extends Component {} @@ -866,14 +860,14 @@ test("Systems with component case sensitive", t => { t.deepEqual(counter, { a: 2, A: 2 }); }); -test("Components with the the same name in uppercase and lowercase", t => { +test("Components with the the same name in uppercase and lowercase", (t) => { class B extends Component {} class b extends Component {} class S extends System { execute() { - this.queries.S.results.forEach(entity => + this.queries.S.results.forEach((entity) => console.log(entity.getComponents()) ); } @@ -885,27 +879,24 @@ test("Components with the the same name in uppercase and lowercase", t => { world.registerComponent(B).registerComponent(b); world.registerSystem(S); - world - .createEntity() - .addComponent(B) - .addComponent(b); + world.createEntity().addComponent(B).addComponent(b); let query = world.getSystem(S).queries.S; let entity = query.results[0]; let components = entity.getComponents(); t.deepEqual( - Object.keys(components).map(c => parseInt(c)), + Object.keys(components).map((c) => parseInt(c)), [B._typeId, b._typeId] ); t.deepEqual( - Object.values(components).map(c => c.getName()), + Object.values(components).map((c) => c.getName()), ["B", "b"] ); }); -test("Unregister systems", t => { +test("Unregister systems", (t) => { class SystemA extends System {} class SystemB extends System { @@ -927,7 +918,7 @@ test("Unregister systems", t => { t.is(world.systemManager._executeSystems.length, 0); }); -test("Register a system that does not extend System", t => { +test("Register a system that does not extend System", (t) => { class SystemA {} const world = new World(); diff --git a/test/unit/systemstatecomponents.test.js b/test/unit/systemstatecomponents.test.js index 3d7f1c00..cacb1b40 100644 --- a/test/unit/systemstatecomponents.test.js +++ b/test/unit/systemstatecomponents.test.js @@ -3,7 +3,7 @@ import test from "ava"; import { World, Not, System, SystemStateComponent } from "../../src/index.js"; import { FooComponent } from "../helpers/components"; -test("reset", t => { +test("reset", (t) => { var world = new World(); class StateComponentA extends SystemStateComponent {} @@ -13,11 +13,11 @@ test("reset", t => { class SystemA extends System { execute() { - this.queries.added.results.forEach(entity => { + this.queries.added.results.forEach((entity) => { entity.addComponent(StateComponentA); }); - this.queries.remove.results.forEach(entity => { + this.queries.remove.results.forEach((entity) => { entity.removeComponent(StateComponentA); }); @@ -30,7 +30,7 @@ test("reset", t => { SystemA.queries = { added: { components: [FooComponent, Not(StateComponentA)] }, remove: { components: [Not(FooComponent), StateComponentA] }, - normal: { components: [FooComponent, StateComponentA] } + normal: { components: [FooComponent, StateComponentA] }, }; world.registerSystem(SystemA); From e469d7ca702774e80f0386c2c7787c159b08755b Mon Sep 17 00:00:00 2001 From: shawticus Date: Tue, 4 Aug 2020 19:56:42 -0700 Subject: [PATCH 45/56] Add ;s to pass linting --- src/ComponentManager.js | 2 +- src/World.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ComponentManager.js b/src/ComponentManager.js index 385f7f7c..ca0f3b28 100644 --- a/src/ComponentManager.js +++ b/src/ComponentManager.js @@ -11,7 +11,7 @@ export class ComponentManager { } hasComponent(Component) { - return this.Components.indexOf(Component) !== -1 + return this.Components.indexOf(Component) !== -1; } registerComponent(Component, objectPool) { diff --git a/src/World.js b/src/World.js index 95c26c2e..15be2e3d 100644 --- a/src/World.js +++ b/src/World.js @@ -43,7 +43,7 @@ export class World { } hasRegisteredComponent(Component) { - return this.componentsManager.hasComponent(Component) + return this.componentsManager.hasComponent(Component); } unregisterSystem(System) { From 5c6e7dcf21467c1e47377da57d04c775b5ee2349 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Wed, 5 Aug 2020 11:00:02 -0700 Subject: [PATCH 46/56] Make getComponent etc. use optional return types --- src/Entity.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Entity.d.ts b/src/Entity.d.ts index f47315c6..cf601c8b 100644 --- a/src/Entity.d.ts +++ b/src/Entity.d.ts @@ -19,7 +19,7 @@ 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>( + getComponent?>( Component: ComponentConstructor, includeRemoved?: boolean ): Readonly; @@ -27,7 +27,7 @@ export class Entity { /** * Get a component that is slated to be removed from this entity. */ - getRemovedComponent>( + getRemovedComponent?>( Component: ComponentConstructor ): Readonly; @@ -50,7 +50,7 @@ export class Entity { * Get a mutable reference to a component on this entity. * @param Component Type of component to get */ - getMutableComponent>( + getMutableComponent?>( Component: ComponentConstructor ): C; From 17677987e094d983244ea32e59ecd0a44aa67cc7 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Wed, 5 Aug 2020 11:00:23 -0700 Subject: [PATCH 47/56] Add queries to system constructor --- src/System.d.ts | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/System.d.ts b/src/System.d.ts index 4dd3fabf..bf249bd5 100644 --- a/src/System.d.ts +++ b/src/System.d.ts @@ -7,6 +7,17 @@ interface Attributes { [propName: string]: any; } +export interface SystemQueries { + [queryName: string]: { + components: (ComponentConstructor | NotComponent)[], + listen?: { + added?: boolean, + removed?: boolean, + changed?: boolean | ComponentConstructor[], + }, + } +} + /** * A system that manipulates entities in the world. */ @@ -15,16 +26,7 @@ export abstract class System { * Defines what Components the System will query for. * This needs to be user defined. */ - static queries: { - [queryName: string]: { - components: (ComponentConstructor | NotComponent)[], - listen?: { - added?: boolean, - removed?: boolean, - changed?: boolean | ComponentConstructor[], - }, - } - }; + static queries: SystemQueries; static isSystem: true; @@ -69,6 +71,7 @@ export abstract class System { export interface SystemConstructor { isSystem: true; + queries: SystemQueries new (...args: any): T; } From 695a72044c3671c4a21c3e44cf404faa77e4a283 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Wed, 5 Aug 2020 20:30:30 +0200 Subject: [PATCH 48/56] Update AWESOME_ECSY.md --- AWESOME_ECSY.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/AWESOME_ECSY.md b/AWESOME_ECSY.md index 56148237..27381b08 100644 --- a/AWESOME_ECSY.md +++ b/AWESOME_ECSY.md @@ -6,15 +6,48 @@ Feel free to submit PRs with more links! ## Components & Systems - ECSY Two, uses HTML canvas instead of WebGL: https://github.com/joshmarinacci/ecsy-two/ +- ECSY Threejs: https://github.com/MozillaReality/ecsy-three +- ECSY Physics system https://github.com/HydraFire/ecsy-physics-system +- ECSY Networking: https://github.com/xr3ngine/ecsy-networking +- ECSY Babylon: https://github.com/megavr/ecsy-babylon +- ECSY Ember Babylon: https://github.com/kaliber5/ember-ecsy-babylon +- ECSY Babylon: https://github.com/kaliber5/ecsy-babylon +- ECSY Babylon examples: https://github.com/megavr/ecsy-babylon-examples +- ECSY Actors (Input action handling, state management, axis blending and event output for character controllers and stateful, interactive objects): https://github.com/xr3ngine/ecsy-actors + +## Engines built on top of ECSY +- XREngine: https://github.com/xr3ngine/xr3ngine +- HekoJS https://github.com/hekojs/core +- A Behavioral ECS game engine https://github.com/xr3ngine/armada ## Demos, games and applications - Hello WebXR: https://mixedreality.mozilla.org/hello-webxr - Jumpy Balls: https://mixedreality.mozilla.org/jumpy-balls - Steam Dungeon: http://steamdungeon.com/ +- ETLU - Extract Transform Load Universe: https://github.com/aaronanderson/etlu +- Thermal runway. code: https://github.com/macaco-maluco/thermal-runway game: https://thermalrunway.macacomaluco.space/ +- NES emulator with ECSY and Rust: https://github.com/takahirox/nes-rust-ecsy +- Jingle Mash https://github.com/joshmarinacci/jinglesmash-ecs +- WebXR Simgame https://github.com/joshmarinacci/webxr-simgame +- Vitruvius 2 player game https://github.com/terrygonguet/vitruvius +- Space shooter multiplayer: https://github.com/nickyvanurk/3d-multiplayer-browser-shooter +- Beat pads music game https://github.com/fernandojsg/beatpads +- Soft body simulation https://github.com/philsawicki/soft-body-simulation +- VoxelJS Next https://github.com/joshmarinacci/voxeljs-next +- Grapher https://github.com/SolarLiner/grapher +- Conquer covid https://github.com/loqwai/conquer-covid +- ECSY babylon study https://github.com/pocka/ecsy-babylonjs-study ## Tools - ECSY Devtools: https://github.com/MozillaReality/ecsy-devtools +## Boilerplates +- ECSY webpack: https://github.com/MozillaReality/ecsy-webpack-boilerplate +- ECSY-three webpack: https://github.com/MozillaReality/ecsy-three-webpack +- ECSY typescript: https://github.com/robertlong/ecsy-typescript-boilerplate +- ECSY Pixi: https://github.com/manueldeval/ecsypixi +- ECSY React https://github.com/jmswrnr/react-ecs + ## Articles - ECSY Devtools introduction: https://blog.mozvr.com/ecsy-developer-tools/ - Introducing ECSY: https://blog.mozvr.com/introducing-ecsy/ From 50f850bce3a6f2c62bf6a0587683f2de25e57d11 Mon Sep 17 00:00:00 2001 From: shawticus Date: Wed, 5 Aug 2020 19:16:01 -0700 Subject: [PATCH 49/56] Added init to System.d.ts --- src/System.d.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/System.d.ts b/src/System.d.ts index 4dd3fabf..a777903f 100644 --- a/src/System.d.ts +++ b/src/System.d.ts @@ -44,10 +44,17 @@ export abstract class System { } world: World; + /** * Whether the system will execute during the world tick. */ enabled: boolean; + + /** + * Called when the system is added to the world. + */ + abstract init(attributes?: Attributes): void + /** * Resume execution of this system. */ From 74a5f34e616a7a3f3f546c6403a749662dd85185 Mon Sep 17 00:00:00 2001 From: shawticus Date: Thu, 6 Aug 2020 00:57:33 -0700 Subject: [PATCH 50/56] Shouldn't be abstract --- src/System.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.d.ts b/src/System.d.ts index a777903f..f4f99754 100644 --- a/src/System.d.ts +++ b/src/System.d.ts @@ -53,7 +53,7 @@ export abstract class System { /** * Called when the system is added to the world. */ - abstract init(attributes?: Attributes): void + init(attributes?: Attributes): void /** * Resume execution of this system. From a12bf6e99592159281a3cc2248ccdb49bc59f3b0 Mon Sep 17 00:00:00 2001 From: Endel Dreyer Date: Sun, 2 Aug 2020 09:28:02 -0300 Subject: [PATCH 51/56] fixes .set() to use only 2 arguments --- site/examples/canvas/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/examples/canvas/index.html b/site/examples/canvas/index.html index 0b2fee4a..33f6e2ba 100644 --- a/site/examples/canvas/index.html +++ b/site/examples/canvas/index.html @@ -52,11 +52,11 @@ .addComponent(Movement); var circle = entity.getMutableComponent(Circle); - circle.position.set(random(0, canvas.width), random(0, canvas.height), 0); + circle.position.set(random(0, canvas.width), random(0, canvas.height)); circle.radius = random(20, 100); var movement = entity.getMutableComponent(Movement); - movement.velocity.set(random(-20, 20), random(-20, 20), 0); + movement.velocity.set(random(-20, 20), random(-20, 20)); } window.world = world; @@ -82,4 +82,4 @@ - \ No newline at end of file + From 25c9a04219a3b1d013b21a01814ec02286d1d8f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2020 23:27:24 +0000 Subject: [PATCH 52/56] Bump prismjs from 1.17.1 to 1.21.0 Bumps [prismjs](https://github.com/PrismJS/prism) from 1.17.1 to 1.21.0. - [Release notes](https://github.com/PrismJS/prism/releases) - [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md) - [Commits](https://github.com/PrismJS/prism/compare/v1.17.1...v1.21.0) Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 75c69ad1..fdc8a09a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8788,9 +8788,9 @@ } }, "prismjs": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz", - "integrity": "sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.21.0.tgz", + "integrity": "sha512-uGdSIu1nk3kej2iZsLyDoJ7e9bnPzIgY0naW/HdknGj61zScaprVEVGHrPoXqI+M9sP0NDnTK2jpkvmldpuqDw==", "dev": true, "requires": { "clipboard": "^2.0.0" From d577ea096c862aceda375c3d0f6f25da05a0b004 Mon Sep 17 00:00:00 2001 From: Paulo Ragonha Date: Sat, 8 Aug 2020 13:57:35 +0200 Subject: [PATCH 53/56] Fix Entity type definition This seems to have been accidentally introduced recently, but it is my understanding that we want the return of the function to be optional, and not the function itself. It could also been my erroneous interpretation. --- src/Entity.d.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Entity.d.ts b/src/Entity.d.ts index cf601c8b..1271b063 100644 --- a/src/Entity.d.ts +++ b/src/Entity.d.ts @@ -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?>( + getComponent>( Component: ComponentConstructor, includeRemoved?: boolean - ): Readonly; + ): Readonly | undefined; /** * Get a component that is slated to be removed from this entity. */ - getRemovedComponent?>( + getRemovedComponent>( Component: ComponentConstructor - ): Readonly; + ): Readonly | undefined; /** * Get an object containing all the components on this entity, where the object keys are the component types. @@ -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?>( + getMutableComponent>( Component: ComponentConstructor - ): C; + ): C | undefined; /** * Add a component to the entity. From 7596fa9ec7f49e1cb9c25bf30ccc57292f21d3be Mon Sep 17 00:00:00 2001 From: David Peicho Date: Fri, 21 Aug 2020 11:16:03 +0100 Subject: [PATCH 54/56] types: system: add as readonly attribute --- src/System.d.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/System.d.ts b/src/System.d.ts index d0fb185e..47ff738b 100644 --- a/src/System.d.ts +++ b/src/System.d.ts @@ -52,6 +52,11 @@ export abstract class System { */ enabled: boolean; + /** + * Execution priority (i.e: order) of the system. + */ + readonly priority: number; + /** * Called when the system is added to the world. */ From 2dbe7c58e4cf7c919f743cd3ca2b5beb91cf2ab1 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Thu, 6 Aug 2020 14:52:25 +0200 Subject: [PATCH 55/56] 0.4.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index fdc8a09a..cf2eb457 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ecsy", - "version": "0.3.2", + "version": "0.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e62512c8..ffeae41a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ecsy", - "version": "0.3.2", + "version": "0.4.0", "description": "Entity Component System in JS", "main": "lib/index.js", "module": "src/index.js", From 498fb643acbd1adb0f3356f34c1e6ca45bec07e9 Mon Sep 17 00:00:00 2001 From: Fernando Serrano Date: Fri, 21 Aug 2020 21:37:24 +0200 Subject: [PATCH 56/56] 0.4.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index cf2eb457..211cd5aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ecsy", - "version": "0.4.0", + "version": "0.4.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index ffeae41a..92a2b09c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ecsy", - "version": "0.4.0", + "version": "0.4.1", "description": "Entity Component System in JS", "main": "lib/index.js", "module": "src/index.js",