Create Extension Systems
The commented code can be found in packages/contracts/src/systems/ReadDemoSystem.sol
(opens in a new tab)
The code should compile with pnpm build
.
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;
import { System } from "@latticexyz/world/src/System.sol";
import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol";
import { Home } from "primodium/tables/Home.sol";
import { Level } from "primodium/tables/Level.sol";
contract ReadDemoSystem is System {
function readMainBaseLevel() public returns (uint32) {
StoreSwitch.setStoreAddress(_world());
bytes32 playerEntity = bytes32(uint256(uint160(_msgSender())));
bytes32 asteroidEntity = Home.get(playerEntity);
bytes32 baseEntity = Home.get(asteroidEntity);
return uint32(Level.get(baseEntity));
}
}
Explanation
Primodium systems are smart contracts that execute on-chain logic to modify state stored in Primodium tables. Systems exist in both the core Primodium contracts and in world extensions.
In the ReadDemo, system contracts are created in packages/contracts/src/systems
. They are fairly standard smart contracts that only need to import the critical functionality from MUD or the World
they are interacting with.
import { System } from "@latticexyz/world/src/System.sol";
import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol";
The contract itself is a System
so we need to import that MUD library. We also need to tell the contract our Store
target. StoreSwitch
allows us to specify the World
address where our target Table
s reside.
import { Home } from "primodium/tables/Home.sol";
import { Level } from "primodium/tables/Level.sol";
We import the necessary Table
libraries from the target World
. These libraries are constructed by MUD scripts during pnpm build
, and include the specific tableId
s and fieldLayout
s, with appropriate setters and getters. They handle the encoding and decoding of the underlying storage records for us.
These libraries are imported from the @primodiumxyz/contracts
npm package.
contract ReadDemoSystem is System {
Make sure this matches what you specified in mud.config.ts
StoreSwitch.setStoreAddress(_world());
StoreSwitch
allows us to choose the data source for our extension. _world()
is inherited from System
, and resolves to the world that called this system. Since our systems will be registered to the Primodium world, this tells us to use the Primodium tables.
https://mud.dev/world/reference/world-context#_world (opens in a new tab)
bytes32 playerEntity = bytes32(uint256(uint160(_msgSender())));
Every player has an ID generated from their address for interacting with the world. This is the raw form; usually we use a helper function like addressToEntity()
to make this cleaner.
_msgSender()
allows us to find the address of the player calling this system. msg.sender
won't work since that will be the Primodium world address.
https://mud.dev/world/reference/world-context#_msgsender (opens in a new tab)
bytes32 asteroidEntity = Home.get(playerEntity);
bytes32 baseEntity = Home.get(asteroidEntity);
This can be a little confusing at first. In Primodium, Home.get
is dual purpose. Using it on a player ID returns their home asteroid ID. Using it on an asteroid ID returns the base building ID of that asteroid. Combined, we get the building ID of the main base building.
Most tables are not multipurpose like this, but it is common to need to drill down through parent-child entity ID layers.
return uint32(Level.get(baseEntity));
And we're done. A simple call to the Level table to get the level of the main base for the player.