v1.0.0-alpha.0
Pre-release1.0.0 alpha is here! This update brings a lot of improvements to Javelin's API, performance, and networking utilities. Below is a high level summary of the changes that I hope to outline in a blog post or on the website as we get closer to a full release.
API Changes
- Built-in effects are now prefixed with
use
instead ofeff
. For example,effMonitor
becomesuseMonitor
. query
->createQuery
world.component
->component
array
->arrayOf
Component Types
Component types are now defined as simple objects, e.g.,
const Position = createComponentType({
type: 1,
schema: {
x: number,
y: number,
},
})
becomes the following:
const Position = {
x: number,
y: number,
}
A component type (schema) can be registered with a specified type id (integer) using the registerSchema
function:
import { registerSchema } from "@javelin/ecs"
registerSchema(Position, 99)
Monitors
Triggers were removed and monitors were made a bit more flexible. useMonitor
now provides an array of changed components that caused a transition to happen.
const q = createQuery(A, B, C)
useMonitor(
q,
(entity, [a, b, c]) => { /* component a, b, or c is non-null if it was attached */ },
(entity, [a, b, c]) => { /* component a, b, or c is non-null if it was detached */ },
)
Queries
query.forEach
was removed in favor of a slightly simpler syntax for iteration. To iterate a query using callbacks, simply invoke the query as a function:
q.forEach((e, [a, b, c]) => {})
becomes the following:
q((e, [a, b, c]) => {})
Networking
The entire @javelin/net
package was rewritten. Not only does the protocol now have a built-in, efficient ArrayBuffer
protocol, users will now have more control of how network messages are sent. MessageProducer
and MessageHandler
both remain but were also rewritten to support the new networking model. MessageProducer
has slightly different responsibilities now, as it will not automatically generate messages with a declarative configuration; it will instead expose an imperative API over the lower-level protocol functions but handle common patterns, like sorting patches by priority and partitioning messages by maximum length.
Below is an example of how MessageProducer
is used. MessageHandler
remains largely unchanged.
import { createMessageProducer, encode, decode } from "@javelin/net"
const producer = createMessageProducer({ maxByteLength: 1000 })
const a = component(A)
const b = component(B)
producer.spawn(1, [a])
producer.attach(1, [b])
producer.detach(1, [a])
producer.update(1, [{...a, x: 1}])
// etc.
const message = producer.take(true /* include component model? */)
const encoded = encode(message)
decode(message, {
onSpawn(entity, components) {
// entity = 1, components = [a]
}
})
@javelin/pack
views are now supported inside of component schemas. If you're building a multiplayer game, consider using views over number
to save bytes:
import { uint8 } from "@javelin/pack"
const Fighter = {
attackMode: uint8, // saves you 7 bytes over `number`!
}
Change Detection
The current strategy of change detection using Proxies is just too slow. After a lot of trial and error, I came to the conclusion that both getters/setters and proxies are both too slow for the types of games I would expect Javelin to support (games with hundreds to thousands of dynamic, networked entities). So, similar to the MessageProducer
rewrite, control is given back to the user for detecting changes and generating patches.
Since change tracking is optional and kind of an advanced feature, its implementation was split out into a new package named @javelin/pack
. Below is an example of the API:
import { ChangeSet, set } from "@javelin/track"
const entity = world.spawn(
component(Position),
component(ChangeSet),
)
const query = createQuery(Position, ChangeSet).bind(world)
const [position, changes] = query.match(entity)
set(position, changes, "x", 1)
set(position, changes, "y", 2)
// then, to write the patch to a message...
producer.patch(entity, changes)
Thanks for reading, and if you get a chance to test the new release, please open an issue with any feedback or issues you run into!!