Releases: JaimeGensler/thyseus
v0.13.1
v0.13.0
0.13.0
See https://thyseus.dev/blog/v0_13_release for full release notes
Minor Changes
- 66bdf49: Change signature of get(), getById(), and spawn() methods
- 745fb57: Define copy/drop functions for structs rather than pointer array
- c390f4d: Flatten Memory.views into Memory
- 66ba4cb: Privatize most fields on WorldBuilder
- f5874ba: Rewrite Commands to use structs, add push() method
- 66bdf49: Remove commands API from Entity
- c50508d: Remove implicit dependencies (first, last)
- 43e0276: Add World.prototype.getComponentId
- 66bdf49: Remove insertInto(), insertTypeInto(), removeFrom() methods on
Commands - ee25756: Remove /types import, setup /global import
Patch Changes
v0.12.0
v0.12.0
See https://thyseus.dev/blog/v0_12_release for detailed release notes!
💥 Breaking Changes
WorldBuilder
- Removed
addSystem()
- Removed
addStartupSystem()
- Removed
setExecutor()
- Removed
registerThreadChannel()
- Removed
World
- Removed
update()
- Renamed
archetypes
totables
- Removed
Table
- Instances are no longer constructed on threads
- Renamed bitfield to archetype
grow()
accepts anewSize
parameter, to individual tables can grow
differently.
ThreadGroup
- Changed send to
send(channelName: string, data: SendableType)
- Changed send to
- Removed
definePlugin()
- Removed
defineSystem()
- Removed
SystemDefinition
applyCommands()
is now a function (prev.SystemDefinition
)- Previous "Startup systems" (now systems in the startup core schedule) do not
run whenworldBuilder.build()
is called, but whenworld.start()
is
called - The
memory
option of world config has been renamed tomemorySize
- The
Entity
component will throw an error if trying to construct it without
world.commands
andworld.entities
- Renamed
table.size
totable.length
applyCommands
is no longer added by default.
✨ Features
- Added schedules
- Added
run()
export - Added
cloneSystem()
export - Added
CoreSchedule
export WorldBuilder
- Added
addSystems(...systems: System[]): WorldBuilder
- Added
addSystemsToSchedule(schedule: symbol, ...systems: System[]): WorldBuilder
- Added
setDefaultExecutor(executor: ExecutorType): WorldBuilder
- Added
setExecutorForSchedule(schedule: symbol, executor: ExecutorType): WorldBuilder
- Added
World
- Added
start(): void
- Added
runSchedule(schedule: symbol): Promise<void>
- Added
getResource<T extends Class>(resourceType: T): InstanceType<T>
- Added
Entity
- Added
hasComponent(componentType: Struct): boolean
- Added
- Added
isMainThread: boolean
anduseSharedMemory: boolean
world config - Exposed
thyseus/types
as possible import - Exposed
thyseus/descriptors
as possible import - Exposed nearly all internal types
- Exposed
memory
API
🐛 Bug Fixes
- Fixed struct system resources not constructing properly on threads.
🔧 Maintenance
- Updated build to preserve identifiers.
v0.11.0
v0.11.0
A new release this weekend, and with it, a (minimal, WIP) docs site! Docs should be pretty close to exhaustive at this point (some advanced patterns and API details aside). Next update will likely feature some QOL features/improvements - for now, enjoy Events!
💥 Breaking Changes
- The
size
property onQuery
instances has been renamed tolength
. - Removed
__$$s
property from struct instances - structs now use
memory.views
and allocate when they are constructed.- All structs that are not managed by Thyseus (i.e., that you
construct yourself withnew YourStruct()
) will need to call
dropStruct()
when they go out of scope. - Structs cannot be created before memory has been initialized.
- If you're having trouble migrating old code after this schange, please
feel free to
open an issue.
- All structs that are not managed by Thyseus (i.e., that you
commands.spawn()
andcommands.getEntityById()
return an instance of
EntityCommands
instead of anEntity
component.EntityCommands
does not have getters for generation and index -
otherwise, this change has no impact.
world.resources
has changed from a map of classes to instances
(Map<Class, object>
) to an array of instances (object[]
).- This does not impact you unless you access
world.resources
directly.
- This does not impact you unless you access
✨ Features
- Added Events!
- Events are useful for cross-system communication. They can be used with
two new system parameters -EventReader<T>
andEventWriter<T>
, which
are generic over structs. EventWriter
can push events to the event queue, andEventReader
can
read events from the queue.- Check the docs page for full documentation.
- Events are useful for cross-system communication. They can be used with
- Added
query.forEach()
.- For those that prefer a functional approach to iteration - provide a
callback, which will be called once for each queried entity. - The callback receives as many arguments as components were queried for,
so there is no difference between queries for single components vs.
queries for multiple components.
- For those that prefer a functional approach to iteration - provide a
🐛 Bug Fixes
- Fixed struct resources not being constructed correctly and added a test for
it this time :) - Fixed
SystemRes
constructing on worker threads even if resources were
main-thread-only.
🔧 Maintenance
- Switch to
tsup
for type bundling. - Query
for...of
iteration has been cleaned up a bit and should not create
objects - Reduced default memory size to 64 MB - the original default (512MB) was way
too aggressive. May be reduced further or slightly bumped in the future.
v0.10.0
Introducing a new strategy for handling memory, which allows dynamically sized
types! So far, this is only used for internal storage (like Entities
and
Table
s) and the new version of @struct.string
, but it has many future
applications as well!
💥 Breaking Changes
@struct
and primitive struct decorators are now plain decorators rather
than decorator factories.- They should now be called as
@struct
rather than@struct()
,
@struct.u32
rather than@struct.u32()
, etc.
- They should now be called as
@struct.string
is now a plain decorator and no longer accepts arguments- See features for more details.
- Remove
buffer
,createBuffer()
, andtableLengths
fromWorld
instances;archetypeLookup
is now private. - Changes to
Entities
,Table
properties/methods.- This does not impact you unless you access Entities/Tables directly.
- Changes to some properties/methods on
Commands
.- This is unlikely to impact you - the common-use API remains the same.
- Certain methods that were previously technically available but meant for
internal use likegetData()
have been removed/replaced.
- Static
schema
on structs removed - stores always contain all elements.- This does not impact you unless you depend on the schema property being
present.
- This does not impact you unless you depend on the schema property being
- The new memory allocation strategy currently throws when out of memory.
- This is unlikely to impact you, and can be remedied by adjusting the
amount of memory allocated with config - see below for more details.
- This is unlikely to impact you, and can be remedied by adjusting the
commands.despawn()
now returns void rather thanthis
.commands.get()
has been renamed tocommands.getEntityById()
.
✨ Features
- Implemented a new memory allocation strategy that allows dynamic sizing.
- Many features of this update are only possible because of this new
strategy! - While we eventually intend to expose this API, working directly with the
memory allocator is very tricky and can lead to memory corruption. Until
the API has stabilized more and better memory safety strategies are
developed, it will remain a private API. - The implementation of this allocator is a first draft - please file an
issue if you run into any errors, odd behavior, or poor performance.
- Many features of this update are only possible because of this new
@struct.string
now creates dynamically sized strings and behaves more or
less equivalently to normal Javascript strings!- This requires caution when constructing/setting strings in structs you
manage, and can lead to memory leaks if not careful.- Better strategies to prevent leaks are being worked on, and should
show up in a future update. - For the time being, we've exported a
dropStruct(instance: object): void
function, which you should call
with structs that (1) you constructed, (2) use strings, and (3) go
out of scope and are suitable for garbage-collection.
- Better strategies to prevent leaks are being worked on, and should
- This requires caution when constructing/setting strings in structs you
- Added
SystemResource
system parameter.- These work like resources, but are unique per system, rather than per
world. As a result, the same type can be reused across multiple systems
and will create unique instances per system! initialize
methods will be called on the main thread only and may be
async, just like their world resource counterparts.
- These work like resources, but are unique per system, rather than per
- Added an optional
memory
property to world config, which specifies the
total amount of memory (in bytes) that should be reserved for all worlds.- By default, reserves 512 MBs (1/2 GB). Max allowed by Thyseues is 4GB,
but your particular environment may not allow this allocation! - Currently, all worlds share the same memory, and so this should be
the amount of memory you need for all worlds! - This memory is allocated when the first world is built.
- This is not the total amount of memory used by your program, but
rather storage used by entities, components, resources, and Thyseus
internals.
- By default, reserves 512 MBs (1/2 GB). Max allowed by Thyseues is 4GB,
intoArgument
method of system parameters may return a promise, which will
be awaited during world building.
🐛 Bug Fixes
- Fixed entities not being recycled correctly.
- Fixed resources not being constructed correctly in workers.
- Fixed some data not being transfered between threads correctly, resulting in
command queues not clearing. - Fixed issue caused by thread group recycling
send
results for single
threaded worlds, resulting in commands being processed multiple times. - Fixed command queues growing unnecessarily.
🔧 Maintenance
- Added explicit return types for functions/methods, which should help prevent
accidental breaking API changes. - Improved error messaging when adding/removing unregistered components.
- Add some additional types to exports (
Plugin
,WorldBuilder
,
WorldConfig
) - more to come in future updates. - Specified
config
inWorldBuilder
andWorld
as read-only.
v0.9.0
This update adds some much-needed features for structs and components! While
these features mark a "useable" baseline for Thyseus, there's still a lot of
features, cleanup, and performance/DX improvements to come!
💥 Breaking Changes
- Removed
__$$i
from struct instances.- This index property was only an unnecessary indirection over the byte
offset (__$$b
), which is what should always be used now. - This only impacts you if you have handwritten structs or have code
accessing this property.
- This index property was only an unnecessary indirection over the byte
- Structs no longer receive
store
,index
, andcommands
arguments. See
features for details!- This only impacts you if you have handwritten structs.
- Removed
entity.insert()
, modified signature ofcommands.insertInto()
.
See features for details! - (Types only) Removed
maxEntities
config & validation.- This config has been unused for a few updates and there are no plans to
reintroduce it.
- This config has been unused for a few updates and there are no plans to
✨ Features
-
You can now add your own constructors for structs, allowing you to construct
them anytime and set initial values!- Added
initStruct(structInstance: object): void
export. If you pass
this
to initStruct in your constructor, you may safely read/write
properties of your struct. - Constructors must not have any required arguments.
- You can specify the default values of a struct this way. These values
will be used as the initial component values any time a component is
added to an entity by type. The below example demonstrates aVec3
component withx
,y
, andz
defaulting to 1, 2, and 3.
import { struct, initStruct } from 'thyseus'; @struct() class Vec3 { @struct.f64() declare x: number; @struct.f64() declare y: number; @struct.f64() declare z: number; constructor(x = 1, y = 2, z = 3) { // Treat this kind of like super - // Must be called before accessing properties! initStruct(this); this.x = x; this.y = y; this.z = z; } } const a = new Vec3(); // x = 1, y = 2, z = 3 const b = new Vec3(5.5, -2, 7); // x = 5.5, y = -2, z = 7
- If you do not create a constructor with default values, values will
default to 0. - Structs you create cannot be recycled by Thyseus, so be aware that they
may add additional garbage collection work.
- Added
-
Added
entity.add()
andcommands.insertInto()
, which accept instances
of structs.- This allows you to set initial values for component data, either from
another entity's component data or by constructing a new component
instance and passing it. - Values are immediately copied from the passed object so modifications
after adding a component are not reflected.
- This allows you to set initial values for component data, either from
-
Added
entity.addType()
andcommands.insertTypeInto()
- These function the same as the previous
insert
andinsertType
.
- These function the same as the previous
-
commands.spawn()
now returns a uniqueEntity
handle that may be safely
used for the rest of the system.- The current implementation adds some additional garbage collection
work - in the future, this may be reworked to reuseEntity
instances
while respecting this guarantee.
- The current implementation adds some additional garbage collection
🐛 Bug Fixes
- Fixed issue where commands would occasionally not correctly despawn entity
if despawn was queued after insert in multithreaded worlds.
🔧 Maintenance
- Changed
System
return types fromvoid
toany
. Internally, system
returns are treated asPromise<void>
, but these types can create some
annoying Typescript errors - especially if usingPromise.all()
- and so
have been relaxed toany
. - Bump dev dependency versions.
v0.8.0
This update prioritizes some necessary cleanup and stability improvements, and so is a bit light on features and heavier on breaking changes. As part of this cleanup, worlds use less memory and bundles should be smaller (especially in production builds)!
The next release will include some some important feature work, including the ability to set default values for component fields and to initialize data when inserting components on entities.
💥 Breaking Changes
- The Dependency API has changed:
defineSystem
returns an instance of the newSystemDefinition
class.addSystem
only accepts one argument - aSystemDefinition
.SystemDefinition
instances havebefore(...others: SystemDefinition[]): this
,after(...others: SystemDefinition[]): this
,beforeAll(): this
, andafterAll(): this
methods. These are now how you declare dependencies.- Dependencies behave the same as before (see v0.2.0 changelog for a refresher).
- When a system is added to the world, its dependencies are cleared. This allows systems to have different dependencies across worlds.
- For this reason, it is recommended that you either declare dependencies when you add systems to a world, or in the same module you build a world.
- As before, dependencies remain unused for startup systems, though this will likely change in the future.
WorldBuilder
'sregisterThreadChannel
method andThreadGroup
'ssend
have been modified.- A
createThreadChannel
function is now exported. This function accepts a channel name (string) and a message handler creator - just like the previousregisterThreadChannel
function signature. registerTheadChannel
now accepts a singleThreadMessageChannel
argument, which is the value returned bycreateThreadChannel
.- The
send
method onThreadGroup
now accepts a singleThreadMessage
argument, which is created by calling your thread message channel with the data you want to send. - This new API has much more robust type safety - the types of the data sent to threads and the data returned are intrinsically linked.
- A
- All validation now only occurs in development builds.
- This currently includes:
- Validation of world config.
- Validation preventing direct access of ZSTs.
- Validation that queries have some possible match criteria.
- Validation that system dependencies are not circular.
- Most validation prevents more cryptic errors from being thrown later, but some (e.g. queries that do not match) are silent.
- This change only impacts you if you have significant differences between your world in prod vs. development, which should not be the case!
- This currently includes:
- Partial rewrites of
Entities
,Table
.- This only impacts you if you directly access properties/methods!
world.archetypes
is now aTable[]
(previouslyMap<Bigint, Table>
).world.systems
is now a((...args: any[]) => any)[]
- Worlds now also have
arguments: any[][]
, so each system has a corresponding member in the arguments array (includes some empty arrays).
- Worlds now also have
✨ Features
- Added
esm-env
as a dependency.- This allows Thyseus to check whether you're building for development or production. We'll use more of these checks in the future to provide better correctness guarantees in dev and better performance in prod!
- Added
isAlive(entityId: bigint)
toEntities
(world.entities
), if you need to check if an entity is still alive. - Added
clone(): SystemDefinition
method toSystemDefinition
.- Useful if you need to add multiple instances of the same system (e.g., if you'd like to add an additional
applyCommands
system in the middle of your update loop). clone
does not clone dependencies or dependents. It's a completely new system with the same functionality!
- Useful if you need to add multiple instances of the same system (e.g., if you'd like to add an additional
- Dependencies are now recursively validated.
- Previously, errors were only thrown if dependencies were directly circular (e.g.,
A before B before A
orA before A
). - We now detect chains of dependencies and throw for those (e.g.
A before B before C before A
will throw).
- Previously, errors were only thrown if dependencies were directly circular (e.g.,
- Added
setExecutor(executor: ExecutorType)
method toWorldBuilder
, so you may pass a custom executor implementation.- Custom executors are an especially advanced pattern - some documentation around API requirements has been added.
- Added
SimpleExecutor
, which is automatically used in single-threaded worlds.SimpleExecutor
currently performs much better thanParallelExecutor
.
🐛 Bug Fixes
- Fixed bug where Executors could get into a bad state that prevented system execution.
- Fixed types for
Query
iterators. - Fixed issue where commands could be added for despawned entities, causing entities with recycled ids to incorrectly add/remove components.
- Fixed entities not being correctly despawned.
- Fixed Commands not properly creating
Entity
stores, preventing access to entityindex
andgeneration
for (spawned) entity handles. - The promise returned by
world.update()
now (correctly) only resolves when all systems have finished executing. - Fixed a rare bug where
beforeAll
/afterAll
could introduce undetected circular dependencies. - Fixed bug where using more than 2 threads would cause unresolveable promises and prevent worlds from being built.
🔧 Maintenance
- Bump dev dependency versions.
v0.7.0
v0.7.0 (December 11, 2022)
Query improvements are here, with a docs page too! New releases will now also include a docs page covering new (and old) APIs, if you find that easier to parse than a changelog.
💥 Breaking Changes
- The value returned by
Mut()
has changed from an array to an instance of
the newMut
class.- This does not impact you unless you directly accessed the return of
Mut()
.
- This does not impact you unless you directly accessed the return of
- Trying to use zero-sized ("tag") components as accessors in queries will
throw during system construction now.- With the new filters feature for queries, the correct way to query for
ZSTs is to specifyWith(MyZST)
/Without(MyZST)
. - Previously, this would cause errors to be thrown downstream anyway.
We're forcing this error to happen earlier to highlight that this is an
invalid use and to offer a clearer resolution.
- With the new filters feature for queries, the correct way to query for
✨ Features
- Query Filters! The
Query()
parameter now permits a second argument
that can be used to specify additional filters on queries. These filters are
available on the parameters object provided bydefineSystem
, and are only
permitted in the second argument of theQuery()
descriptor function.With()
- specifies that queried entities must have a specific
component.- Accepts structs or tuples of structs as arguments.
Without()
- the opposite of With. Matched entities will not have
the specified component.- Acccepts structs or tuples of structs as arguments.
Or()
- allows more complex querying logic, where multiple conditions
may be permitted.- Accepts two arguments.
- Arguments must be instances of
With
,Without
,Or
, or tuples of
those items.
- And - tuples of any size (
[ComponentA, ComponentB, ...]
) specify
"And" clauses. - Some additional notes:
- Filters do not grant access to components and do not impact whether
systems can run in parallel. - With the exception of
Optional()
, accessors are processed as
With
clauses automatically and do not need to be added to the
filter list. - Queries are validated and will remove impossible combinations of
filters. If no valid filters remain, an error will be thrown. For
example:[With(A), Without(A)]
will throw.[Or(With(A), With(B)), Or(Without(A), Without(B))]
will not
throw, and will simplify to expect(A && !B) || (!A && B)
(removing the logically impossible(A && !A) || (B && !B)
).
- Filters do not grant access to components and do not impact whether
- Added
Optional()
to parameters object, accepted byQuery()
parameters
(only permitted in first argument).- Use
Optional()
if you want access to a component only if an entity
contains it. Optional has no affect on query matches (both entities with
and without the component will match the query). - Iterating queries with
Optional()
will yield an instance of the
component if it exists, ornull
if not.
- Use
- Queries no longer have to be tuples - if you need a single item, you can use
Query(MyComponent)
and the iterator will yield plainMyComponent
instances. - Nested query iteration now works!
- Previously, queries held a single instance of elements, which meant that
nesting query iterators would override other element values. Now,
invoking the iterator guarantees new instances.
- Previously, queries held a single instance of elements, which meant that
- Added
size
toQuery
instances, a getter that returns the number of
entities that currently match. executor
on worlds is now public.
🔧 Maintenance
- Bump Typescript version.
v0.6.0
Structs just got a lot more powerful!
💥 Breaking Changes
- The
store
andindex
properties onComponentType
instances have been
renamed, freeing up store and index as useable field names.- This change does not impact you unless you directly access either of
those properties. - The new names are
__$$s
for store,__$$i
for index, and__$$b
for
the new byteOffset property (these may move to symbols in the future).- If you write your own accessors, you must use byte offset if you
want your struct to function with@struct.substruct()
- If you write your own accessors, you must use byte offset if you
- This change does not impact you unless you directly access either of
entityIndex
onEntity
has been changed toindex
.- Static properties for struct classes have changed.
- This change does not impact you unless you have handwritten Components -
i.e., without use of@struct
. alignment
- the number of bytes needed by the largest field of the
struct (e.g. 8 forf64
,i64
,u64
, 4 forf32
,i32
,u32
,
etc.).- Array/string alignment is the same as individual field alignment
(e.g., ani32
and anInt32Array
have the same alignment).
- Array/string alignment is the same as individual field alignment
size
- the size of the type including padding (previously did not
include padding). Must be a multiple of alignment.schema
- a numeric (bitfield) property indicating which
TypedArrays are needed when creating a store.- Bit flags start with
u8 = 1 << 0
, and are ordered
u8 ... u64, i8 ... i64, f32, f64
.
- Bit flags start with
- This change does not impact you unless you have handwritten Components -
- The shape & memory layout of component stores has changed.
- This change does not impact you unless you directly access component
store data (likeworld.archetypes
) or have handwritten accessors on
components. - Previously, component stores would allocate a buffer and each field
would claim a section of the buffer (e.g.|x---|y---|z---|
for a store
ofVec3
components). Now, components are placed sequentially in the
buffer (e.g.,|xyz|xyz|xyz|
).- Stores are now objects containing the
buffer
, a Uint8Array (u8
)
over the (entire) buffer, and any other typed arrays specified by a
struct'sschema
over the entire buffer.
- Stores are now objects containing the
- Benchmarks suggested this was a significantly more performant approach -
both for iteration and for copying component data such as growing
stores, moving tables, and (in the future) setting initial values. - Please note that if you do access component data directly, fields are
reordered as necessary (by alignment, largest to smallest, equal
alignments appended), and so may or may not be in the same order as
declared.
- This change does not impact you unless you directly access component
✨ Features
- Added
@struct.string()
for fixed-size strings.- This decorator factory accepts an options object, containing either:
characterCount: number
allocates enough space forn
three-byte characters - if any characters use fewer than three
bytes, more characters will be able to fit.byteLength
specifies how many bytes the string takes up. If you
know you'll only be using one or two byte characters, you can reduce
the required storage space of the string.- byteLength is used if both are set.
- Unlike normal Javascript strings,
'\u0000'
characters terminate the
string. - Please note that the current implementation uses dynamic typed array
creation, and so is much slower than primitive numeric fields.
- This decorator factory accepts an options object, containing either:
- Added
@struct.array()
for fixed-size numeric arrays.- This decorator factory accepts an options argument with two required
fields:type
- The name of the array type (e.g.'u8'
,'i16'
,'f64'
,
etc.)length
- The length (in elements) of the array.
- Please note that the current implementation uses dynamic typed array
creation, and so is much slower than primitive numeric fields.
- This decorator factory accepts an options argument with two required
- Added
@struct.substruct()
, so structs can have other structs as
properties.- This decorator factory accepts a struct.
- Creates both a getter and a setter.
- The current implementation dynamically creates instances of the provided
struct, and so is much slower than primitive numeric fields. In the
future this will be refactored to take advantage of a per-thread
struct-instance pool.
- It is no longer required that
getNewTableSize
return multiples of 8.- As a result, resources create a store of size 1, rather than using
getNewTableSize(0)
- As a result, resources create a store of size 1, rather than using
🐛 Bug Fixes
- Fixed issue where table stores wouldn't re-pack data correctly when moving
to tables with fewer components. - Fixed a bug where new components would initialize with old component data.
- The current implementation eagerly zeroes data when components move -
this will likely be done lazily in the future, so if you manually access
component data, do not rely on eager zeroing.
- The current implementation eagerly zeroes data when components move -
- Fixed tables not correctly sharing metadata between threads.
- Prevent columns from being created for ZSTs in tables.
🔧 Maintenance
- Cleanup type names (
ComponentType
->Struct
) - Bumped dev dependency versions.
v0.5.0
This update is heavy on breaking changes and light on features - primarily
focused on internal cleanup & moving forward with a better API. Exciting feature
work coming soon!
💥 Breaking Changes
- Removed
Component()
function andType
enum - replaced with@struct()
decorator (check the README for updated API reference).- The primary motivation for this change was that, for this use case,
decorators:- Better demonstrate intent
- Have clearer fields at a glance
- Allow components to be declared without extending a class
- Are more flexible for future features
- Additionally, thyseus will eventually have an optional TS compiler to
provide better performance & DX, and decorators will likely be easier to
work with for this. - Unfortunately, current Typescript decorators do not provide outstanding
type information. The current implementation will be updated to the new
decorators proposal once Typescript adds support, which will hopefully
provide better type info.
- The primary motivation for this change was that, for this use case,
- Removed
ThreadProtocol
,Resource()
function (resources should be
declared with@struct()
like components)- This was primarily to unify around a single model of shared data.
- Additionally, ThreadProtocol had some less than ideal performance
characteristics that impacted all values sent across thread boundaries.
Without it, transfering data across threads should be much faster.
- Static
create()
methods on Resources will not be called anymore. See
features for the replacement API, which allows async initialization and
access to private class members. - The first argument of
defineSystem()
is now a function that is passed an
object containing descriptors (P
) as well asMut
.- This reduces the amount of top-level API, and marks a step towards
making the TS compiler mentioned above easier to build.
- This reduces the amount of top-level API, and marks a step towards
- Removed
P
,Mut
exports, movedWorld
from default to named export.
✨ Features
- Resources with an
initialize(world: World): void | Promise<void>
method
(not static) will have that method called immediately after the world is
created (before theWorldBuilder.build()
promise resolves).initialize
is only called on the main thread, so context-sensitive
operations like setting up event listeners can be done here.
- Add
registerThreadChannel()
method toWorldBuilder
.- Pass a channel name (
string
) and a handler creator
((world: World) => (data: SendableType) => SendableType
). - Channels must be unique - one channel may only have one handler.
- Pass a channel name (
- Nearly all properties on
World
andWorldBuilder
are public now.- While mutating World properties directly is rarely a good idea, not
letting users access World internals seemed like a worse idea.
- While mutating World properties directly is rarely a good idea, not
- Component fields can be booleans with
@struct.bool()
.- At the moment, this is just a wrapper around
@struct.u8()
, so bool
fields require an entire byte (may be reworked in the future).
- At the moment, this is just a wrapper around
- The internal
Executor
for systems has been slightly rewritten and no
longer usersAtomics.waitAsync
.- This marks multithreading capabilities on all browsers that support
SharedArrayBuffer
and module Workers, and no longer requires
SharedArrayBuffer
when singlethreaded! 🎉 - In a near update, Worlds will accept custom executors, so while the
default Executor will always aim to work in (nearly) all runtimes out of
the box, ifAtomics.waitAsync
/Atomics.wait
/another approach is
better for your use case, a custom implementation could be swapped in.
- This marks multithreading capabilities on all browsers that support
🐛 Bug Fixes
- Fixed promises returned by systems not being awaited by the executor.
- Fixed tables not retaining new columns after grow.
🔧 Maintenance
- Bumped dev dependency versions.