yarn to install all local dependenciesyarn start to start the hardhat node, deploy the contracts and start the client
Before we get started, let's get familiar with the codebase. The only two relevant files for now are contracts/src/Game.sol and client/src/Game.ts.
The starterkit includes a general scaffold for crypto-native games built with the Lattice SDK (or Lattice Mud). Mappings between contract components and client components are set up for you, as well as a basic Phaser and React renderer.
The starterkit is powered by lattice-solidity-ecs, @latticexyz/mobx-ecs@latticexyz/eth-middleware, and @latticexyz/phaser-middleware, all of which are not part of this workshop. The source files are included in the starterkit, so feel free to dive deeper into the internals if you like.
The diffs you see below are already part of the starterkit, nothing else do do here.
For a sneak peak of the game we'll implement, checkout the demo branch.
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createWorld } from "@latticexyz/mobx-ecs";
|
|
2
|
+
import { createMapping, loadEvents, setupContracts, setupMappings } from "../packages/lattice-eth-middleware";
|
|
3
|
+
import { setupPhaser } from "@latticexyz/phaser-middleware";
|
|
4
|
+
import { createCoordComponent, decodeCoordComponent } from "./components";
|
|
5
|
+
import { createExampleSystem } from "./systems/ExampleSystem";
|
|
6
|
+
|
|
7
|
+
export async function createGame(contractAddress: string, privateKey: string, chainId: number, personaId: number) {
|
|
8
|
+
const world = createWorld();
|
|
9
|
+
|
|
10
|
+
/*****************************************
|
|
11
|
+
* Contract setup
|
|
12
|
+
*****************************************/
|
|
13
|
+
const { contracts, txExecutor, provider, signer } = await setupContracts(contractAddress, privateKey, chainId);
|
|
14
|
+
const componentAddresses = await contracts.Game.c();
|
|
15
|
+
|
|
16
|
+
/*****************************************
|
|
17
|
+
* Phaser setup
|
|
18
|
+
*****************************************/
|
|
19
|
+
const width = (await contracts.Game.width()).toNumber();
|
|
20
|
+
const height = (await contracts.Game.height()).toNumber();
|
|
21
|
+
const phaser = await setupPhaser(width, height);
|
|
22
|
+
|
|
23
|
+
/*****************************************
|
|
24
|
+
* Component definitions
|
|
25
|
+
*****************************************/
|
|
26
|
+
const Position = createCoordComponent(world, "Position");
|
|
27
|
+
|
|
28
|
+
const components = {
|
|
29
|
+
Position,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/*****************************************
|
|
33
|
+
* Mapping contract to client components
|
|
34
|
+
*****************************************/
|
|
35
|
+
setupMappings(world, contracts.World, {
|
|
36
|
+
...createMapping(componentAddresses.position, Position, decodeCoordComponent),
|
|
37
|
+
});
|
|
38
|
+
await loadEvents(provider, contracts.World);
|
|
39
|
+
|
|
40
|
+
/*****************************************
|
|
41
|
+
* Methods and context for consumers
|
|
42
|
+
*****************************************/
|
|
43
|
+
function ping() {
|
|
44
|
+
return "pong";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const context = {
|
|
48
|
+
world,
|
|
49
|
+
phaser,
|
|
50
|
+
contracts,
|
|
51
|
+
components,
|
|
52
|
+
signer,
|
|
53
|
+
txExecutor,
|
|
54
|
+
personaId,
|
|
55
|
+
api: { ping },
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/*****************************************
|
|
59
|
+
* Systems
|
|
60
|
+
*****************************************/
|
|
61
|
+
createExampleSystem(context);
|
|
62
|
+
|
|
63
|
+
return context;
|
|
64
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Unlicense
|
|
2
|
+
pragma solidity >=0.8.13;
|
|
3
|
+
|
|
4
|
+
import { console } from 'forge-std/console.sol';
|
|
5
|
+
import { World } from 'lattice-ecs/World.sol';
|
|
6
|
+
import { Component } from 'lattice-ecs/Component.sol';
|
|
7
|
+
import { CoordComponent, Coord } from './components/CoordComponent.sol';
|
|
8
|
+
import { UintComponent } from './components/UintComponent.sol';
|
|
9
|
+
|
|
10
|
+
struct Components {
|
|
11
|
+
CoordComponent position;
|
|
12
|
+
UintComponent num;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
contract Game {
|
|
16
|
+
address public world;
|
|
17
|
+
address public owner;
|
|
18
|
+
uint256 public width = 32;
|
|
19
|
+
uint256 public height = 32;
|
|
20
|
+
address[] public componentList;
|
|
21
|
+
Components public c;
|
|
22
|
+
|
|
23
|
+
modifier onlyContractOwner() {
|
|
24
|
+
require(msg.sender == owner, 'only contract owner');
|
|
25
|
+
_;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
constructor(address _world) {
|
|
29
|
+
owner = msg.sender;
|
|
30
|
+
world = _world;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function registerComponents(Components memory _components, address[] memory _componentList) public onlyContractOwner {
|
|
34
|
+
c = _components;
|
|
35
|
+
componentList = _componentList;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Remove all components from the given entity
|
|
40
|
+
*/
|
|
41
|
+
function remove(uint256 entity) internal {
|
|
42
|
+
for (uint256 i; i < componentList.length; i++) {
|
|
43
|
+
Component(componentList[i]).remove(entity);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|