190-MUD引擎深入浅出(二)

上一篇我们讲了MUD store

如何编写data model数据模型

还是比较简单的

接下来我们来讲MUD world

我们可以理解成是一个MUD的世界状态

比如我们现在创建并部署了一个world合约

那么world合约本质上是来记录我们现在所有的游戏状态的

那么存储的结构也就是我们通过data model生成的表结构

每一个用户都是将一个namespace命名空间作为存储索引

举个例子

比如我们现在有3个表,cat表,dog表,balance表

用户a的地址为0xaaa

用户b的地址为0xbbb

那么0xaaa和0xbbb就是这2个用户的命名空间

然后他们在每张表上存储了自己的数据

那么MUD store和MUD world就是2个最基本的概念了

接下来我们正式来看MUD框架

MUD就是集成了

1.前端React

2.后端Next

3.合约框架foundry

我们来创建个项目

pnpm create mud@next my-project

创建完之后就来运行一下

pnpm run dev

我们可以看到现在的模板

实现了一个非常简单的功能

按一下按钮就调用了increment()方法

然后合约上的number就增加了1

那我们来看看这一切是如何运作的

首先我们来看看data model数据模型

export default mudConfig({
  tables: {
    Counter: {
      keySchema: {},
      schema: "uint32",
    },
  },
});

我们已经学习过data model了

这里很简单吧

那么我们看一下通过data model生成的table合约

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/* Autogenerated file. Do not edit manually. */

// Import schema type
import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol";

// Import store internals
import { IStore } from "@latticexyz/store/src/IStore.sol";
import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol";
import { StoreCore } from "@latticexyz/store/src/StoreCore.sol";
import { Bytes } from "@latticexyz/store/src/Bytes.sol";
import { Memory } from "@latticexyz/store/src/Memory.sol";
import { SliceLib } from "@latticexyz/store/src/Slice.sol";
import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol";
import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol";
import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol";

bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Counter")));
bytes32 constant CounterTableId = _tableId;

library Counter {
  /** Get the table's key schema */
  function getKeySchema() internal pure returns (Schema) {
    SchemaType[] memory _schema = new SchemaType[](0);

    return SchemaLib.encode(_schema);
  }

  /** Get the table's value schema */
  function getValueSchema() internal pure returns (Schema) {
    SchemaType[] memory _schema = new SchemaType[](1);
    _schema[0] = SchemaType.UINT32;

    return SchemaLib.encode(_schema);
  }

  /** Get the table's key names */
  function getKeyNames() internal pure returns (string[] memory keyNames) {
    keyNames = new string[](0);
  }

  /** Get the table's field names */
  function getFieldNames() internal pure returns (string[] memory fieldNames) {
    fieldNames = new string[](1);
    fieldNames[0] = "value";
  }

  /** Register the table's key schema, value schema, key names and value names */
  function register() internal {
    StoreSwitch.registerTable(_tableId, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames());
  }

  /** Register the table's key schema, value schema, key names and value names (using the specified store) */
  function register(IStore _store) internal {
    _store.registerTable(_tableId, getKeySchema(), getValueSchema(), getKeyNames(), getFieldNames());
  }

  /** Get value */
  function get() internal view returns (uint32 value) {
    bytes32[] memory _keyTuple = new bytes32[](0);

    bytes memory _blob = StoreSwitch.getField(_tableId, _keyTuple, 0, getValueSchema());
    return (uint32(Bytes.slice4(_blob, 0)));
  }

  /** Get value (using the specified store) */
  function get(IStore _store) internal view returns (uint32 value) {
    bytes32[] memory _keyTuple = new bytes32[](0);

    bytes memory _blob = _store.getField(_tableId, _keyTuple, 0, getValueSchema());
    return (uint32(Bytes.slice4(_blob, 0)));
  }

  /** Set value */
  function set(uint32 value) internal {
    bytes32[] memory _keyTuple = new bytes32[](0);

    StoreSwitch.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getValueSchema());
  }

  /** Set value (using the specified store) */
  function set(IStore _store, uint32 value) internal {
    bytes32[] memory _keyTuple = new bytes32[](0);

    _store.setField(_tableId, _keyTuple, 0, abi.encodePacked((value)), getValueSchema());
  }

  /** Tightly pack full data using this table's schema */
  function encode(uint32 value) internal pure returns (bytes memory) {
    return abi.encodePacked(value);
  }

  /** Encode keys as a bytes32 array using this table's schema */
  function encodeKeyTuple() internal pure returns (bytes32[] memory) {
    bytes32[] memory _keyTuple = new bytes32[](0);

    return _keyTuple;
  }

  /* Delete all data for given keys */
  function deleteRecord() internal {
    bytes32[] memory _keyTuple = new bytes32[](0);

    StoreSwitch.deleteRecord(_tableId, _keyTuple, getValueSchema());
  }

  /* Delete all data for given keys (using the specified store) */
  function deleteRecord(IStore _store) internal {
    bytes32[] memory _keyTuple = new bytes32[](0);

    _store.deleteRecord(_tableId, _keyTuple, getValueSchema());
  }
}

这个Counter.sol就是一个table合约

是自动生成的,我们不需要去修改

然后我们看一下client这边

看看App.tsx

import { useComponentValue } from "@latticexyz/react";
import { useMUD } from "./MUDContext";
import { singletonEntity } from "@latticexyz/store-sync/recs";

export const App = () => {
  const {
    components: { Counter },
    systemCalls: { increment },
  } = useMUD();

  const counter = useComponentValue(Counter, singletonEntity);

  return (
    <>
      <div>
        Counter: <span>{counter?.value ?? "??"}</span>
      </div>
      <button
        type="button"
        onClick={async (event) => {
          event.preventDefault();
          console.log("new counter value:", await increment());
        }}
      >
        Increment
      </button>
    </>
  );
};

这里我们看到

点一下Button就调用了increment

这里的increment是来自useMUD()的systemcalls

  const {
    components: { Counter },
    systemCalls: { increment },
  } = useMUD();

那么我们来看一下systemCalls

import { getComponentValue } from "@latticexyz/recs";
import { ClientComponents } from "./createClientComponents";
import { SetupNetworkResult } from "./setupNetwork";
import { singletonEntity } from "@latticexyz/store-sync/recs";

export type SystemCalls = ReturnType<typeof createSystemCalls>;

export function createSystemCalls(
  { worldContract, waitForTransaction }: SetupNetworkResult,
  { Counter }: ClientComponents
) {
  const increment = async () => {
    const tx = await worldContract.write.increment();
    await waitForTransaction(tx);
    return getComponentValue(Counter, singletonEntity);
  };

  return {
    increment,
  };
}

createSystemCalls.ts

那么这里就看到了increment方法

    const tx = await worldContract.write.increment();

那么我们实际上调用了worldContract的increment方法

那么我们再返回去看一下worldContract中的increment

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import { System } from "@latticexyz/world/src/System.sol";
import { Counter } from "../codegen/Tables.sol";

contract IncrementSystem is System {
  function increment() public returns (uint32) {
    uint32 counter = Counter.get();
    uint32 newValue = counter + 1;
    Counter.set(newValue);
    return newValue;
  }
}

这是我们的IncrementSystem.sol合约

那么这里的increment方法实际上就是在Counter表中记录的数值

我们再看一下读取counter的地方

  const counter = useComponentValue(Counter, singletonEntity);

这里使用了useComponentValue

是因为使用ecs

import { singletonEntity } from "@latticexyz/store-sync/recs";

ecs指的是游戏开发中的ecs结构

即 Entity-Component-System(实体-组件-系统) 的缩写,其模式遵循组合优于继承原则,游戏内的每一个基本单元都是一个实体,每个实体又由一个或多个组件构成,每个组件仅仅包含代表其特性的数据(即在组件中没有任何方法),例如:移动相关的组件MoveComponent包含速度、位置、朝向等属性,一旦一个实体拥有了MoveComponent组件便可以认为它拥有了移动的能力,系统便是来处理拥有一个或多个相同组件的实体集合的工具,其只拥有行为(即在系统中没有任何数据),在这个例子中,处理移动的系统仅仅关心拥有移动能力的实体,它会遍历所有拥有MoveComponent组件的实体,并根据相关的数据(速度、位置、朝向等),更新实体的位置。

我们现在用另一种方法来读取counter

用一下useRow

import { useComponentValue } from "@latticexyz/react";
import { useMUD } from "./MUDContext";
import { singletonEntity } from "@latticexyz/store-sync/recs";

export const App = () => {
  const {
    components: { Counter },
    systemCalls: { increment },
    network: { singletonEntity, storeCache },
  } = useMUD();

  // const counter = useComponentValue(Counter, singletonEntity);
    const counter= useRow(storeCache, {table: "Counter", key:{} })

  return (
    <>
      <div>
        {/*Counter: <span>{counter?.value ?? "??"}</span>*/}
        Counter: <span>{counter?.value.value ?? "??"}</span>
      </div>

这里我们用了useRow

然后从storeCache里面查询

填入table和key

下面读取的地方改成

counter?.value.value

import { useComponentValue ,useRow} from "@latticexyz/react";

ok

ok

ok

ok

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值