Use multiple actors
在本教程中,您将创建一个包含多个参与者的项目。
目前,您只能在 Motoko 文件中定义一个参与者,并且单个参与者始终编译为单个容器智能合约。
但是,您可以创建具有多个参与者的项目,并且可以从同一个 dfx.json
配置文件构建多个容器。
在本教程中,您将为同一项目中的三个actor创建单独的程序文件。 该项目定义了以下不相关的参与者:
-
assistant
actor 提供了在待办事项列表中添加和显示任务的功能。 -
rock_paper_scissors
actor 提供了一个函数,用于在硬编码的剪刀石头布比赛中确定获胜者。此代码示例说明了在带有硬编码播放器和选项的 Motoko 程序中
switch
和case
的基本用法。 -
daemon
角色提供了启动和停止守护进程的模拟函数。此代码示例仅分配一个变量并打印消息以用于演示目的。
开始之前
在开始本教程之前,请验证以下内容:
-
您已按照下载并安装中的说明下载并安装了 {sdk-short-name} 包。
-
您已停止在您的计算机上运行的任何本地容器执行环境。
完成本教程大约需要 20 分钟。
创建一个新项目
要为本教程创建一个新项目:
-
如果您还没有打开一个终端外壳,请在您的本地计算机上打开一个终端外壳。
-
更改为您用于 Internet Computer 项目的文件夹(如果您正在使用一个)。
-
通过运行以下命令创建一个新项目:
dfx new multiple_actors
-
通过运行以下命令切换到您的项目目录:
cd multiple_actors
修改默认配置
您已经看到创建一个新项目会在您的项目目录中添加一个默认的 dfx.json
配置文件。
对于本教程,您需要在此文件中添加部分以指定每个容器的位置,这些容器定义了您要构建的actor。
修改默认的 dfx.json
配置文件:
-
在文本编辑器中打开
dfx.json
配置文件,然后将默认的multiple_actors
容器名称和源目录更改为assistant
。例如,在
canisters
键下:"assistant": { "main": "src/assistant/main.mo", "type": "motoko" },
因为您要向配置文件的这个
canisters
部分添加设置,所以还必须在包含assistant
主源代码文件和容器的位置的大括号之后添加一个逗号 类型。 -
从文件中删除
multiple_actors_assets
部分。 -
为“rock_paper_scissors”容器添加新容器名称、源代码位置和容器类型,并在“assistant”容器定义下方为“daemon”程序文件添加新容器名称、源代码位置和容器类型。
进行更改后,
dfx.json
文件的canisters
部分应类似于以下内容:{ "canisters": { "assistant": { "main": "src/assistant/main.mo", "type": "motoko" }, "rock_paper_scissors": { "main": "src/rock_paper_scissors/main.mo", "type": "motoko" }, "daemon": { "main": "src/daemon/main.mo", "type": "motoko" } }, "defaults": { "build": { "packtool": "" } }, "dfx": "0.9.2", "networks": { "local": { "bind": "127.0.0.1:8000", "type": "ephemeral" } }, "version": 1 }
您可以按原样保留其他部分。
-
保存您的更改并关闭
dfx.json
文件以继续。 -
通过运行以下命令,更改默认源文件目录的名称以匹配
dfx.json
配置文件中指定的名称:cp -r src/multiple_actors/ src/assistant/
-
复制
assistant
源文件目录,通过运行以下命令为rock_paper_scissors
角色创建主容器文件:cp -r src/assistant/ src/rock_paper_scissors/
-
复制
assistant
源文件目录,通过运行以下命令为daemon
角色创建主容器文件:cp -r src/assistant/ src/daemon/
修改默认容器
现在,src
目录中有三个单独的目录,每个目录都有一个模板 main.mo
文件。
在本教程中,您将用不同的 actor 替换每个模板 main.mo
文件中的内容。
修改默认源代码:
-
在文本编辑器中打开
src/assistant/main.mo
文件并删除现有内容。 -
将以下示例代码复制并粘贴到文件中:
import Array "mo:base/Array"; import Nat "mo:base/Nat"; // Define the actor actor Assistant { stable var todos : [ToDo] = []; stable var nextId : Nat = 1; // Define to-do item properties type ToDo = { id : Nat; description : Text; completed : Bool; }; // Add to-do item utility func add(todos : [ToDo], description : Text, id : Nat) : [ToDo] { let todo : ToDo = { id = id; description = description; completed = false; }; Array.append(todos, [todo]) }; // Show to-do item utility func show(todos : [ToDo]) : Text { var output : Text = "\n___TO-DOs___"; for (todo : ToDo in todos.vals()) { output #= "\n(" # Nat.toText(todo.id) # ") " # todo.description; if (todo.completed) { output #= " ✔"; }; }; output }; public func addTodo (description : Text) : async () { todos := add(todos, description, nextId); nextId += 1; }; public query func showTodos () : async Text { show(todos) }; };
-
保存更改并关闭
main.mo
文件以继续。 -
在文本编辑器中打开
src/rock_paper_scissors/main.mo
文件并删除现有内容。 -
将以下示例代码复制并粘贴到文件中:
import I "mo:base/Iter"; actor RockPaperScissors { stable var alice_score : Nat = 0; stable var bob_score : Nat = 0; stable var alice_last : Choice = #scissors; stable var bob_last : Choice = #rock; type Choice = { #rock; #paper; #scissors; }; public func contest() : async Text { for (i in I.range(0, 99)) { battle_round(); }; var winner = "The contest was a draw"; if (alice_score > bob_score) winner := "Alice won" else if (alice_score < bob_score) winner := "Bob won"; return (winner); }; func battle_round() : () { let a = alice(bob_last); let b = bob(alice_last); switch (a, b) { case (#rock, #scissors) alice_score += 1; case (#rock, #paper) bob_score += 1; case (#paper, #scissors) alice_score += 1; case (#paper, #rock) bob_score += 1; case (#scissors, #paper) alice_score += 1; case (#scissors, #rock) bob_score += 1; case (#rock, #rock) alice_score += 0; case (#paper, #paper) bob_score += 0; case (#scissors, #scissors) alice_score += 0; }; alice_last := a; bob_last := b; return (); }; // Hard-coded players and choices func bob(last : Choice) : Choice { return #paper; }; func alice(last : Choice) : Choice { return #rock; }; };
-
保存更改并关闭
main.mo
文件以继续。 -
在文本编辑器中打开
src/daemon/main.mo
文件并删除现有内容。 -
将以下示例代码复制并粘贴到文件中:
actor Daemon { stable var running = false; public func launch() : async Text { running := true; debug_show "The daemon process is running"; }; public func stop(): async Text { running := false; debug_show "The daemon is stopped"; }; };
-
保存更改并关闭
main.mo
文件以继续。
启动本地容器执行环境
在本地安装 multiple_actors
项目之前,您需要启动本地容器执行环境。 如果您打算将其部署到 Internet Computer 区块链主网,则可以跳过本节。
启动本地容器执行环境:
-
在本地计算机上打开一个新的终端窗口或选项卡。
-
如有必要,导航到项目的根目录。
-
通过运行以下命令启动本地容器执行环境:
dfx start
-
将显示容器执行操作的终端保持打开状态,并将焦点切换到您在其中创建新项目的原始终端。
部署你的多容器 dapp
要部署多容器 dapp,您需要将容器注册、构建和安装到本地容器执行环境或 Internet Computer 区块链主网。
要在本地部署 dapp:
-
如果需要,请检查您是否仍在项目的根目录中。
-
通过运行以下命令注册、构建和部署应用程序:
dfx deploy
要将 dapp 部署到 Internet Computer 区块链主网:
-
如果需要,请检查您是否仍在项目的根目录中。
-
运行
dfx deploy
命令,指定--network
选项和在dfx.json
文件中配置的网络别名。 例如,如果您使用网络别名ic
指定的 URL 连接到 Internet Computer 主网,您将运行类似于以下的命令:dfx deploy --network ic
dfx deploy
命令输出显示有关它执行的操作的信息。
例如,该命令显示在 dfx.json
配置文件中定义的三个容器的特定容器标识符。
Deploying all canisters. Creating canisters... Creating canister "assistant"... "assistant" canister created with canister id: "75hes-oqbaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q" Creating canister "daemon"... "daemon" canister created with canister id: "cxeji-wacaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q" Creating canister "rock_paper_scissors"... "rock_paper_scissors" canister created with canister id: "7kncf-oidaa-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa-q"
通过调用容器来验证部署
您现在在本地容器执行环境或 Internet Computer 区块链主网上部署了三个 canisters 智能合约,并且可以使用 dfx canister call
命令对其进行测试。
要测试您在本地部署的容器:
-
使用
dfx canister call
命令通过addTodo
函数调用容器assistant
,并通过运行以下命令将要添加的任务传递给它:dfx canister call assistant addTodo '("Schedule monthly demos")'
-
通过运行以下命令,验证该命令是否使用
showTodos
函数返回待办事项列表项:dfx canister call assistant showTodos
该命令返回类似于以下内容的输出:
(" ___TO-DOs___ (1) Schedule monthly demos")
-
使用
dfx canister call
命令通过运行以下命令,使用contest
函数调用容器rock_paper_scissors
:dfx canister call rock_paper_scissors contest
该命令返回类似于以下内容的硬编码竞赛结果:
("Bob won")
-
使用
dfx canister call
命令通过运行以下命令,使用launch
函数调用容器daemon
:dfx canister call daemon launch
-
验证 mock
launch
函数返回“守护进程正在运行”消息“:(""The daemon process is running"")
要测试在 Internet Computer 区块链主网上运行的容器,请使用与上述相同的命令,指定 --network
选项和在 dfx.json
文件中配置的网络别名。