{"id":14155367,"url":"https://github.com/NSSTC/sim-ecs","last_synced_at":"2025-08-06T01:31:20.299Z","repository":{"id":37993668,"uuid":"203428586","full_name":"NSSTC/sim-ecs","owner":"NSSTC","description":"Batteries included TypeScript ECS","archived":false,"fork":false,"pushed_at":"2024-04-11T18:12:19.000Z","size":3564,"stargazers_count":71,"open_issues_count":8,"forks_count":11,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-04-11T23:55:01.686Z","etag":null,"topics":["ecs","ecs-libraries","entities","entity-component-system","game-development","game-engine","game-engine-library","javascript","javascript-library","js","prefabs","sim-ecs","simulation","typescript"],"latest_commit_sha":null,"homepage":"https://nsstc.github.io/sim-ecs/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NSSTC.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2019-08-20T18:04:19.000Z","updated_at":"2024-04-14T16:58:03.850Z","dependencies_parsed_at":"2023-10-01T16:38:54.708Z","dependency_job_id":"5701473a-d495-4d38-94a7-945f88264b86","html_url":"https://github.com/NSSTC/sim-ecs","commit_stats":{"total_commits":308,"total_committers":2,"mean_commits":154.0,"dds":0.01948051948051943,"last_synced_commit":"66557339056095ba9d21cf1d95ecaf7e00590229"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NSSTC%2Fsim-ecs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NSSTC%2Fsim-ecs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NSSTC%2Fsim-ecs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NSSTC%2Fsim-ecs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NSSTC","download_url":"https://codeload.github.com/NSSTC/sim-ecs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228821404,"owners_count":17977166,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ecs","ecs-libraries","entities","entity-component-system","game-development","game-engine","game-engine-library","javascript","javascript-library","js","prefabs","sim-ecs","simulation","typescript"],"created_at":"2024-08-17T08:02:59.409Z","updated_at":"2024-12-09T02:31:20.795Z","avatar_url":"https://github.com/NSSTC.png","language":"TypeScript","funding_links":[],"categories":["typescript"],"sub_categories":[],"readme":"# sim-ecs\n\nA type-based, components-first, fully async batteries-included ECS, which is optimized for simulation needs.\nThere's a big emphasis on developer experience, like type-hinting and auto-completions. \nSim-ecs will run in NodeJS, Deno, BunJS and the browser.\n\nIt can be installed using your favorite package manager, for example:\n\n```shell\n$ npm install sim-ecs\n```\n\nor used as direct import for Deno:\n\n```typescript\nimport * as simEcs from \"https://deno.land/x/sim_ecs@v0.6.4/src/index.ts\";\n```\n\n\n---\n\n\n- [Considerations](#considerations)\n- [Why use sim-ecs](#why-use-sim-ecs)\n    - [Runtime requirements](#runtime-requirements)\n- [Examples](#examples)\n    - [Counter](#counter)\n    - [Events](#events)\n    - [Pong](#pong)\n    - [System Error](#system-error)\n- [Where is the Documentation](#where-is-the-documentation)\n    - [Creating the ECS and a World](#creating-the-ecs-and-a-world)\n        - [Prepare-Time World](#prepare-time-world)\n        - [Runtime World](#runtime-world)\n- [Setting Resources](#setting-resources)\n    - [Defining Systems](#defining-systems)\n      - [System Parameter Types](#system-parameter-types)\n      - [Hot Reloading Systems](#hot-reloading-systems)\n- [Defining Components](#defining-components)\n- [Adding Entities](#adding-entities)\n- [Working with States](#working-with-states--optional-)\n- [Update loop](#Update-loop)\n- [Commands](#commands)\n- [Saving and using Prefabs](#saving-and-using-prefabs)\n- [Syncing instances](#syncing-instances)\n- [Building for Production](#building-for-production)\n- [Plugins](#plugins)\n- [Performance](#performance)\n    - [The Benchmarks](#the-benchmarks)\n    - [The Result](#the-result)\n\n\n## Considerations\n\nThis ECS is inspired by SPECS and bevy-ecs (two Rust ECS libraries), however optimized for TypeScript.\nIt is built for easy usage (DX) and high iteration speed.\nThe trade-off is that insertion and deletion are slower,\nhowever there are optimizations and opinionations in place to still make it fast.\nFor example, by using commands, these operations are batched and executed when it is safe to perform them.\n\nIn order to create optimized simulation runs, the ECS has to be fully specified in the beginning.\nAll components, systems and queries need to be registered at the start.\n\n\n### AoS vs SoA\n\nSim-ecs uses the AoS (Array of Structs) approach, because it leads to much more control on the library side,\nand better lends itself to the promises we make about sim-ecs. The result is a polished experience with simple usage.\n\nAn SoA (Struct of Arrays) on the other hand is an approach famously used by bitecs to get performance out of\nlow-level mechanics in JS. This means it has overall better raw performance, but puts a lot of responsibilities on the \nlib-users' side. This leads to more time spend developing ecs features, which already exist in sim-ecs.\n\n\n## Why use sim-ecs\n\nSim-ecs was created out of the lack of a fast, featured ECS library for TypeScript, which is able to handle\nthe requirements in a big, agile game project. While there are other ECS libraries available,\nthey do not necessarily cater to that goal and will have short-comings in such an environment.\n\nSim-ecs comes with batteries included to make sure everything fits together and is simple to use.\nThe focus is on developer-friendliness (by supporting full type/intention support in the IDE),\nsupport for a variety of scenarios and performance.\n\nSince sim-ecs implements many bevy-ecs RFCs, it is very featured and modern. It can be used inside a generic game engine\nor for a game directly.\n\n\n### Runtime requirements\n\nIf using the prebuilt library, \"ES2020\" was selected as the build target. Hence, this is the matrix:\n\n| App              | Version | Comment                     |\n|------------------|---------|-----------------------------|\n| Chrome           | 80+     | Desktop and mobile          |\n| Edge             | 80+     |                             |\n| Safari           | 14.1+   | Desktop and mobile          |\n| Firefox          | 80+     | Desktop (no info on mobile) |\n| Opera            | 67+     | Desktop                     |\n| Samsung Internet | 13.0+   |                             |\n| NodeJS           | 14+     |                             |\n| Deno             | 1.0+    |                             |\n| Bun              | 0.2.2+  |                             |\n\n\n## Examples\n\nFor quickly seeing the ECS in action, there are several examples available.\nYou can find them in the `/examples` directory.\n\n\n### Counter\n\n```\n$ npm run example-counter\n``` \n\nThe counter example is a very small, minimal example to get a quick overview.\nIt increases a number a few times and then terminates.\n\n\n### Events\n\n```\n$ npm run example-events\n``` \n\nThe events example demonstrates how to use the event bus to write and read events.\nIt will print a message every second.\n\n\n### Pong\n\n![Pong can be played with the keyboard and saves on pausing.](./media/pong.png)\n\n```\n$ cd examples/pong \u0026\u0026 npm install \u0026\u0026 npm run start\n``` \n\nPong is a full game which can be run in the browser. It demonstrates all features of sim-ecs.\nIt comes with numerous components and systems, handles states and makes use of prefabs and saves.\nSince it is an ECS demo, other parts of the game code may be minimal, like rendering and sound.\nIt is recommended to use readily available libraries for these parts for any real endeavour, like BabylonJS.\n\nYou will need to build Pong from its directory.\nThen, you can open the `index.html` in the public folder to run the game.\n\n\n### System Error\n\n![On error, detailed information including the system's name can be retrieved](./media/error.png)\n\n```\n$ npm run example-system-error\n``` \n\nError handling is very simple with sim-ecs. It uses the events system to catch and provide handling opportunities\nwithout aborting the execution.\nThe System-Error example demonstrates how error handling works with a simple example.\n\n\n## Where is the Documentation\n\nAnything which is not explained in detail enough in this README can be found in the code.\nYou will notice that there are spec-files. These contain the specification for a certain class,\nwhich usually means an interface with comments on what the methods do.\n\nAlso, there is a [generated API-documentation](https://nsstc.github.io/sim-ecs/) available!\n\n\n## Creating the ECS and a World\n\nIn an ECS, a world is like a container for entities.\nSim-ecs comes, by default, with two variants: A prepare-time world and a runtime world.\n\n_See\n[\"Counter\" example](https://github.com/NSSTC/sim-ecs/blob/master/examples/counter.ts)\n\u0026nbsp;_\n\n\n### Prepare-Time World\n\nThe prepare-time world is a place which focuses on easily preparing a simulation.\nThat means, this is the place where everything should be defined and readied.\n\n```typescript\nconst prepWorld = buildWorld().build();\n```\n\n_See\n[IPreptimeWorld](https://nsstc.github.io/sim-ecs/interfaces/IPreptimeWorld.html),\n[\"Counter\" example](https://github.com/NSSTC/sim-ecs/blob/master/examples/counter.ts)\n\u0026nbsp;_\n\n\n### Runtime world\n\nAfter the preparation is done, a runtime world can be forked, which is optimized for executing a simulation.\nOne of the main differences is that this world is not as configurable,\nin order to optimize for what was set up in the prep-time world.\n\n```typescript\nconst runWorld = await prepWorld.prepareRun();\n```\n\n_See\n[IRuntimeWorld](https://nsstc.github.io/sim-ecs/interfaces/IRuntimeWorld.html),\n[\"Counter\" example](https://github.com/NSSTC/sim-ecs/blob/master/examples/counter.ts)\n\u0026nbsp;_\n\n\n## Scheduling a run\n\nIn sim-ecs, a run has to be planned ahead. This is done by giving a developer the means to put systems into stages\nand then decide in which order stages should run and if they run in parallel.\n\nOne thing to add is that a pipeline, which contains the entire program order, is made up of \"Sync Points\". These\nconstructs allow for hooking into the plan in a non-destructive way. For example third-party code (like plugins)\ncan make use of such a feature to add their own Systems at the right place in the program chronology.\nIf that's not necessary, sim-ecs will work fine with just the `root` Sync Point.\n\n```typescript\nconst prepWorld = buildWorld()\n    .withDefaultScheduling(root =\u003e root\n        .addNewStage(stage =\u003e stage\n            .addSystem(CounterSystem)\n        )\n    )\n    .build();\n```\n\nSince this is a very verbose way, sim-ecs also adds a data-driven approach,\nwhich enables schedules to be stored as simple arrays which can even be loaded without logic.\nThe Pong example demonstrates this by providing several schedules, stored as separate data. In short:\n\n```typescript\nimport {buildWorld, ISyncPointPrefab} from \"sim-ecs\";\n\nconst gameSchedule: ISyncPointPrefab = {\n    stages: [\n        // Stages are executed sequentially (order guaranteed!)\n        [BeforeStepSystem],\n        [InputSystem],\n        [\n            // Systems inside a stage are executed in parallel, if possible (no order guaranteed!)\n            MenuSystem,\n            PaddleSystem,\n            PauseSystem,\n        ],\n        [CollisionSystem],\n        [BallSystem],\n        [AnimationSystem],\n        [\n            RenderGameSystem,\n            RenderUISystem,\n        ],\n        [ErrorSystem],\n    ],\n};\n\nconst prepWorld = buildWorld()\n    .withDefaultScheduling(root =\u003e root.fromPrefab(gameSchedule))\n    .build();\n```\n\n_See\n[\"Counter\" example](https://github.com/NSSTC/sim-ecs/blob/master/examples/counter.ts),\n[\"Pong\" example](https://github.com/NSSTC/sim-ecs/tree/master/examples/pong)\n\u0026nbsp;_\n\n\n## Setting Resources\n\nResources are objects, which can hold certain data, like the start DateTime.\n\n```typescript\n// this call implicitely creates a new object of type Date. You can also pass an instance instead.\n// you can pass arguments to the constructor by passing them as additional parameters here\nprepWorld.addResource(Date);\nconsole.log(world.getResource(Date).getDate());\n```\n\n_See\n[addResource()](https://nsstc.github.io/sim-ecs/interfaces/IMutableWorld.html#addResource),\n[getResource()](https://nsstc.github.io/sim-ecs/interfaces/IWorld.html#getResource),\n[getResources()](https://nsstc.github.io/sim-ecs/interfaces/IWorld.html#getResources)\n\u0026nbsp;_\n\n## Defining Systems\n\nSystems are the logic, which operates on data sets (components).\nThey are logic building blocks which separate concerns and make the world move.\n\n```typescript\nconst CountSystem = createSystem({\n    query: queryComponents({counterObj: Write(Counter)}),\n})\n    // this function is called every time the world needs to be updated. Put your logic in there\n    .withRunFunction(({query}) =\u003e\n        query.execute(({counterObj}) =\u003e console.log(++counterObj.a))\n    )\n    .build();\n```\n\n_See\n[createSystem()](https://nsstc.github.io/sim-ecs/functions/createSystem.html),\n[ISystemBuilder](https://nsstc.github.io/sim-ecs/classes/SystemBuilder.html),\n[\"Counter\" example](https://github.com/NSSTC/sim-ecs/blob/master/examples/counter.ts),\n[\"Pong\" example](https://github.com/NSSTC/sim-ecs/tree/master/examples/pong)\n\u0026nbsp;_\n\n\n### System Parameter Types\n\nA system can request different types of parameter:\n\n\n```typescript\nconst CountSystem = createSystem({\n    // Queries are most obvious, since they allow access to stored data\n    // All parameters form the query, and only entities which match all criteria will be picked\n    query: queryComponents({\n        // Access the entity matching this query\n        entity: ReadEntity(),\n        // Access to a component\n        counterObjR: Read(Counter),\n        // This component may or may not exist, but if it does, it's readonly\n        counterObjRO: ReadOptional(Counter),\n        // Access a component in a mutable way\n        counterObjW: Write(Counter),\n        // This component may or may not exist, but if it does, it's mutable\n        counterObjWO: WriteOptional(Counter),\n        // If the component itself doesn't matter, but it must exist on the entity, this is the way!\n        _counterObjWith: With(Counter),\n        // It's also possible to require tags to be present. There's no value.\n        _tag1: WithTag(ATag),\n        // If the component itself doesn't matter, but it must _not_ exist on the entity, this is the way!\n        _counterObjWithout: WithOut(Counter),\n        // It's also possible to require that the entity does _not_ have a tag\n        _tag2: WithoutTag(ATag),\n    }),\n    // As a way to pass information between systems and event the outside,\n    // sim-ecs provides an event bus. It can be accessed easily from systems:\n    eventReader: ReadEvents(MyEvent),\n    eventWriter: WriteEvents(MyEvent),\n    // If it's necessary to mutate the runnning world, \"system actions\" can be accessed!\n    actions: Actions,\n    // World-global resources can also be easily be added. Their access is cached, which is a performance boost\n    resourceR: ReadResource(Date),\n    resourceW: WriteResource(Date),\n    // Last but not least, systems also allow for local variables,\n    // which are unique to that system instance within a run.\n    // Please prefer them over putting variables into the module's scope!\n    // They can be declared using a generic (if needed) and initialized in the parameter \n    systemStorage1: Storage({ foo: 42, bar: 'Hello!' }),\n    systemStorage2: Storage({ data: [1,2,3] }),\n}).build();\n```\n\n_See\n[createSystem()](https://nsstc.github.io/sim-ecs/functions/createSystem.html)\n\u0026nbsp;_\n\n\n### Hot Reloading Systems\n\nSystems can be hot-reloaded. The Pong example shows that off nicely.\nIn order to enable HMR, the following is suggested:\n\n1. Make sure that you put every system into its own module (file), from which it's exported.\n2. Every system must be named.\n3. Implement your dev server's HMR strategy and on accept call `hmrSwapSystem()`. \n   It takes the new system as its parameter, which you should get from the new module's exports.\n\nFor example, a system module may look like this:\n\n```typescript\nimport {createSystem, hmrSwapSystem, ISystem, queryComponents, Read, ReadResource, Write} from \"sim-ecs\";\nimport {Position} from \"../components/position.ts\";\nimport {Velocity} from \"../components/velocity.ts\";\nimport {GameStore} from \"../models/game-store.ts\";\n\n\n// The System must be exported (of course)\nexport const AnimationSystem = createSystem({\n    gameStore: ReadResource(GameStore),\n    query: queryComponents({\n        pos: Write(Position),\n        vel: Read(Velocity),\n    }),\n})\n    .withName('AnimationSystem')                                                  // It's important to name the System!!\n    .withRunFunction(({gameStore, query}) =\u003e {\n        const k = gameStore.lastFrameDeltaTime / 10;\n        return query.execute(({pos, vel}) =\u003e {\n            pos.x += vel.x * k;\n            pos.y += vel.y * k;\n        });\n    })\n    .build();\n\n// using the dev server's HMR strategy...\n// @ts-ignore\nhmr:if (import.meta.hot) {\n    // @ts-ignore\n    import.meta.hot.accept(mod =\u003e \n        hmrSwapSystem(mod[Object.getOwnPropertyNames(mod)[0]] as AnimationSystem) // pass the System from the new Module\n    );\n}\n```\n\nNote: The label `hmr` was chosen as an easy and reliable way to remove this code block from a prod build.\n\n_See\n[\"Pong\" example](https://github.com/NSSTC/sim-ecs/blob/master/examples/pong/src/systems/ball.ts)\n\u0026nbsp;_\n\n\n## Defining Components\n\nComponents are needed to define data on which the whole system can operate.\nYou can think of them like columns in a database.\nAny serialize-able object may be a component in sim-ecs.\n\n```typescript\nclass Counter {\n    a = 0;\n}\n\nconst prepWorld = createWorld().withComponent(Counter).build();\nprepWorld.buildEntity().with(Counter).build();\n```\n\nIn case you have advanced components, it is possible to pass a serializer and deserializer\nto the entity builder later on. If you don't do so, it is assumed that the component is a simple data struct.\nYou can also use a default-type de-/serializer on save/load, which allows for a variety of standard types (such as `Date`) as components.\n\n_See\n[IWorldBuilder](https://nsstc.github.io/sim-ecs/interfaces/IWorldBuilder.html),\n[buildEntity()](https://nsstc.github.io/sim-ecs/interfaces/IPreptimeWorld.html#buildEntity),\n[IEntityBuilder](https://nsstc.github.io/sim-ecs/interfaces/IEntityBuilder.html)\n\u0026nbsp;_\n\n\n## Adding Entities\n\nEntities are like glue. They define which components belong together and form one data.\nEntities are automatically added to the world they are built in.\nYou can think of entities like rows in a database.\n\n```typescript\nprepWorld.buildEntity().withComponent(Counter).build();\n```\n\n_See\n[buildEntity()](https://nsstc.github.io/sim-ecs/interfaces/IPreptimeWorld.html#buildEntity),\n[IEntityBuilder](https://nsstc.github.io/sim-ecs/interfaces/IEntityBuilder.html)\n\u0026nbsp;_\n\n\n## Working with States (optional)\n\nStates allow for splitting up a simulation into different logical parts.\nIn games, that's for example \"Menu\", \"Play\" and \"Pause\".\nStates can be switched using a push-down automaton.\nStates define which systems should run, so that a pause-state can run graphics updates, but not game-logic, for example.\nIf no state is passed to the dispatcher, all systems are run by default.\n\nWhile the world is running (using `run()`), the state can be changed using commands.\nSingle calls to `step()` do not offer the benefits of a PDA.\n\n_See\n[\"Pong\" example](https://github.com/NSSTC/sim-ecs/tree/master/examples/pong/src/states)\n\u0026nbsp;_\n\n\n## Update loop\n\nThe update loop (for example game loop) is what keeps simulations running.\nIn order to provide an efficient way of driving the ECS, sim-ecs offers its own built-in loop:\n\n```typescript\nrunWorld.start() // run() will drive the simulation based on the data provided to set up the world\n    .catch(console.error) // this won't catch non-fatal errors, see error example!\n    .then(() =\u003e console.log('Finished.'));\n```\n\nWhile this is the recommended way to drive a simulation, sim-ecs also offers a step-wise execution: `runWorld.step()`.\nNote, though, that each step will need to run the preparation logic, which introduces overhead!\n\n\n## Commands\n\nCommands, accessible using `runWorld.commands` and `actions.commands` in Systems, are a mechanism,\nwhich queues certain functionality, like adding entities.\nThe queue is then worked on at certain sync points, usually at the end of every step.\nThis is a safety and comfort mechanism, which guarantees that critical changes can be triggered comfortably,\nbut still only run at times when it is actually safe to do them.\n\nSuch sync points include any major transitions in a step's life-cycle, and sim-ecs will always trigger the execution\nof all queued commands at the end of the step.\n\n_See\n[ICommands](https://nsstc.github.io/sim-ecs/interfaces/ICommands.html)\n\u0026nbsp;_\n\n\n## Saving and using Prefabs\n\nPrefabs, short for pre-fabrications, are ready-made files or objects,\nwhich can be loaded at runtime to initialize a certain part of the application.\nIn the case of sim-ecs, prefabs can be used to load entities with their components.\n\nAll loaded entities are tracked and can be unloaded when not needed anymore. This is thanks to a grouping mechanism,\nwhich means that prefabs can be used to design menus, levels, GUIs, etc. which are only loaded when needed\nand discarded after use. After all, who needs level1 data when they switched over to level2?\n\nThe same is true for save games, so that when going back to the menu or loading another save, this can be done cleanly.\n\nSaving and loading save-data works the same in sim-ecs, since they both use a shared mechanism\nIf you wish to work with the raw serializable data instead of writing it as JSON, the SaveFormat extends Array,\nso it can be used just like an `Array\u003cTEntity\u003e`.\n\n```typescript\nenum MonsterTypes {\n    Duck,\n    Lizard,\n    Tiger,\n}\n\n// loading a prefab, the prefab might be in a different file, even maybe just JSON data!\nconst prefab = [\n    {\n        Position: {\n            x: 0,\n            y: 1,\n        } satisfies Position,\n        Player: {\n            level: 1,\n            name: 'Jane',\n        } satisfies Player,\n        Health: {\n            current: 100,\n            max: 100,\n        } satisfies Health,\n    },\n    {\n        Position: {\n            x: 0,\n            y: 1,\n        } satisfies Position,\n        Monster: {\n            hostileToPlayer: true,\n            type: MonsterTypes.Tiger,\n        } satisfies Monster,\n        Health: {\n            current: 100,\n            max: 250,\n        } satisfies Health,\n    },\n];\n\n\n// to load from JSON, use SerialFormat.fromJSON() instead!\nconst prefabHandle = prepWorld.load(SerialFormat.fromArray(prefab));\n\n// ...\n\n// unloading is also easily possible to clean up the world\nrunWorld.unloadPrefab(prefabHandle);\n```\n\n```typescript\n// saving a prefab from the current world. This may be used to write an editor\n// or export a PoC for game designers to improve on\nconst jsonPrefab = runWorld.save().toJSON(4);\nsaveToFile(jsonPrefab, 'prefab.json');\n```\n\n```typescript\n// filtering what should be saved is also possible,\n// so that only certain data is saved and not all data of the whole world\nconst saveData = runWorld.save(queryEntities(With(Player))).toJSON();\nlocalStorage.setItem('save', saveData);\n```\n\n_See\n[load()](https://nsstc.github.io/sim-ecs/interfaces/IWorld.html#load),\n[save()](https://nsstc.github.io/sim-ecs/interfaces/ITransitionActions.html#save),\n[\"Pong\" example](https://github.com/NSSTC/sim-ecs/blob/master/examples/pong/src/states/game.ts)\n\u0026nbsp;_\n\n\n## Syncing instances\n\nIn order to keep several instances in sync, sim-ecs provides tooling.\nEspecially when writing networked simulations, it is very important to keep certain entities in sync.\n\n```typescript\n// OPTIONALLY initialize UUID mechanism\nimport {uuid} from 'your-favorit-uuid-library';\nEntity.uuidFn = uuid; // type: () =\u003e string\n\n// at the source, entities can be created as normal\nconst entity = prepWorld.buildEntity().build();\n\n// IDs are created lazily when getting them for the first time\nconst entityId = entity.id;\n\n// on another instance, you can assign the entity ID on entity creation:\nconst syncedEntity = prepWorld.buildEntity(entityId).build();\n\n// in order to fetch an entity with a given ID, the ECS's function can be used\nconst entityFromIdGetter = getEntity(entityId);\n// or inside a Query:\nconst {entityFromIdQuery} = queryComponents({ entityFromIdQuery: ReadEntity(entityId) }).getFirst();\n```\n\n\n## Building for Production\n\nWhen building for production, it is important to keep class names. Some minimizers need to be adjusted.\nThe Pong example is a good template for a project setup for development and production builds. \n\n\n### Webpack\n\nWebpack must [use Terser](https://webpack.js.org/plugins/terser-webpack-plugin/). The config could look like this:\n\n```javascript\nconst TerserPlugin = require(\"terser-webpack-plugin\");\n\nmodule.exports = {\n  optimization: {\n    minimize: true,\n    minimizer: [new TerserPlugin({\n      terserOptions: {\n        keep_classnames: true,\n      },\n    })],\n  },\n};\n```\n\n\n## Plugins\n\nHere's an overview of known plugins for sim-ecs:\n\n| Name                                                    | Description        |\n|---------------------------------------------------------|--------------------|\n\n\n## Performance\n\nPlease take the results with a grain of salt. These are benchmarks, so they are synthetic.\nAn actual application will use a mix out of everything and more, and depending on that may have a different experience.\n\nYou can run these benchmarks on your own machine - they are in the `examples/bench` folder.\n\nThe below result compares several AoS-based ECS libraries, which are similar to sim-ecs.\nThe only exception is bitecs, which is a SoA-based ECS library, for its usage in Phaser.\n\n\n### The Benchmarks\n\nThese benchmarks are based on the Rust [ECS Bench Suite](https://github.com/rust-gamedev/ecs_bench_suite).\n\n\n#### Simple Insert\n\nThis benchmark is designed to test the base cost of constructing entities and moving components into the ECS.\nInserts 1,000 entities, each with 4 components.\n\n\n#### Simple Iter\n\nThis benchmark is designed to test the core overheads involved in component iteration in best-case conditions.\n\nDataset: 1,000 entities, each with 4 components.\n\nTest: Iterate through all entities with Position and Velocity, and add velocity onto position.\n\n\n#### System Scheduling\n\nThis benchmark is designed to test how efficiently the ECS can schedule multiple independent systems.\nThis is primarily an outer-parallelism test.\n\nDataset:\n\n- 10,000 entities with (A, B) components.\n- 10,000 entities with (A, B, C) components.\n- 10,000 entities with (A, B, C, D) components.\n- 10,000 entities with (A, B, C, E) components.\n\nTest: Three systems accessing the following components mutably, where each system swaps the values stored in each component:\n\n- (A, B)\n- (C, D)\n- (C, E)\n\n\n#### Serialize\n\nThis benchmark is designed to test how quickly the ECS can serialize and deserialize its entities in JSON.\n\nDataset: 1,000 entities, each with 4 components.\n\nTest: Serialize all entities to JSON in-memory. Then deserialize back into the ECS.\n\n\n### The Result\n\n```\n--------------------------------------------------------------------------------\nTypeScript ECS Bench\n--------------------------------------------------------------------------------\n\n22nd May 2024\n\nPlatform: Windows_NT win32 x64 v10.0.22631\nCPU: AMD Ryzen 7 3700X 8-Core Processor@3600MHz\nNodeJS: v21.1.0\n\nBench           v0.3.0\nTypeScript      v5.4.5\nTS-Lib          v2.6.2\nTSX             v4.10.5\n\nApe-ECS         v1.3.1\nbitecs          v0.3.40\nJavelin         v1.0.0-alpha.13\nsim-ecs         v0.6.5\ntick-knock      v4.2.0\n\nMeasured in \"points\" for comparison. More is better!\n```\n\n\n **Default Suite / Simple Insert**\n\n|    Library | Points | Deviation | Comment |\n|-----------:|-------:|:----------|:--------|\n|    Ape-ECS |    767 | ± 1.1%    |         |\n|     bitecs |  11218 | ± 0.41%   |         |\n|    javelin |   2028 | ± 1.6%    |         |\n|    sim-ecs |   1057 | ± 1.1%    |         |\n| tick-knock |   7911 | ± 0.22%   |         |\n\n\n\n **Default Suite / Simple Iter**\n\n|    Library | Points | Deviation | Comment |\n|-----------:|-------:|:----------|:--------|\n|    Ape-ECS |  23170 | ± 0.21%   |         |\n|     bitecs | 119904 | ± 0.36%   |         |\n|    javelin |  19845 | ± 0.049%  |         |\n|    sim-ecs |    924 | ± 0.23%   |         |\n| tick-knock |  11038 | ± 0.085%  |         |\n\n\n\n\n **Default Suite / Schedule**\n\n|    Library | Points | Deviation | Comment |\n|-----------:|-------:|:----------|:--------|\n|    Ape-ECS |    110 | ± 0.13%   |         |\n|     bitecs |   7288 | ± 0.19%   |         |\n|    javelin |    101 | ± 0.071%  |         |\n|    sim-ecs |    244 | ± 0.29%   |         |\n| tick-knock |     55 | ± 0.18%   |         |\n\n\n\n **Default Suite / Serialize**\n\n| Library | Points | Deviation | Comment                      |\n|--------:|-------:|:----------|:-----------------------------|\n| Ape-ECS |     64 | ± 1.4%    | file size: 417.3427734375 KB |\n| Javelin |    557 | ± 1.3%    | file size: 31.1455078125 KB  |\n| sim-ecs |    113 | ± 1.6%    | file size: 92.677734375 KB   |\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNSSTC%2Fsim-ecs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FNSSTC%2Fsim-ecs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNSSTC%2Fsim-ecs/lists"}