RECS
The recs package is a way to query the Store and react to changes.
There are two APIs to read data and react to changes with recs
:
For the examples below, let's assume this MUD config. Notice that each of these tables has an (implicit) bytes32
key.
const config = mudConfig({
tables: {
NameComponent: "string",
PlayerComponent: "bool",
PositionComponent: { valueSchema: { x: "int32", y: "int32" } },
},
});
Reading component value directly
If you have a specific key and a reference to a component, you can query its value directly, or use a React hook that will re-render when the corresponding component value updates.
Reading component value in vanilla javascript
import { getComponentValueStrict } from "@latticexyz/recs";
// note: NameComponent is an recs component; components could come from the `setup` function of MUD.
const { NameComponent } = components
// get a reference to the recs world; as an example from the `setup` function of MUD.
const world = [...]
// you need a reference to an `Entity` from recs. For the sake of the example, let's say we know that the bytes32 key is "0xDEAD"
// you wouldn't do normally: entities would be found via queries.
const entityID = "0xDEAD" as EntityID;
const entity = world.registerEntity({ id: "0xDEAD" as EntityI })
// now we can fetch the value of the NameComponent on our entity
const name = getComponentValueStrict(NameComponent, entity)
// -> "John Doe"
Reading component value in react with @latticexyz/react
import { useComponentValue } from "@latticexyz/react";
function ExampleComponent() {
const { components, world } = useMUD;
const { NameComponent } = components;
// you need a reference to an `Entity` from recs. For the sake of the example, let's say we know that the bytes32 key is "0xDEAD"
// you wouldn't do this normally: entities would be found via queries.
const entityID = "0xDEAD" as EntityID;
const entity = world.registerEntity({ id: "0xDEAD" });
// now we can fetch the value of the NameComponent on our entity
const name = useComponentValue(NameComponent, entity);
// -> "John Doe"
// this will re-render when the component value changes!
return <p>{name ?? "<no name>"}</p>;
}
Running queries
A common way of working with ECS data is by using queries. A query is a function that returns a list of entities that match a set of predicates called query fragments.
There are 4 types of query fragments:
Has
: matches entities that have a specific component.Not
: matches entities that do not have a specific component.HasValue
: matches entities that have a specific component with a set value.NotValue
: matches entities that do not have a component with a set value (will also match if it doesn't have the component at all)
Running query with vanilla javascript
import { runQuery, Has, HasValue, getComponentValueStrict } from "@latticexyz/recs";
const { PlayerComponent, PositionComponent, NameComponent } = components
// query for all named players at the center of the universe
const matchingEntities = runQuery([
Has(PlayerComponent),
Has(Name),
HasValue(PositionCompoennt, {x: 0, y: 0})
])
// now you can map these to their name as an example
const names = matchingEntities.map(
playerEntity => getComponentValueStrict(NameComponent, playerEntity
)
// -> ["Bob", "Alice", "Eve"]
Reacting to queries with vanilla javascript
import { defineSystem, Has, HasValue, getComponentValueStrict, UpdateType } from "@latticexyz/recs";
const { PlayerComponent, PositionComponent, NameComponent } = components
// get a reference to the recs world; as an example from the `setup` function of MUD.
const world = [...]
defineSystem(world, [Has(PlayerComponent), Has(Name), HasValue(PositionComponent, {x: 0, y: 0}], ({entity, component, value, type}) => {
// every time an entity enter, exit, or get updated within a query; this callback with fire
// the "type" will match these: UpdateType.Enter, UpdateType.Exit, UpdateType.Update
// as an example, if a new entity is named, has a player component, and is at the center of the universe; the callback fires
// if the name of a player at the center of the universe changes, the callback will also fire for that entity
// let's only log when a new entity matches this query
// every time a named player reaches {0, 0}; we want to log their name
if(type !== UpdateType.Enter) return
console.log(getComponentValueStrict(NameComponent, entity) + " reached the center!")
})
Reacting to queries with React and @latticexyz/react
import { useEntityQuery } from "@latticexyz/react";
import { Has, HasValue, getComponentValueStrict } from "@latticexyz/recs";
function ExampleComponent() {
const { components, world } = useMUD;
const { NameComponent, PlayerComponent, PositionCompoennt } = components
// get a list of all entities that are named, players, and at the center of the universe
// it is reactive and will trigger a re-render when the set of matching queries update
const entities = useEntityQuery([Has(PlayerComponent), Has(Name), HasValue(PositionComponent, {x: 0, y: 0}])
// [entity1, entity2, ...]
return(
<div>
<span>Players at the center:</span>
<ul>
{entities.map(entity => (
<li>{getComponentValueStrict(NameComponent, entity)}</li>
))}
</ul>
</div>
)
}