Skip to content

Releases: pmndrs/koota

v0.5.0 - Skywind Coastlines

18 Jul 16:28
Compare
Choose a tag to compare

A minor version bump means a breaking change, so what happened? Before the traits schemas were vague about using nested data structures and it was valid to add an object literal as property. However, this had unexpected results as this same object would be copied into all traits. Intuitively you might think a nested store would be created, but it turns out this makes data operations exponentially more complex and avoiding non-scalars is often considered the first rule of normalizing data.

// ❌ Arrays and objects are not allowed in trait schemas
const Inventory = trait({
  items: [],
  vec3: { x: 0, y: 0, z: 0}
  max: 10,
})

// ✅ Use a callback initializer for arrays and objects
const Inventory = trait({
  items: () => [],
  vec3: () => ({ x: 0, y: 0, z: 0})
  max: 10,
})

This breaking change should affect very few users, but all the same it is breaking. In addition to some fixes and internal cleanup we also have wildcard relation removal.

player.add(Likes(apple))
player.add(Likes(banana))

// Remove all Likes relations
player.remove(Likes('*'))

player.has(apple) // false
player.has(banana) // false

What's Changed

  • feat: constrain trait schemas to no longer accept object or array literals by @krispya in #141
  • feat: remove all relation targets with a wildcard by @krispya in #133
  • fix: world set types by @krispya in #136
  • chore: update CI node version by @krispya in #137
  • chore: update three-stdlib fixing examples by @ospira in #140

New Contributors

Full Changelog: v0.4.4...v0.5.0

v0.4.4 - Elephant Gun

13 Jun 12:12
Compare
Choose a tag to compare

I really love beautiful code. I thrive in an organized space and strive for my code to come out like a haiku, the APIs like finishing a sentence. However, performant code is often ugly and adds complexity to the mental model. For example, inlining functions is a simple way to improve performance on critical pathways that are called many times a second. Each function incurs some overhead, either in its actual execution on the machine or in the JIT compiler that needs to analyze and inline the code at runtime. At the same time, inlining functions makes it harder to maintain the code (it is in multiple places) and is also just ugly!

My ahem passion for an aesthetic dev experience drove me to write the experimental esbuild-plugin-inline-functions. This lets me write simple, legible functions but guarantee they are inlined when transpiled for the final build. To learn more about how it works, check out the repo.

While I was improving the dev experience of working on Koota, I could not stop gardening and updated all of the tooling with a monorepo layout I am testing for larger projects. It sparks joy.

But with the headline feature out of the way, here is what actually impacts users 😉 :

  • getStore is now exported. Use it to get the store. For low level use only! See docs for more info.
  • IsExcluded is now exported. This tag makes an entity excluded from all queries.
  • unpackEntity is now exported. Use this function to unpack the entity into its entity ID, generation and world ID. See docs for more info.
  • The entity generation can now be accessed with entity.generation().
  • The new linter caught some rerender edge cases in useTraitEffect and useQuery that I fixed.
  • Builds are now streamlined with automated inlining improving performance marginally.
  • Schemas now support BigInt as primitive type. If you have never seen the 1n syntax, check it out!

Note: What happened v0.4.3? I messed up publishing it, so we skipped to v0.4.4. Whoops! 🤫

What's Changed

  • chore(core): Add missing exports by @krispya in #110
  • feat(core): Entity generation method by @krispya in #118
  • feat: Use inline plugin by @krispya in #46
  • chore: Migrate to pnpm catalogs by @krispya in #122
  • chore: Switch to Oxlint by @krispya in #123
  • fix(core): Make array creation explicit by @krispya in #124
  • fix(react): useTraitEffect no longer rerenders if callback is an arrow function by @krispya in #125
  • fix(react): Make useQuery rerenders more stable by @krispya in #126
  • chore: Remove dev package and simplify dev workflow by @krispya in #127
  • feat(core): Support BigInt in schemas by @krispya in #129

Full Changelog: v0.4.2...v0.4.4

v0.4.2 - Jumpscare

27 May 19:29
a7f3ac1
Compare
Choose a tag to compare

A bushel of fixes here!

  • world.reset() will no longer break if entities have exclusive relations.
  • Tracking modifiers, including Changed, have various bugs fixed related to the tracking not being properly cleared after being read. This fixes in particular using multiple tracking modifiers in the same query.

What's Changed

  • Fix typos in README (no queryable → not queryable, enttiy → entity) by @pietdaniel in #112
  • fix: ensure entities are only destroyed when present by @wrangelvid in #111
  • fix: only update a Changed query when the tracked trait is changed by @r04423 in #116
  • Reset bit masks after executing tracking queries by @r04423 in #117

New Contributors

Full Changelog: v0.4.1...v0.4.2

v0.4.1 - Metro Phenomenon

05 May 23:59
Compare
Choose a tag to compare

This release fixes two bugs. The first is simple, cached queries will now return a proper query result. Some refactoring will follow to make this kind of mistake harder to do in the future.

The second enables Koota to work with React Native when bundled with Metro. It turns out that Metro removes all use strict directives, opting JS files into so-called "sloppy mode" if they are not invoking modules. The causes number primitives to be coerced into objects and destroys the internals of Koota. We now force all apps using Koota to be in strict mode. I'm sorry, we need to enforce updates to the language from 16 years ago. You can read more here.

What's Changed

  • 🐛 core: fix cached queries not returning query results by @krispya in #100
  • fix: emit "use strict" everywhere to thwart bad bundlers by @thejustinwalsh in #109
  • Add failing test case for use query result by @itsdouges in #99

New Contributors

Full Changelog: v0.4.0...v0.4.1

v0.4.0 - Have Heaven

19 Apr 17:10
Compare
Choose a tag to compare

I realized that the implementation of onAdd and onRemove was mixing two ideas together. One is trait events, when a trait is added, removed of changed on an entity. Another is query events, when a query has an entity added or removed from it. These are now two distinct events bringing a breaking change.

  • onAdd triggers when entity.add() is called after the initial value has been set on the trait.
  • onRemove triggers when entity.remove() is called, but before any data has been removed.
  • onChange triggers when an entity's trait value has been set with entity.set() or when it is manually flagged with entity.changed().
// Subscribe to Position additions
const unsub = world.onAdd(Position, (entity) => {
    console.log(`Entity ${entity} added position`);
});

// Subscribe to Position removals
const unsub = world.onRemove(Position, (entity) => {
    console.log(`Entity ${entity} removed position`);
});

// Subscribe to Position changes
const unsub = world.onChange(Position, (entity) => {
    console.log(`Entity ${entity} changed position`);
});

And then we have query events which trigger when an entity is added or removed from a query with no guarantees about data. This is stream of all modifications to a query, not to the addition or removal of traits to an entity. You can think of this an internal hook for advanced features that require observing queries like our useQuery hook.

// Subscribe to add or remove query events
// This triggers whenever a query is updated
// Return unsub function
const unsub = world.onQueryAdd([Position, Velocity], (entity) => {})
const unsub = world.onQueryRemove([Position, Velocity], (entity) => {})

What's Changed

  • 💥 core: onAdd is now a trait event, former onAdd is renamed onQueryAdd
  • 🐛 core: fix queryFirst types
  • 🐛 react: fix useTraitEffect, add tests

v0.3.1 - Palisades

11 Apr 16:39
Compare
Choose a tag to compare

This mostly fixes a few bugs that have been reported but also removes the reset options added in the previous version. They did not work as expected and were creating complexity where it was not needed.

What's Changed

  • 🐛 Fix world reset reactivity by @krispya in #75
  • 🏷️ core: improve action types by @krispya in #77
  • 🐛 core: trait generic type now requires the schema matches by @krispya in #84
  • 🐛 core: system traits get properly registered with new worlds by @krispya in #85
  • 🐛 core: tracked traits were removed prematurely by @krispya in #88

Full Changelog: v0.3.0...v0.3.1

v0.3.0 - Shame, Shame

21 Mar 20:46
Compare
Choose a tag to compare

This release has a minor breaking change for the React API where I removed the default world. WorldProvider is required now. It did not help as much as I thought it would and allowed for subtle bugs where it was easy to forget that a ghost world was created. Otherwise, we have a laundry list of updates. Some highlights are:

  • useQuery is now more stable for React causing it to hit the fast path more often.
  • Worlds are now side-effect free and can be GCed without worry.
  • A sort method has been added to the query result interface.
  • Getting queries with a cache key now results is proper typing.

What's Changed

  • 🏷️ core: add sort method to QueryResult type by @krispya in #64
  • ✨ core: add sort method to query result by @krispya in #65
  • 🏷️ core: fix cache query types by @krispya in #68
  • ✨ react: sort useQuery results for better stability by @krispya in #69
  • ✨ core: world is now GC safe by @krispya in #71
  • 💥 react: remove default world by @krispya in #72
  • ✨ Add world reset options by @krispya in #74

Full Changelog: v0.2.3...v0.3.0

v0.2.3 - Trust

05 Mar 23:30
Compare
Choose a tag to compare

This release fixes some bugs and adds a new method for entities for checking if they are alive.

entity.isAlive() // ture
entity.destroy()
entity.isAlive() // false

What's Changed

  • 🐛 core: query all removes destroyed entities by @krispya in #61
  • ✨ core: add isAlive method to entity by @krispya in #59
  • 🐛 core: query version was not incrementing on remove by @krispya in #60
  • ♻️ core: replace entity methods with functions internally by @krispya in #62

Full Changelog: v0.2.1...v0.2.3

v0.2.1 - Lemon Days

28 Feb 16:04
Compare
Choose a tag to compare

This release simply makes Koota compatible to install with React 18 or 19.

What's Changed

Full Changelog: v0.2.0...v0.2.1

v0.2.0 - Words Without Sound

28 Feb 00:08
Compare
Choose a tag to compare

This release includes breaking changes so we are bumping to v0.2.0.

Querying the world with empty parameters now returns all queryable entities.
Previously querying a world with empty parameters (ie world.query()) would give empty results. But there was two problems, one this doesn't really follow set theory where the empty set is the set of all entities, but also there was no public API for getting all queryable entities. What we mean by queryable entities? Some entities are system level and cannot be queried, but will still show up in world.entities. This is any entity with the IsExcluded trait and includes entities that represent the world and any other abstract entity that comes up for future features. This will exclude these entities. Thank you @wrangelvid and @DanAmador for your help here!

world.spawn(Position)
world.entities.length // Is 2 since a system entity is create for world
world.query().length // Is 1 since the system entity is excluded

useQuery is immutable and more stable
Previously, useQuery returned a memoized array that was mutated with rerenders being triggered. However, this led to subtle bugs as it broke React's rules about immutable state so we fixed it. useQuery now returns a new array result each time it rerenders and effects will work as expected. Also, it will not longer rerender twice on first call even if the entities does not change.


What's Changed

  • 💥 core: queries with empty params return all queryable entities by @krispya in #45
  • 🔧 🐛 Peer dependencies are properly optional, including types by @krispya in #48
  • Update README.md by @stephencorwin in #51
  • 🐛 react: useQuery was unstable with the same params by @krispya in #52
  • Fix relation store get type with get method by @krispya in #56
  • Make useQuery immutable and more stable by @krispya in #55

New Contributors

Full Changelog: v0.1.12...v0.2.0