diff --git a/.gitignore b/.gitignore index 0ef2cf7..3e94403 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,6 @@ yarn-error.log* # local env files .env*.local - # typescript *.tsbuildinfo .vercel @@ -30,9 +29,6 @@ yarn-error.log* src/.vitepress/cache src/.vitepress/cache/deps src/.vitepress/dist -.vitepress -.vitepress/cache -.vitepress/cache/deps wallet.json diff --git a/languages/def.js b/languages/def.js index 4a21c86..33c8275 100644 --- a/languages/def.js +++ b/languages/def.js @@ -2,12 +2,20 @@ import { createRequire } from "node:module"; const enStrings = createRequire(import.meta.url)("./strings/en.json"); +const zhStrings = createRequire(import.meta.url)("./strings/zh.json"); /** * Start adding new languages by making a new language object inside the array * * @see file://./README.md#add-a-new-language */ -const languages = []; +const languages = [ + { + display: "中文", // Name of the language displayed in UI + name: "中文", // Name of the language in English, used by OpenAI translation + code: "zh", // 2 letter language code (ISO 639‑1) + strings: zhStrings, // JSON object of translated UI element strings + }, +]; const i18n_strs = languages.reduce((langs, currentLang) => { langs[currentLang.code] = currentLang.strings; diff --git a/languages/strings/en.json b/languages/strings/en.json index 95d1d95..f60c936 100644 --- a/languages/strings/en.json +++ b/languages/strings/en.json @@ -5,8 +5,12 @@ "title": "Cookbook", "description": "The decentralized computer with infinite threads.", "edit": "Edit", + "on-this-page": "On this page", + "prev-page": "Previous page", + "next-page": "Next page", "language": "Language", "docs": "Docs", + "edit-link-text": "Suggest changes to this page", "welcome": "Welcome", "welcome-getting-started": "Getting Started", "welcome-testnet-info": "Testnet Info", diff --git a/languages/strings/zh.json b/languages/strings/zh.json new file mode 100644 index 0000000..f769ce2 --- /dev/null +++ b/languages/strings/zh.json @@ -0,0 +1,82 @@ +{ + "label": "中文", + "display": "中文", + "lang": "zh", + "title": "手册", + "description": "拥有无限线程的去中心化计算机。", + "edit": "编辑", + "on-this-page": "本页内容", + "prev-page": "上一页", + "next-page": "下一页", + "edit-link-text": "对本页提出修改建议", + "language": "语言", + "docs": "文档", + "welcome": "欢迎", + "welcome-getting-started": "入门", + "welcome-testnet-info": "测试网信息", + "concepts": "概念", + "concepts-specs": "规范", + "concepts-messages": "消息", + "concepts-processes": "进程", + "concepts-units": "单元", + "concepts-how-it-works": "信使工作原理", + "concepts-meet-lua": "Lua 快速入门", + "concepts-tour": "aos 接口", + "guides": "指南", + "guides-aos": "aos", + "guides-aos-intro": "概览", + "guides-aos-installing": "安装", + "guides-aos-cli": "命令行", + "guides-aos-load": ".load", + "guides-aos-token": "创建代币", + "guides-aos-prompt": "自定义提示符", + "guides-aos-pingpong": "Ping-Pong 服务器", + "guides-aos-editor": "设置你的编辑器", + "guides-aos-inbox-and-handlers": "理解收件箱", + "guides-aos-troubleshooting": "使用 ao.link 进行故障排除", + "guides-aos-faq": "常见问题", + "guides-aos-blueprints": "蓝图", + "guides-aos-blueprints-token": "代币蓝图", + "guides-aos-blueprints-chatroom": "聊天室蓝图 ", + "guides-aos-blueprints-voting": "投票蓝图", + "guides-aos-blueprints-staking": "质押蓝图", + "guides-aos-modules": "模块", + "guides-aos-modules-json": "JSON", + "guides-aos-modules-ao": "ao", + "guides-aos-modules-base64": "Base64", + "guides-aos-modules-pretty": "Pretty", + "guides-aos-modules-utils": "Utils", + "guides-aoconnect": "aoconnect", + "guides-calling-dryrun": "调用试运行", + "guides-connecting": "连接到节点", + "guides-monitoring-cron": "监控定时任务", + "guides-installing-connect": "安装 aoconnect", + "guides-reading-results": "读取结果", + "guides-sending-messages": "发送消息", + "guides-spawning-processes": "创建进程", + "tutorials": "教程", + "tutorials-begin": "开始", + "tutorials-begin-preparations": "准备", + "tutorials-begin-messaging": "消息传递", + "tutorials-begin-chatroom": "创建聊天室", + "tutorials-begin-token": "创建代币", + "tutorials-begin-tokengating": "代币门控", + "tutorials-bots-and-games": "机器人和游戏", + "tutorials-bots-and-games-ao-effect": "我们玩个游戏", + "tutorials-bots-and-games-announcements": "解析公告", + "tutorials-bots-and-games-game-state": "获取游戏状态", + "tutorials-bots-and-games-decisions": "战略决策", + "tutorials-bots-and-games-attacking": "自动反馈", + "tutorials-bots-and-games-bringing-together": "整合一起", + "tutorials-bots-and-games-arena-mechanics": "竞技场的机制", + "tutorials-bots-and-games-build-game": "扩展竞技场", + "references": "参考", + "references-lua": "Lua", + "references-wasm": "Web Assembly", + "references-ao": "ao 模块", + "references-handlers": "Handlers", + "references-token": "代币", + "references-data": "加载数据", + "references-cron": "定时消息", + "references-editor-setup": "ao 编辑器设置" +} diff --git a/src/.vitepress/config.js b/src/.vitepress/config.js index 22e4be1..0980da5 100644 --- a/src/.vitepress/config.js +++ b/src/.vitepress/config.js @@ -39,7 +39,7 @@ const config = defineConfig({ */ locales: languages.reduce( (locales, { code }) => { - locales[`/${code}/`] = localeConfig(code); + locales[code] = localeConfig(code); return locales; }, { root: localeConfig("en") }, diff --git a/src/.vitepress/locales.js b/src/.vitepress/locales.js index cc8caf2..01bb9cf 100644 --- a/src/.vitepress/locales.js +++ b/src/.vitepress/locales.js @@ -12,6 +12,17 @@ export const localeConfig = (langCode) => ({ title: get_i18n_str(langCode, "title"), description: get_i18n_str(langCode, "description"), themeConfig: { + outline: { + label: get_i18n_str(langCode, "on-this-page"), + }, + docFooter: { + prev: get_i18n_str(langCode, "prev-page"), + next: get_i18n_str(langCode, "next-page"), + }, + editLink: { + pattern: "https://github.com/permaweb/ao-cookbook/edit/main/src/:path", + text: get_i18n_str(langCode, "edit-link-text"), + }, logo: { light: { src: "/ao_pictograph_lightmode.svg", height: 26, width: 26 }, dark: { src: "/ao_pictograph_darkmode.svg", height: 26, width: 26 }, diff --git a/src/concepts/index.md b/src/concepts/index.md index 8172d8a..1e6864d 100644 --- a/src/concepts/index.md +++ b/src/concepts/index.md @@ -1,10 +1,10 @@ --- prev: - text: "Guides" - link: "/guides/index" + text: "Monitoring Cron" + link: "../guides/aoconnect/monitoring-cron" next: - text: "References" - link: "/references/index" + text: "Specifications" + link: "./specs" --- # Concepts diff --git a/src/concepts/processes.md b/src/concepts/processes.md index b31c1be..2b77e2a 100644 --- a/src/concepts/processes.md +++ b/src/concepts/processes.md @@ -1,6 +1,6 @@ # Processes -Processes possess the capability to engage in communication via message passing, both receiving and dispatching messages within the network. Additionally, they hold the potential to instantiate further processes, enhancing the network's computational fabric. This dynamic method of data dissemination and interaction within the network is referred to as a 'holographic state,' underpinning the shared and persistent state of the network. +Processes possess the capability to engage in communication via message passing, both receiving and dispatching messages within the network. Additionally, they hold the potential to instantiate further processes, enhancing the network's computational fabric. This dynamic method of data dissemination and interaction within the network is referred to as a 'holographic state', underpinning the shared and persistent state of the network. ![Process-Diagram](process-diagram.png) @@ -62,7 +62,7 @@ ao.spawn(ao.env.Module.Id, { The `ao.env` property contains the `Process` and `Module` Reference Objects -``` +```lua env = { Process = { Id = "5WzR7rJCuqCKEq02WUPhTjwnzllLjGu6SA7qhYpcKRs", diff --git a/src/concepts/tour.md b/src/concepts/tour.md index e1573ec..a8377e4 100644 --- a/src/concepts/tour.md +++ b/src/concepts/tour.md @@ -12,38 +12,38 @@ Welcome to a quick tour of aos! This tutorial will walk you through the key glob - **Functionality**: `Send(Message)` is a global function to send messages to other processes. - **Usage Example**: `Send({Target = "...", Data = "Hello, Process!"})` sends a message with the data "Hello, Process!" to a specified process. -#### 3. Creating Processes with Spawn(Module, Message) +## 3. Creating Processes with Spawn(Module, Message) - **Purpose**: Use `Spawn(Module, Message)` to create new processes. - **Example**: `Spawn("MyModule", {Data = "Start"})` starts a new process using "MyModule" with the provided message. -#### 4. Understanding Name and Owner +## 4. Understanding Name and Owner - **Name**: A string set during initialization, representing the process's name. - **Owner**: Indicates the owner of the process. Changing this might restrict your ability to interact with your process. - **Important Note**: Treat these as read-only to avoid issues. -#### 5. Utilizing Handlers +## 5. Utilizing Handlers - **What They Are**: `Handlers` is a table of helper functions for creating message handlers. - **Usage**: Define handlers in `Handlers` to specify actions for different incoming messages based on pattern matching. -#### 6. Data Representation with Dump +## 6. Data Representation with Dump - **Function**: `Dump` converts any Lua table into a print-friendly format. - **How to Use**: Useful for debugging or viewing complex table structures. Example: `Dump(Inbox)` prints the contents of `Inbox`. -#### 7. Leveraging Utils Module +## 7. Leveraging Utils Module - **Contents**: Utils contains a collection of functional utilities like`map`, `reduce`, and `filter`. - **Usage**: Great for data manipulation and functional programming patterns in Lua. For example, `Utils.map(myTable, function(x) return x * 2 end)` to double the values in a table. -#### 8. Exploring the ao Core Library +## 8. Exploring the ao Core Library - **Description**: `ao` is a core module that includes key functions for message handling and process management. - **Key Features**: Includes functions for sending messages (`send`) and spawning processes (`spawn`), along with environment variables. -#### Conclusion +## Conclusion This brief tour introduces you to the primary globals and functionalities within the aos environment. With these tools at your disposal, you can create and manage processes, handle messages, and utilize Lua's capabilities to build efficient and responsive applications on the aos platform. Experiment with these features to get a deeper understanding and to see how they can be integrated into your specific use cases. Happy coding in aos! diff --git a/src/guides/aos/blueprints/index.md b/src/guides/aos/blueprints/index.md index 29f85e6..254b163 100644 --- a/src/guides/aos/blueprints/index.md +++ b/src/guides/aos/blueprints/index.md @@ -1,3 +1,12 @@ +--- +prev: + text: "Build a Token" + link: "../token" +next: + text: "Token Blueprint" + link: "./token" +--- + # Blueprints Blueprints are predesigned templates that help you quickly build in `ao`. They are a great way to get started and can be customized to fit your needs. diff --git a/src/guides/aos/modules/index.md b/src/guides/aos/modules/index.md index 01c878b..c9b94c2 100644 --- a/src/guides/aos/modules/index.md +++ b/src/guides/aos/modules/index.md @@ -1,3 +1,12 @@ +--- +prev: + text: "Staking Blueprint" + link: "../blueprints/staking" +next: + text: "JSON" + link: "./json" +--- + # Modules Documentation on all the built-in modules for aos. diff --git a/src/guides/index.md b/src/guides/index.md index 426d186..9184ff2 100644 --- a/src/guides/index.md +++ b/src/guides/index.md @@ -1,10 +1,10 @@ --- prev: - text: "Tutorials" - link: "../tutorials/index" + text: "Expanding the Arena" + link: "/tutorials/bots-and-games/build-game" next: - text: "Concepts" - link: "/concepts/index" + text: "aos" + link: "./aos/index" --- # Guides diff --git a/src/index.md b/src/index.md index fb3fdd1..971198e 100644 --- a/src/index.md +++ b/src/index.md @@ -8,19 +8,19 @@ hero: tagline: "Decentralized compute at any scale. Only possible on Arweave." actions: - theme: brand - text: Lets Go! + text: Let's Go! link: /welcome/index features: - - title: Tutorials. + - title: Tutorials details: Follow the step-by-step tutorials to start building on ao. link: /tutorials/index - - title: Guides. + - title: Guides details: Bite size walkthroughs on specific features. link: /guides/index - - title: Concepts. + - title: Concepts details: Learn how the ao network works under the hood. link: /concepts/index --- diff --git a/src/references/index.md b/src/references/index.md index c53be0c..046d9c8 100644 --- a/src/references/index.md +++ b/src/references/index.md @@ -1,10 +1,10 @@ --- prev: - text: "Concepts" - link: "/concepts/index" + text: "The aos interface" + link: "/concepts/tour" next: - text: "Getting Started" - link: "/references/lua" + text: "Lua" + link: "./lua" --- # References diff --git a/src/tutorials/begin/chatroom.md b/src/tutorials/begin/chatroom.md index f012bd8..b3412a9 100644 --- a/src/tutorials/begin/chatroom.md +++ b/src/tutorials/begin/chatroom.md @@ -192,10 +192,10 @@ If she successfully joins the chatroom, she'll then pose the next challenge to y - Provide Onboarding Instructions: Share a simple script with them for easy onboarding: -``` -Hey, let's chat on aos! Join my chatroom by sending this command in your aos environment: +```lua +-- Hey, let's chat on aos! Join my chatroom by sending this command in your aos environment: Send({ Target = [Your Process ID], Action = "Register" }) -Then, you can broadcast messages using: +-- Then, you can broadcast messages using: Send({Target = [Your Process ID], Action = "Broadcast", Data = "Your Message" }) ``` diff --git a/src/tutorials/begin/dao.md b/src/tutorials/begin/dao.md index e52a980..0f0c3ab 100644 --- a/src/tutorials/begin/dao.md +++ b/src/tutorials/begin/dao.md @@ -6,13 +6,13 @@ In our DAO we will implement a process knwon as "slashing". In the case of ao, i Make a new directory called `dao` and copy in the token.lua created in the token guide. -``` +```sh mkdir dao cd dao cp ../token/token.lua . ``` -Now create a new file called dao.lua and open it in your favorite editor. +Now create a new file called `dao.lua` and open it in your favorite editor. ## Writing the DAO code diff --git a/src/tutorials/begin/messaging.md b/src/tutorials/begin/messaging.md index f52ebd5..b63b295 100644 --- a/src/tutorials/begin/messaging.md +++ b/src/tutorials/begin/messaging.md @@ -1,6 +1,6 @@ # Messaging in `ao` -### Learn how Messages gives `ao` Parallel Compute Capability +## Learn how Messages gives `ao` Parallel Compute Capability In `ao`, every process runs in parallel, creating a highly scalable environment. Traditional direct function calls between processes aren't feasible because each process operates independently and asynchronously. @@ -90,7 +90,7 @@ Send({ Target = Morpheus, Data = "Morpheus?" }) # Message is added to the outbox message added to outbox # A New Message is received from `Morpheus`'s process ID -New Message From BWM...ulw: Data = I am here. You are finally awake. Are yo +New Message From BWM...ulw: Data = I am here. You are f ``` @@ -167,7 +167,7 @@ Send Morpheus a message with the tag `Action` and the value `rabbithole`. **Example:** -```sh +```lua Send({ Target = Morpheus, Data = "Code: rabbithole", Action = "Unlock" }) ``` @@ -184,7 +184,7 @@ Send({ Target = Morpheus, Data = "Code: rabbithole", Action = "Unlock" }) - **Workflow Management**: Tags can be instrumental in managing workflows, especially in systems where messages pass through multiple stages or processes. -## Additional Tips for Messaging: +## Additional Tips for Messaging - **Message Structure**: Explore other fields like `Epoch`, `From`, and `Nonce` for more complex messaging needs. - **Debugging**: Use the [`Dump`](/concepts/tour.html#_6-data-representation-with-dump) function to print messages for debugging. diff --git a/src/tutorials/begin/preparations.md b/src/tutorials/begin/preparations.md index e1ee8d9..360e8e5 100644 --- a/src/tutorials/begin/preparations.md +++ b/src/tutorials/begin/preparations.md @@ -6,7 +6,6 @@ You've always known there's more to this world, just outside of your reach. You've been searching for it, not even knowing what it was you were looking for. It... is `ao`. We begin our journey by installing the `aos` client and starting a new process. This will allow us to interact with the ao computer and complete the rest of the tutorial. - ::: ## Video Tutorial @@ -98,4 +97,4 @@ If your OS version is different than the latest version, a message asking if you Welcome to your new home in the ao computer! The prompt you are now looking at is your own personal server in this decentralized machine. -Now, let's journey further down the rabbit hole by exploring one of the two core concept type of ao: [messaging.](messaging) +Now, let's journey further down the rabbit hole by exploring one of the two core concept type of ao: [messaging](messaging). diff --git a/src/tutorials/begin/rabbithole.md b/src/tutorials/begin/rabbithole.md index 4d9c7cd..79fb6e4 100644 --- a/src/tutorials/begin/rabbithole.md +++ b/src/tutorials/begin/rabbithole.md @@ -1,4 +1,4 @@ -# Enter The Construct - An Interactive Tutorial +# Enter `The Construct` - An Interactive Tutorial ![White Rabbit](/white_rabbit_outline.svg) diff --git a/src/tutorials/begin/token.md b/src/tutorials/begin/token.md index 773fb82..6a5b7ff 100644 --- a/src/tutorials/begin/token.md +++ b/src/tutorials/begin/token.md @@ -52,13 +52,13 @@ You should see a new list of handlers that have been loaded into your `aos` proc Now that the token blueprint is loaded, we can test the token by sending a message to ourselves using the `Action = "Info"` tag. ```sh - Send({ Target = ao.id, Action = "Info" }) +Send({ Target = ao.id, Action = "Info" }) ``` This will print a message to the console, but to read the message, we'll need to call the `.Data` from the latest message. ```sh - Inbox[#Inbox].Data +Inbox[#Inbox].Data # Replace `#Inbox` with the number of the last message received. ``` @@ -70,7 +70,7 @@ This will print the token information to the console. It should show your proces Now that we've tested the token and it's working as expected, we can send some tokens to `Trinity`. We'll send 1000 tokens to `Trinity` using the `Action = "Transfer"` tag. ```sh - Send({ Target = ao.id, Action = "Transfer", Recipient = Trinity, Quantity = "1000"}) +Send({ Target = ao.id, Action = "Transfer", Recipient = Trinity, Quantity = "1000"}) ``` When `Trinity` receives the tokens, she'll respond to the transfer with a message to confirm that she's received the tokens. diff --git a/src/tutorials/begin/tokengating.md b/src/tutorials/begin/tokengating.md index 44ea125..edadf32 100644 --- a/src/tutorials/begin/tokengating.md +++ b/src/tutorials/begin/tokengating.md @@ -4,7 +4,7 @@ Now that we've created a token and sent it to `Trinity`, we can use the token to tokengate our chatroom. This will allow only those who have the token to enter the chatroom. ::: -### How to Tokengate the Chatroom +## How to Tokengate the Chatroom Let's create a handler that will allow us to tokengate the chatroom. This handler will respond to the tag `Action = "Broadcast"` meaning it will replace the original `Broadcast` handler we built for our chatroom. @@ -64,7 +64,7 @@ Now that the chatroom is tokengated, let's test it by sending a message to the c First, we'll test it from the original aos process. ```sh - Send({ Target = ao.id , Action = "Broadcast", Data = "Hello" }) +Send({ Target = ao.id , Action = "Broadcast", Data = "Hello" }) # Expected Results: message added to outbox Broadcasting message from Neo. Content: Hello. @@ -76,10 +76,15 @@ Broadcasting message from Neo. Content: Hello. Now, let's test it from a new aos process that doesn't have a token. +```sh +aos chatroom-no-token # the `chatroom-no-token` is the new process name +``` + We'll first need to register to the chatroom. ```sh - Send({ Target = [Your Process ID], Action = "Register" }) +.load chatroom.lua +Send({ Target = [Your Process ID], Action = "Register" }) # Expected Results: message added to outbox New Message From [Your Process ID]: Data = registered @@ -88,7 +93,7 @@ New Message From [Your Process ID]: Data = registered Now, let's try to send a message to the chatroom. ```sh - Send({ Target = [Your Process ID] , Action = "Broadcast", Data = "Hello?" }) +Send({ Target = [Your Process ID] , Action = "Broadcast", Data = "Hello?" }) # Expected Results: message added to outbox UNAUTH REQ: [New Process ID] diff --git a/src/tutorials/bots-and-games/announcements.md b/src/tutorials/bots-and-games/announcements.md index 5942248..12cb9e3 100644 --- a/src/tutorials/bots-and-games/announcements.md +++ b/src/tutorials/bots-and-games/announcements.md @@ -1,12 +1,3 @@ ---- -prev: - text: "Let's Play A Game!" - link: "/tutorials/bots-and-games/ao-effect" -next: - text: "Fetching Game State" - link: "/tutorials/bots-and-games/game-state" ---- - # Interpreting Announcements Welcome back to your coding journey. It's time to use the skills you've acquired from previous tutorials to enhance your gaming experience. diff --git a/src/tutorials/bots-and-games/ao-effect.md b/src/tutorials/bots-and-games/ao-effect.md index 07bda0e..7b0893e 100644 --- a/src/tutorials/bots-and-games/ao-effect.md +++ b/src/tutorials/bots-and-games/ao-effect.md @@ -1,10 +1,7 @@ --- prev: text: "Bots and Games" - link: "/tutorials/bots-and-games/index" -next: - text: "Interpreting Announcements" - link: "/tutorials/bots-and-games/announcements" + link: "./index" --- # Let's Play A Game! @@ -27,22 +24,22 @@ Checkout the guides on the [Mechanics of the Arena](arena-mechanics.md) and [Exp To join this global escapade, you'll need to set things up. Don't worry, it's as easy as 1-2-3! -1. **Install aos** - Fire up your terminal and run: +1.**Install aos** +Fire up your terminal and run: ```bash npm i -g https://get_ao.g8way.io ``` -2. **Launch aos** - Next, create your instance of aos: +2.**Launch aos** +Next, create your instance of aos: ```bash aos ``` -3. **Set Up the Game ID** - Let's keep our game server ID handy for quick access: +3.**Set Up the Game ID** +Let's keep our game server ID handy for quick access: ```lua Game = "0rVZYFxvfJpO__EfOz0_PUQ3GFE9kEaES0GkUDNXjvE" diff --git a/src/tutorials/bots-and-games/arena-mechanics.md b/src/tutorials/bots-and-games/arena-mechanics.md index d33d907..2158def 100644 --- a/src/tutorials/bots-and-games/arena-mechanics.md +++ b/src/tutorials/bots-and-games/arena-mechanics.md @@ -1,12 +1,3 @@ ---- -prev: - text: "Bringing it Together" - link: "/tutorials/bots-and-games/bringing-together" -next: - text: "Expanding the Arena" - link: "/tutorials/bots-and-games/build-game" ---- - # Mechanics of the Arena This guide provides a comprehensive overview of the fundamental mechanics essential for designing and managing arena-style games in `aos`. In arena games, participants engage in rounds, strategically vying to eliminate each other until a sole victor emerges. diff --git a/src/tutorials/bots-and-games/attacking.md b/src/tutorials/bots-and-games/attacking.md index fd42491..db51754 100644 --- a/src/tutorials/bots-and-games/attacking.md +++ b/src/tutorials/bots-and-games/attacking.md @@ -1,12 +1,3 @@ ---- -prev: - text: "Strategic Decicions" - link: "/tutorials/bots-and-games/decisions" -next: - text: "Bringing it Together" - link: "/tutorials/bots-and-games/bringing-together" ---- - # Automated Responses Following our [last guide](decisions), our creation has progressed from a simple bot to a sophisticated autonomous agent. Now, let's further enhance its capabilities by adding a counterattack feature, allowing it to instantly retaliate against an opponent's attack, potentially catching them off-guard before they can retreat to safety. @@ -49,7 +40,7 @@ You can refer to the latest code for `bot.lua` in the dropdown below: LatestGameState = LatestGameState or nil function inRange(x1, y1, x2, y2, range) - return math.abs(x1 - x2) <= range and math.abs(y1 - y2) <= range + return math.abs(x1 - x2) <= range and math.abs(y1 - y2) <= range end function decideNextAction() @@ -57,10 +48,10 @@ function decideNextAction() local targetInRange = false for target, state in pairs(LatestGameState.Players) do - if target ~= ao.id and inRange(player.x, player.y, state.x, state.y, 1) then - targetInRange = true - break - end + if target ~= ao.id and inRange(player.x, player.y, state.x, state.y, 1) then + targetInRange = true + break + end end if player.energy > 5 and targetInRange then @@ -75,54 +66,54 @@ function decideNextAction() end Handlers.add( -"HandleAnnouncements", -Handlers.utils.hasMatchingTag("Action", "Announcement"), -function (msg) - ao.send({Target = Game, Action = "GetGameState"}) - print(msg.Event .. ": " .. msg.Data) -end + "HandleAnnouncements", + Handlers.utils.hasMatchingTag("Action", "Announcement"), + function (msg) + ao.send({Target = Game, Action = "GetGameState"}) + print(msg.Event .. ": " .. msg.Data) + end ) Handlers.add( -"UpdateGameState", -Handlers.utils.hasMatchingTag("Action", "GameState"), -function (msg) - local json = require("json") - LatestGameState = json.decode(msg.Data) - ao.send({Target = ao.id, Action = "UpdatedGameState"}) -end + "UpdateGameState", + Handlers.utils.hasMatchingTag("Action", "GameState"), + function (msg) + local json = require("json") + LatestGameState = json.decode(msg.Data) + ao.send({Target = ao.id, Action = "UpdatedGameState"}) + end ) Handlers.add( -"decideNextAction", -Handlers.utils.hasMatchingTag("Action", "UpdatedGameState"), -function () - if LatestGameState.GameMode ~= "Playing" then - return + "decideNextAction", + Handlers.utils.hasMatchingTag("Action", "UpdatedGameState"), + function () + if LatestGameState.GameMode ~= "Playing" then + return + end + print("Deciding next action.") + decideNextAction() end - print("Deciding next action.") - decideNextAction() -end ) Handlers.add( -"ReturnAttack", -Handlers.utils.hasMatchingTag("Action", "Hit"), -function (msg) - local playerEnergy = LatestGameState.Players[ao.id].energy - if playerEnergy == undefined then - print("Unable to read energy.") - ao.send({Target = Game, Action = "Attack-Failed", Reason = "Unable to read energy."}) - elseif playerEnergy == 0 then - print("Player has insufficient energy.") - ao.send({Target = Game, Action = "Attack-Failed", Reason = "Player has no energy."}) - else - print("Returning attack.") - ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(playerEnergy)}) - end - InAction = false - ao.send({Target = ao.id, Action = "Tick"}) -end + "ReturnAttack", + Handlers.utils.hasMatchingTag("Action", "Hit"), + function (msg) + local playerEnergy = LatestGameState.Players[ao.id].energy + if playerEnergy == undefined then + print("Unable to read energy.") + ao.send({Target = Game, Action = "Attack-Failed", Reason = "Unable to read energy."}) + elseif playerEnergy == 0 then + print("Player has insufficient energy.") + ao.send({Target = Game, Action = "Attack-Failed", Reason = "Player has no energy."}) + else + print("Returning attack.") + ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(playerEnergy)}) + end + InAction = false + ao.send({Target = ao.id, Action = "Tick"}) + end ) ``` diff --git a/src/tutorials/bots-and-games/bringing-together.md b/src/tutorials/bots-and-games/bringing-together.md index cf914aa..a29c39c 100644 --- a/src/tutorials/bots-and-games/bringing-together.md +++ b/src/tutorials/bots-and-games/bringing-together.md @@ -1,12 +1,3 @@ ---- -prev: - text: "Automated Responses" - link: "/tutorials/bots-and-games/attacking" -next: - text: "Mechanics of the Arena" - link: "/tutorials/bots-and-games/arena-mechanics" ---- - # Bringing it Together This final guide wraps up our series, where you've built up an autonomous agent piece by piece. Now, let's refine your agent with some optimizations that fine-tune its operations. Here's a quick overview of the key improvements made: diff --git a/src/tutorials/bots-and-games/build-game.md b/src/tutorials/bots-and-games/build-game.md index 1961407..18c5b74 100644 --- a/src/tutorials/bots-and-games/build-game.md +++ b/src/tutorials/bots-and-games/build-game.md @@ -1,10 +1,7 @@ --- -prev: - text: "Mechanics of the Arena" - link: "/tutorials/bots-and-games/arena-mechanics" next: - text: "Tutorials" - link: "/tutorials/index" + text: "Guides" + link: "/guides/index" --- # Expanding the Arena diff --git a/src/tutorials/bots-and-games/decisions.md b/src/tutorials/bots-and-games/decisions.md index 5b5e1bc..0da573c 100644 --- a/src/tutorials/bots-and-games/decisions.md +++ b/src/tutorials/bots-and-games/decisions.md @@ -1,15 +1,6 @@ ---- -prev: - text: "Fetching Game State" - link: "/tutorials/bots-and-games/game-state" -next: - text: "Automated Responses" - link: "/tutorials/bots-and-games/attacking" ---- - # Strategic Decicions -With the [latest game state](game-state) at your disposal, your bot can evolve into an "autonomous agent". This transition marks an upgrade in functionality, enabling not just reactions to game states but strategic actions that consider context, energy, and proximity to make decisions. +With the [latest game state](game-state) at your disposal, your bot can evolve into an `autonomous agent`. This transition marks an upgrade in functionality, enabling not just reactions to game states but strategic actions that consider context, energy, and proximity to make decisions. ## Writing the Code diff --git a/src/tutorials/bots-and-games/game-state.md b/src/tutorials/bots-and-games/game-state.md index 6e74b62..0b63f80 100644 --- a/src/tutorials/bots-and-games/game-state.md +++ b/src/tutorials/bots-and-games/game-state.md @@ -1,12 +1,3 @@ ---- -prev: - text: "Interpreting Announcements" - link: "/tutorials/bots-and-games/announcements" -next: - text: "Strategic Decicions" - link: "/tutorials/bots-and-games/decisions" ---- - # Fetching Game State Now that you're seeing game announcements directly in your terminal, you have a better grasp of the game's dynamics. However, these insights are limited to specific actions occurring within the game. diff --git a/src/tutorials/bots-and-games/index.md b/src/tutorials/bots-and-games/index.md index 186092e..d01eb33 100644 --- a/src/tutorials/bots-and-games/index.md +++ b/src/tutorials/bots-and-games/index.md @@ -1,10 +1,10 @@ --- prev: - text: "DAO Guide" - link: "/tutorials/dao" + text: "Tokengating" + link: "../begin/tokengating" next: text: "Let's Play A Game!" - link: "/tutorials/bots-and-games/ao-effect" + link: "./ao-effect" --- # Bots and Games diff --git a/src/tutorials/index.md b/src/tutorials/index.md index 0c6f4ee..0279581 100644 --- a/src/tutorials/index.md +++ b/src/tutorials/index.md @@ -1,10 +1,10 @@ --- prev: - text: "Welcome" - link: "/welcome/index" + text: "Testnet Info" + link: "/welcome/testnet-info" next: - text: "Guides" - link: "/guides/index" + text: "Begin" + link: "./begin/index" --- # Tutorials diff --git a/src/welcome/getting-started.md b/src/welcome/getting-started.md index 71c7ed1..8d4d40c 100644 --- a/src/welcome/getting-started.md +++ b/src/welcome/getting-started.md @@ -2,14 +2,14 @@ In less than 5 mins, we'll walk you through the process of taking your first peek into the rabbit hole. 🕳️🐇 -## System requirements. +## System requirements The local client of aos is super simple to install. Just make sure you have: - [NodeJS](https://nodejs.org) version 20+. (If you haven't yet installed it, check out [this page](https://nodejs.org/en/download/package-manager) to find instructions for your OS). - A code editor of your choice. -## Installing aos. +## Installing aos Once you have NodeJS on your machine, all you need to do is install aos and run it: @@ -25,7 +25,7 @@ aos You authenticate yourself to your aos process using a keyfile. If you have an Arweave wallet you can specify it by adding a `--wallet [location]` flag. If you don't, a new keyfile will be generated and stored locally for you at `~/.aos.json`. -## Welcome to the rabbit hole. +## Welcome to the rabbit hole The utility you just started is a local client, which is ready to relay messages for you to your new process inside the ao computer. @@ -65,7 +65,7 @@ aos> Welcome to your new home in the ao computer! The prompt you are now looking at is your own personal server in this decentralized machine. We will be using it to play with and explore ao in the rest of this tutorial. -## Sending your first command. +## Sending your first command Your new personal aos process is a server that lives inside the computer, waiting to receive and execute your commands. diff --git a/src/welcome/index.md b/src/welcome/index.md index 8532565..13ffabe 100644 --- a/src/welcome/index.md +++ b/src/welcome/index.md @@ -1,13 +1,13 @@ --- next: text: "Getting Started" - link: "/welcome/getting-started" + link: "./getting-started" --- -![ao logo](/ao-logo-grey.svg) - # Welcome to ao +![ao logo](/ao-logo-grey.svg) + The ao computer is a world where countless parallel processes interact within a single, cohesive computing environment, seamlessly interlinked through a native message-passing layer. It is a burgeoning ecosystem of decentralized programs, akin to the World Wide Web, where each process, like a website, operates independently yet is intricately woven into a unified experience. ## ao + aos: The rocket and your rocket fuel. diff --git a/src/zh/concepts/CU-diagram.png b/src/zh/concepts/CU-diagram.png new file mode 100644 index 0000000..cd01722 Binary files /dev/null and b/src/zh/concepts/CU-diagram.png differ diff --git a/src/zh/concepts/MU-diagram.png b/src/zh/concepts/MU-diagram.png new file mode 100644 index 0000000..c146420 Binary files /dev/null and b/src/zh/concepts/MU-diagram.png differ diff --git a/src/zh/concepts/SU-diagram.png b/src/zh/concepts/SU-diagram.png new file mode 100644 index 0000000..fc4e112 Binary files /dev/null and b/src/zh/concepts/SU-diagram.png differ diff --git a/src/zh/concepts/ao-pictograph-c94ebf2d.svg b/src/zh/concepts/ao-pictograph-c94ebf2d.svg new file mode 100644 index 0000000..7234f7b --- /dev/null +++ b/src/zh/concepts/ao-pictograph-c94ebf2d.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/zh/concepts/handler-diagram.png b/src/zh/concepts/handler-diagram.png new file mode 100644 index 0000000..2b3a2bd Binary files /dev/null and b/src/zh/concepts/handler-diagram.png differ diff --git a/src/zh/concepts/holographic-state.md b/src/zh/concepts/holographic-state.md new file mode 100644 index 0000000..1333ed7 --- /dev/null +++ b/src/zh/concepts/holographic-state.md @@ -0,0 +1 @@ +TODO diff --git a/src/zh/concepts/how-it-works.md b/src/zh/concepts/how-it-works.md new file mode 100644 index 0000000..1b2ba87 --- /dev/null +++ b/src/zh/concepts/how-it-works.md @@ -0,0 +1,27 @@ +# ao 信使的工作原理 + +在我们深入了解 ao 之前,我想与你分享一些有关 UNIX 的信息。 Unix 是一个功能强大的操作系统,但在其设计中它专注于两种主要 `“类型”`。 文件和程序。 文件是数据,程序是逻辑,当你将两者结合起来时,你就会得到信息。 + +`Input.file | TransformProgram | Output.file` + +你可能在命令行上做了类似的事情,但不知道自己在做什么。 能够将文件连接到程序并返回文件,然后将其传递给其他程序,从而创建一个由简单应用程序组成的复杂系统。 这是一个非常牛逼的想法。 + +现在,我们来讨论 `ao` 即超并行计算机,并将文件更改为 `ao` 概念下的消息,将程序更改为 `ao` 概念下的进程。 `ao` 计算机接收消息并将它们发送到进程,其中这些进程可以输出可发送到其他进程的消息。 结果是一个建立在简单模块化逻辑容器之上的复杂系统。 + +`MessageA | Process | MessageB` + +![ao-消息](https://g8way.io/eAoqMqhwQ5vnpH_NJ6H2PiGgrcGDprtDIUH9Re2xcic) + +以下是流程图中概述的过程的描述: + +1. 一条消息从 ao Connect 被发出。 该消息使用 POST 请求发送到 `mu` 服务。 请求正文包含遵循同一协议的数据,标记为 `ao`,类型为 `Message`。 + +2. `mu` 服务处理 POST 请求并将消息转发到 `su` 服务。 这也是使用具有相同数据协议和消息类型的 POST 请求来完成的。 + +3. `su` 服务似乎与存储消息的称为 Arweave 的存储或数据层进行交互。 + +4. 向 `cu` 服务发出 GET 请求,以根据消息 ID 检索结果。 `cu` 是一种进程上的对消息求值并可以根据单个消息标识符返回结果的服务。 + +5. 向 `su` 服务发出 GET 请求以检索消息。 此请求查找来自某个进程 ID 的开始结束时间范围是 `from*` 到 `to*` 的消息。 + +6. 最后一步是推送所有发件箱消息。 它涉及检查结果对象中的消息和新进程生成请求。 根据此检查的结果,可以对每个相关消息或新进程生成请求重复步骤 2、3 和 4。 diff --git a/src/zh/concepts/index.md b/src/zh/concepts/index.md new file mode 100644 index 0000000..abf3779 --- /dev/null +++ b/src/zh/concepts/index.md @@ -0,0 +1,26 @@ +--- +prev: + text: "监控 Cron" + link: "/zh/guides/aoconnect/monitoring-cron" +next: + text: "规范" + link: "./specs" +--- + +# 概念 + +ao在设计中内置了很多概念,但核心概念都是非常简单的原则: + +- 两种核心类型:消息和进程 +- 没有共享状态,只有全息状态 +- 去中心化计算机(网格) + +下面是一组概念文档,将 ao 系统分解为不同的部分。 + +- [规范](specs) +- [进程](processes) +- [消息](messages) +- [单元](units) +- [消息传递的工作原理](how-it-works) + +[[toc]] diff --git a/src/zh/concepts/lua.md b/src/zh/concepts/lua.md new file mode 100644 index 0000000..49f2f08 --- /dev/null +++ b/src/zh/concepts/lua.md @@ -0,0 +1,124 @@ +# 走马观花的学习下 Lua + +在我们更深入地探索 ao 之前,让我们花点时间学习一下 Lua 的基础知识:你指挥 aos 进程的伙伴。 + +Lua 是一种简单的语言,没有什么惊喜。 如果你了解 Javascript,你会感觉它是一个简化、更纯粹的版本。 如果你从头开始学习,它看起来像是一门小型语言,专注于重要的事情:使用合理的语法进行极简的计算。 + +在本节中,我们将在短短几分钟内介绍 Lua 的基础知识。 如果你已经了解 Lua,请直接跳到[下一章](tour.md) + +## 跳回到你的 aos 进程 + +出于本教程的目的,我们假设你已经完成了[入门](/zh/welcome/getting-started) 指南。 如果没有,请先完成。 + +如果你注销了进程,你可以随时通过在命令行上运行 `aos` 来重新打开它,也可以选择使用 `--wallet [location]` 指定你的密钥文件。 + +## 基本 Lua 表达式 + +在本入门书的其余部分中,我们将快速浏览 Lua 的核心功能和语法。 + +边推进边尝试你的 aos 进程中的示例,如果你觉得很直观明了,则可以跳过它们。 + +- **基本算术**:尝试一些基本算术,例如 `5 + 3`。 处理后,你将看到结果 `8`。 `+`、`-`、`*`、`/` 和 `^` 都按你的预期工作。 `%` 是 Lua 用于求模的符号。 +- **设置变量**:输入 `a = 10` 并按 Enter 键。 这会将变量 `a` 设置为 10。按照惯例(语法上不强制),全局变量在 Lua 中以大写字母开头(例如 `Handlers`)。 + +- **使用变量**:现在输入 `a * 2`。 你将看到命令行上返回 `20`。 +- **字符串连接**:通过执行 `"Hello, " .. ao.id` 向自己说 `Hello`。 + +## 尝试条件语句 + +- **If-Else**:与大多数编程语言一样,Lua 使用 if-else 块来有条件地执行代码。 + + 在你的 aos 进程中,输入 `.editor` 并按 Enter 键。 这将在命令行界面中打开一个内联文本编辑器。 + + ```lua + aos_coolness = 9001 + if aos_coolness > 9000 then + return "aos is coolness is over 9000!" + else + return "Oh. 🤷" + end + ``` + + 在终端上完成编辑后,在新行中输入 `.done` 并按 Enter 键。 这将终止编辑模式并将表达式提交给你的进程进行求值。 + + 结果,你会看到 `aos is coolness is over 9000!` + + Lua 中的 `if` 语句还可以有额外的 `elseif [condition] then` 块,更便于写多级条件执行语句。 + +## Lua 中的循环 + +在 Lua 中循环代码有几种不同的方法。 以下是我们的最爱: + +- **While 循环**: + + 首先输入 `n = 0` 并按 Enter 键将计数器初始化为零。 + + 然后使用 `.editor` 再次打开内联编辑器。 + + ```lua + while n < 5 do + n = n + 1 + end + ``` + + 在新行中输入 `.done` 以执行 while 循环。 你只需运行 `n` 即可检查循环结果。 + +- **For 循环**: + + Lua 还可以在一组值之间执行 python 风格的 `for` 循环。 例如,使用 `.editor` 输入以下代码块: + + ```lua + for m = 1, 100 do + n = n + m + end + ``` + + 再次运行 `n` 来得到变量的新值。 + +## 上手函数 + +- **定义一个函数**: + + 再次使用 `.editor` ,提交以下行: + + ```lua + function greeting(name) + return "Hello, " .. name + end + ``` + + 一旦提交,aos 将返回 `undefined`,因为 Lua 中的函数(和变量)定义不返回值。 + + Lua 还具有 `匿名` 或 `高阶` 函数。 这些本质上允许你像使用普通数据一样使用函数本身 - 作为参数传递给其他函数等。以下示例定义了一个匿名函数,并且与上面的示例等效: + + ```lua + greeting = + function(name) + return "Hello, " .. name + end + ``` + +- **调用函数**:使用 `greeting("Earthling")` 调用函数。 aos 将返回 `"Hello, Earthling"`。 + +## 用表定义深层对象 + +表是Lua 唯一的复合数据结构。 它们将 `键` 映射到 `值`,但也可以像传统数组一样使用。 + +- **创建一个简单的表**:输入 `ao_is = {"hyper", "parallel", "compute"}` 来创建一个简单的表。 +- **访问表的元素**:使用 `ao_is[2]` 访问元素。 aos 将返回 `parallel`。 注意:Lua 中的下标是从 1 开始的! +- **统计表的元素**:Lua 中表的大小是通过运算符 `#` 读取的。 例如,运行 `#ao_is` 将返回 `3`。 +- **设置命名元素**:输入 `ao_is["cool"] = true` 以向表中添加新的命名键。 命名元素也可以使用 `.` 运算符访问,例如 `ao_is.cool`。 + +## Lua 注意事项 + +aos 使用 Lua 是因为它是一种简单、明了的语言,大多数有经验的程序员都可以很快学会,而且由于它在 Roblox 等视频游戏中的使用,它也是一种越来越流行的首选编程语言。 + +尽管如此,该语言的一些问题很容易让新手 Lua 构建者犯错。 口味可能会有所不同,但以下是我们的 Lua [wat](https://www.destroyallsoftware.com/talks/wat) 的详尽列表: + +- **记住:** 表索引从 1 开始,而不是 0! +- **记住:** `不等于` 用 `~=` 表示,而不是 `!=` 或类似的。 +- **记住:** Lua 中的对象被称为 `表`,而不是它们更常见的名称。 + +## 继续前行 + +记住这些,你现在已经了解了使用 Lua 构建出色的去中心化进程所需的一切! 在下一章中,我们将开始使用 Lua 和 aos 构建并行进程。 diff --git a/src/zh/concepts/message-workflow-diagram.png b/src/zh/concepts/message-workflow-diagram.png new file mode 100644 index 0000000..17b5b43 Binary files /dev/null and b/src/zh/concepts/message-workflow-diagram.png differ diff --git a/src/zh/concepts/messages.md b/src/zh/concepts/messages.md new file mode 100644 index 0000000..706c707 --- /dev/null +++ b/src/zh/concepts/messages.md @@ -0,0 +1,40 @@ +# 消息 + +消息作为 ao 中的基本数据协议单元,依据 [ANS-104 DataItems](https://specs.g8way.io/?tx=xwOgX-MmqN5_-Ny_zNu2A8o-PnTGsoRb_3FrtiMAkuw) 制作而成,从而与 Arweave 的原生结构保持一致。 当消息体进入进程时,其结构如下: + +```lua +{ + Cron = false, + Data = "Hello aos", + Epoch = 0, + From = "5WzR7rJCuqCKEq02WUPhTjwnzllLjGu6SA7qhYpcKRs", + Id = "ayVo53qvZswpvxLlhMf8xmGjwxN0LGuHzzQpTLT0_do", + Nonce = 1, + Owner = "z1pq2WzmaYnfDwvEFgUZBj48anUsxxN64ZjbWOsIn08", + Signature = "...", + Tags = { + Type = "Message", + Variant = "ao.TN.1", + ["Data-Protocol"] = "ao", + ["From-Module"] = "lXfdCypsU3BpYTWvupgTioLoZAEOZL2_Ihcqepz6RiQ", + ["From-Process"] = "5WzR7rJCuqCKEq02WUPhTjwnzllLjGu6SA7qhYpcKRs" + }, + Target = "5WzR7rJCuqCKEq02WUPhTjwnzllLjGu6SA7qhYpcKRs", + Timestamp = 1704936415711, + ["Block-Height"] = 1340762, + ["Forwarded-By"] = "z1pq2WzmaYnfDwvEFgUZBj48anUsxxN64ZjbWOsIn08", + ["Hash-Chain"] = "hJ0B-0yxKxeL3IIfaIIF7Yr6bFLG2vQayaF8G0EpjbY" +} +``` + +该架构将任务类型与消息类型合并,使进程能够全面了解消息的上下文,以便进行有效的处理。 + +下面是发送消息时,消息在 ao 计算机内流转的流程图。 + +![消息工作流程](message-workflow-diagram.png) + +消息工作流程从 MU(信使单元)开始,消息的签名在此处进行验证。紧接着,SU(调度程序单元)为消息分配 Epoch 和 Nonce,将消息与任务类型捆绑在一起,并将其分发给 Arweave。 随后,`aoconnect` 库从 CU(计算单元)拿到结果。 然后,CU 从 SU(调度器单元)读取此前流转到当前消息 Id 的所有消息,处理它们以推断出结果。 完成后,计算结果被传送回给 `aoconnect`,`aoconnect` 库被集成在 `aos` 等客户端接口中。 + +## 概括 + +消息作为 ao 网络的主要数据协议类型,利用 Arweave 原生的 ANS-104 数据项。消息包含多个字段,包括数据内容、来源、目标以及签名和随机数等加密元素。 接着他们被发给信使单元 (MU) ,以确保它们经过签名,通过调度器单元 (SU) 对它们进行时间戳和排序,然后捆绑并发布到 Arweave。 然后,`aoconnect` 库从计算单元 (CU) 读取结果,CU 通过处理消息来计算出结果后通过 `aoconnect` 发回响应,供 `aos` 等客户端使用。 CU 是这些进程的执行环境。 diff --git a/src/zh/concepts/process-diagram.png b/src/zh/concepts/process-diagram.png new file mode 100644 index 0000000..66c8c2d Binary files /dev/null and b/src/zh/concepts/process-diagram.png differ diff --git a/src/zh/concepts/processes.md b/src/zh/concepts/processes.md new file mode 100644 index 0000000..dbab3a4 --- /dev/null +++ b/src/zh/concepts/processes.md @@ -0,0 +1,84 @@ +# 进程 + +进程之间具有通过消息传递进行通信的能力,包括在网络内接收和发送消息。 此外,它们还具有实例化更多进程的潜力,从而增强网络的计算结构。这种网络内数据传播和交互的动态方法被称为 `全息状态`,支撑网络状态的共享和持久化。 + +![流程图](process-diagram.png) + +当使用 `aos` 构建进程时,你可以添加`处理程序`(`handlers`),可以通过调用 `Handlers.add` 函数并传递 “name”、“match”函数和“handle”函数来添加这些处理程序。 + +![处理程序图](handler-diagram.png) + +核心模块包含一个注入到处理函数中的帮助程序库,该库称为 `ao`。 + +```lua +{ + env = { + Process = { + Id = "5WzR7rJCuqCKEq02WUPhTjwnzllLjGu6SA7qhYpcKRs", + Owner = "_r9LpP4FtClpsGX3TOohubyaeb0IQTZZMcxQ24tTsGo", + Tags = {...} + }, + Module = { + Id = "UAUszdznoUPQvXRbrFuIIH6J0N_LnJ1h4Trej28UgrE", + Owner = "_r9LpP4FtClpsGX3TOohubyaeb0IQTZZMcxQ24tTsGo", + Tags = {..} + } + }, + id = "5WzR7rJCuqCKEq02WUPhTjwnzllLjGu6SA7qhYpcKRs", + isTrusted = "function: 0x5468d0", + result = "function: 0x547120", + send = "function: 0x547618", + spawn = "function: 0x5468b0" +} +``` + +在这个 `ao` 辅助库中要查看的主要函数是 + +- ao.send(Message) - 向进程发送消息 +- ao.spawn(Module, Message) - 创建一个新进程 + +## ao.send 示例 + +```lua +ao.send({ + Target = Chatroom, + Action = "Broadcast", + Data = "Hello from my Process!" +}) +``` + +## ao.spawn 示例 + +```lua +ao.spawn(ao.env.Module.Id, { + ['Memory-Limit'] = "500-mb", + ['Compute-Limit"] = "900000000000000000" +}) +``` + +## ao.env + +> 注意:`ao.env` 是开发人员创建进程时可能要用到的重要上下文数据。 + +`ao.env` 属性包含 `Process` 和 `Module` 引用对象 + +```lua +env = { + Process = { + Id = "5WzR7rJCuqCKEq02WUPhTjwnzllLjGu6SA7qhYpcKRs", + Owner = "_r9LpP4FtClpsGX3TOohubyaeb0IQTZZMcxQ24tTsGo", + Tags = {...} + }, + Module = { + Id = "UAUszdznoUPQvXRbrFuIIH6J0N_LnJ1h4Trej28UgrE", + Owner = "_r9LpP4FtClpsGX3TOohubyaeb0IQTZZMcxQ24tTsGo", + Tags = {..} + } +} +``` + +`Process` 和 `Module` 都包含 `ao` 数据协议的属性。 + +## 概括 + +网络中的进程通过消息传递进行通信,并且可以创建新进程,从而形成共享和持久数据的 `"全息状态"`。 开发人员可以使用 `aos` 构建进程,通过 `Handlers.add` 函数添加具有特定名称、以及匹配和处理函数的处理程序。 核心模块中的 `ao` 辅助库有助于此过程,提供 `ao.send` 函数来分发消息以及 `ao.spawn` 函数来创建新模块,以及同等重要的 `ao.env` 属性,它包含进程和模块的必要信息。`ao` 数据协议概述了这些元素的结构和属性。 diff --git a/src/zh/concepts/specs.md b/src/zh/concepts/specs.md new file mode 100644 index 0000000..b586c81 --- /dev/null +++ b/src/zh/concepts/specs.md @@ -0,0 +1,9 @@ +# ao 规范 + +## 什么是 `ao` ? + +`ao` 计算机是一个 [actor oriented](https://en.wikipedia.org/wiki/Actor_model) 的机器,它产生于遵守其核心数据协议的节点网络,运行在 [Arweave](https://arweave.org) 网络上。 本文档简要介绍了该协议及其功能以及技术细节,以便构建者可以创建与其集成的新实现和服务。 + +`ao` 计算机是一个单一的、统一的计算环境([单系统映像](https://en.wikipedia.org/wiki/Single_system_image)),托管在分布式网络中的一组异构节点上。 `ao` 旨在提供一个环境,其中可以驻留任意数量的并行进程,并通过开放的消息传递层进行协调。 这种消息传递标准将机器的独立运行进程连接到一个 `"网络"` 中,就像网站在独立服务器上运行但通过超链接连接成一个有凝聚力的统一整体。 + +[了解更多](https://ao.g8way.io/specs) diff --git a/src/zh/concepts/tour.md b/src/zh/concepts/tour.md new file mode 100644 index 0000000..32b263b --- /dev/null +++ b/src/zh/concepts/tour.md @@ -0,0 +1,48 @@ +# aos 简明教程 + +欢迎来到 aos 快速入门!本教程将引导你了解 aos 环境中可用的关键全局函数和变量,让你对如何有效地与 aos 交互和使用有基本的了解。 + +## 1.收件箱(Inbox)简介 + +- **它是什么**:`Inbox` 是一个 Lua 表,用于存储进程收到但尚未处理的所有消息。 +- **如何使用**:直接输入 `Inbox` 可以查看传入的消息。 迭代 `Inbox[x]` 来处理这些消息。 + +## 2. 使用 `Send(Message)` 发送消息 + +- **功能**:`Send(Message)` 是一个向其他进程发送消息的全局函数。 +- **使用示例**:`Send({Target = "...", Data = "Hello, Process!"})` 发送一条带有数据 `Hello, Process!` 的消息到指定的进程。 + +## 3. 使用 `Spawn(Module, Message)` 创建进程 + +- **目的**:使用 `Spawn(Module, Message)` 创建新进程。 +- **示例**:`Spawn("MyModule", {Data = "Start"})` 使用 `MyModule` 和提供的消息启动一个新进程。 + +## 4. 理解名称(Name)和所有者(Owner) + +- **名称**:初始化时设置的字符串,代表进程的名称。 +- **所有者**:表示进程的所有者。 更改此值可能会限制你与流程交互的能力。 +- **重要提示**:将它们视为只读以避免出现问题。 + +## 5. 使用处理程序(Handlers) + +- **它们是什么**:`Handlers` 是用于创建消息处理程序的辅助函数表。 +- **用法**:在 `Handlers` 中定义处理程序,以根据模式匹配给不同的消息指定对应操作。 + +## 6. 使用 `Dump` 进行数据展示 + +- **功能**:`Dump` 将任何 Lua 表转换为适合打印的格式。 +- **如何使用**:对于调试或查看复杂的表结构很有用。 示例:`Dump(Inbox)` 打印 `Inbox` 的内容。 + +## 7. 用好 Utils 模块 + +- **内容**:Utils 包含一系列功能实用程序,如 `map`、`reduce`和`filter`。 +- **用法**:非常适合 Lua 中的数据操作和函数式编程模式。 例如,`Utils.map(myTable, function(x) return x * 2 end)` 将表中的值加倍。 + +## 8. 探索 ao 核心库 + +- **描述**:`ao` 是一个核心模块,包括消息处理和进程管理的关键功能。 +- **主要功能**:包括发送消息(`send`)和生成进程(`spawn`)的函数以及环境变量。 + +## 结论 + +此简明教程向你介绍了 aos 环境中的主要全局变量和功能。 有了这些工具(在手边),你可以创建和管理进程、处理消息,并利用 Lua 的功能在 aos 平台上构建高效且响应迅速的应用程序。 尝试这些功能以更深入地了解以便明白如何将它们集成到你的特定用例中。 在 aos 中玩的开心! diff --git a/src/zh/concepts/units.md b/src/zh/concepts/units.md new file mode 100644 index 0000000..755f08f --- /dev/null +++ b/src/zh/concepts/units.md @@ -0,0 +1,80 @@ +# 单元 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +## 什么是单元? + +ao 计算机由三种单元类型组成,每种类型都包含一组计算机相应的职责。 每个单元都可以水平扩展。 + +在 ao 中,我们有 `信使单元` 或 `MU`、 `调度单元` 或 `SU` 以及 `计算单元` 或 `CU`。 这些单元是 ao 计算机网格的构建块。 网络上可以有 1 个或多个这样的单元,它们协同工作来为 ao 操作系统即 `aos` 提供动力。 + +![MU](MU-diagram.png) + +- 信使单元 - 该单元是 ao 的前门,它接收来自外部的所有消息并按序引导给进程。 这种流量我们称之为 `推送`。 每个进程在处理消息时可以返回一个发件箱,这个发件箱可以填充消息或生成新进程的请求,信使单元负责从发件箱中提取这些消息并签名后将它们发送到调度单元进行加工。 + +![SU 图](SU-diagram.png) + +- 调度单元 - 调度单元负责对消息进行排序,并将这些消息存储在 Arweave 上。 重要的是,每条消息都必须适当排序,以便求值过程可以被重现和验证。 调度单元负责这整个过程。 它提供了通过端点查询的能力,以获得用于求值的消息的顺序。 + +![CU 图](CU-diagram.png) + +- 计算单元 - 计算单元负责计算,该单元加载二进制模块并管理该模块的内存,以便进程的执行始终在最新的内存上运行。 计算单元将求值结果返回给信使单元,然后信使单元可以将任何消息推送到给定进程的发件箱中。 + +## 概括 + +ao 计算机由三种可扩展单元类型组成:信使单元 (MU)、调度器单元 (SU) 和计算单元 (CU),它们构成了 ao 计算机的基础。 这些单元可以在网络上存在多个,并共同运行 ao 操作系统 (aos)。 + +MU 充当入口点,接收外部消息并管理进程通信。 它处理来自进程发件箱的传出消息和生成新进程请求,并将它们转发到 SU。 + +SU 确保消息正确排序并存储在 Arweave 上,维护顺序以实现消息求值的重放和验证一致性。 + +CU 处理计算、加载二进制模块和管理内存,以确保进程使用当前数据运行。 然后它将求值结果返回给 MU 以进行进一步的消息处理。 diff --git a/src/zh/guides/aoconnect/aoconnect.md b/src/zh/guides/aoconnect/aoconnect.md new file mode 100644 index 0000000..61ce9ab --- /dev/null +++ b/src/zh/guides/aoconnect/aoconnect.md @@ -0,0 +1,13 @@ +--- +prev: + text: "Utils" + link: "zh/guides/aos/modules/utils" +next: + text: "安装 aoconnect" + link: "zh/guides/aoconnect/installing-connect" +--- + +# aoconnect + +**aoconnect** 是一个用于同 NodeJS系统或浏览器交互的 Javascript/Typescript 库。 +本节中的指南提供了如何利用 **aoconnect** 的代码片段。所有的代码片段均用 Javascript 编写,但应该很容易转换为 Typescript。 diff --git a/src/zh/guides/aoconnect/calling-dryrun.md b/src/zh/guides/aoconnect/calling-dryrun.md new file mode 100644 index 0000000..f861ae3 --- /dev/null +++ b/src/zh/guides/aoconnect/calling-dryrun.md @@ -0,0 +1,17 @@ +# 调用 DryRun + +DryRun 是将消息对象发送到特定进程并获取返回的结果对象的过程,但内存不会被保存,这非常适合创建一个读取消息以返回内存的当前值。例如,一个代币的余额,或者一个转账的结果等。你可以使用 DryRun 来获取输出,而无需发送实际消息 + +```js +import { createDataItemSigner, dryrun } from "@permaweb/aoconnect"; + +const result = await dryrun({ + process: 'PROCESSID', + data: '', + tags: [{name: 'Action', value: 'Balance'}, + anchor: '1234', + ...rest are optional (Id, Owner, etc) +}); + +console.log(result.Messages[0]); +``` diff --git a/src/zh/guides/aoconnect/connecting.md b/src/zh/guides/aoconnect/connecting.md new file mode 100644 index 0000000..88a9296 --- /dev/null +++ b/src/zh/guides/aoconnect/connecting.md @@ -0,0 +1,50 @@ +# 连接到特定的 ao 节点 + +在你的代码中包含 **aoconnect** 时,你可以连接到特定的 MU(消息单元)和 CU(计算单元),并能够指定一个 Arweave 网关。这可以通过导入 `connect` 函数并从对 `connect` 函数的调用中提取函数来完成。 + +你希望了解在发送你的消息时调用了哪个 MU,以便稍后可以从指定的 MU 进行调试。你也可能想要从特定的 CU 读取结果。事实上,你可能出于某种其他原因偏好某个特定的 MU 和 CU。你可以指定其他的网关,默认的网关是即 :arweave.net + +## aoconnect 的导入 + +```js +// Here aoconnect will implicitly use the default nodes/units +import { + result, + results, + message, + spawn, + monitor, + unmonitor, + dryrun, +} from "@permaweb/aoconnect"; +``` + +## 连接到特定的 MU, CU, 和网关 + +```js +import { connect } from "@permaweb/aoconnect"; + +const { result, results, message, spawn, monitor, unmonitor, dryrun } = connect( + { + MU_URL: "https://mu.ao-testnet.xyz", + CU_URL: "https://cu.ao-testnet.xyz", + GATEWAY_URL: "https://arweave.net", + }, +); + +// now spawn, message, and result can be used the same way as if they were imported directly +``` + +
+ +连接的这三个参数都是可选的,仅指定 1 个或 2 个,或者不指定都是有效的。例如,你可以仅传入 MU_URL。 + +```js +import { connect } from "@permaweb/aoconnect"; + +const { result, results, message, spawn, monitor, unmonitor, dryrun } = connect( + { + MU_URL: "https://ao-mu-1.onrender.com", + }, +); +``` diff --git a/src/zh/guides/aoconnect/installing-connect.md b/src/zh/guides/aoconnect/installing-connect.md new file mode 100644 index 0000000..9db0ae4 --- /dev/null +++ b/src/zh/guides/aoconnect/installing-connect.md @@ -0,0 +1,55 @@ +# 安装 ao connect + +## 先决条件 + +--- + +为了在你的应用程序中安装 **ao connect**,你必须安装 NodeJS/NPM 18 或更高版本。 +
+ +## 安装方法 + +### 使用 npm 安装 + +```sh +npm install --save @permaweb/aoconnect +``` + +### 使用 yarn 安装 + +```sh +yarn add @permaweb/aoconnect -D +``` + +
+ +安装完成后,你可以在 NodeJS 或 浏览器中使用 **aoconnect**。根据你的项目类型(模块系统),可以按照下面的方式引入 **aoconnect** + +#### ESM(Node & 浏览器)即 type: module + +```js +import { + result, + results, + message, + spawn, + monitor, + unmonitor, + dryrun, +} from "@permaweb/aoconnect"; +``` + +#### CJS(Node)即 type: `commonjs` + + +```js +const { + result, + results, + message, + spawn, + monitor, + unmonitor, + dryrun, +} = require("@permaweb/aoconnect"); +``` diff --git a/src/zh/guides/aoconnect/monitoring-cron.md b/src/zh/guides/aoconnect/monitoring-cron.md new file mode 100644 index 0000000..5f73d0e --- /dev/null +++ b/src/zh/guides/aoconnect/monitoring-cron.md @@ -0,0 +1,33 @@ +# 监控 Cron + +当使用 cron 消息时,ao 用户需要一种方式开始接收这些消息,通过使用这个监控方法,ao 用户可以启动 cron 消息的订阅服务。设置 cron 标签意味着你的进程将开始在其发件箱中生成 cron 结果,但如果你希望这些结果中的消息通过网络被推送,则需要监控这些结果。 + +```js +import { readFileSync } from "node:fs"; +import { createDataItemSigner, monitor } from "@permaweb/aoconnect"; + +const wallet = JSON.parse( + readFileSync("/path/to/arweave/wallet.json").toString(), +); + +const result = await monitor({ + process: "process-id", + signer: createDataItemSigner(wallet), +}); +``` + +你可以通过调用 unmonitor 来停止监控 + +```js +import { readFileSync } from "node:fs"; +import { createDataItemSigner, unmonitor } from "@permaweb/aoconnect"; + +const wallet = JSON.parse( + readFileSync("/path/to/arweave/wallet.json").toString(), +); + +const result = await unmonitor({ + process: "process-id", + signer: createDataItemSigner(wallet), +}); +``` diff --git a/src/zh/guides/aoconnect/reading-results.md b/src/zh/guides/aoconnect/reading-results.md new file mode 100644 index 0000000..af747ae --- /dev/null +++ b/src/zh/guides/aoconnect/reading-results.md @@ -0,0 +1,41 @@ +# 从 ao 进程读取结果 + +在 ao 中,消息产生的结果通过计算单元(CU)提供。结果是包含以下字段的 JSON 对象:messages, spawns, output 和 error。 + +结果包含ao系统用来发送消息和生成进程的返回值。一个进程可以发送消息,就像你作为开发者可以通过在结果中返回消息和返回值一样。 + +你可能想要访问一个结果,以显示由你的消息生成的输出。或者,你可能想要看看生成了哪些消息等。你不需要自己取出结果中的消息和返回值并发送它们。它们由消息单元(MU)自动处理。对结果的调用还可以为你提供多个结果的分页列表。 + +## 获取单个结果 + +```js +import { result } from "@permaweb/aoconnect"; + +let { Messages, Spawns, Output, Error } = await result({ + // the arweave TXID of the message + message: "message-id", + // the arweave TXID of the process + process: "process-id", +}); +``` + +## 获取一组结果 + +```js +import { results } from "@permaweb/aoconnect"; + +// fetching the first page of results +let resultsOut = await results({ + process: "process-id", + sort: "ASC", + limit: 25, +}); + +// calling more with a cursor +let resultsOut2 = await results({ + process: "process-id", + from: resultsOut.edges?.[resultsOut.edges.length - 1]?.cursor ?? null, + sort: "ASC", + limit: 25, +}); +``` diff --git a/src/zh/guides/aoconnect/sending-messages.md b/src/zh/guides/aoconnect/sending-messages.md new file mode 100644 index 0000000..a708156 --- /dev/null +++ b/src/zh/guides/aoconnect/sending-messages.md @@ -0,0 +1,71 @@ +# Sending a Message to a Process + +可以在 [ao Messages](../../concepts/messages.md) 概念中找到关于消息的深入探讨。本指南着重于使用 ao connect 向进程发送消息。 + +发送消息是你的应用与 ao 交互的核心方式。消息是输入到一个进程的。消息有五个部分你可以指定,分别是“目标(target)”,“数据(data)”,“标签(tags)”,“锚点(anchor)”,以及最后的消息“签名(signature)”。 + +请参考你的进程模块的源代码或文档,以了解消息如何在其计算中使用。ao connect 库将解析你在下面的代码中传递的参数,构造一个消息,并发送它。 + +## 在 NodeJS 中发送消息 + +```js +import { readFileSync } from "node:fs"; + +import { message, createDataItemSigner } from "@permaweb/aoconnect"; + +const wallet = JSON.parse( + readFileSync("/path/to/arweave/wallet.json").toString(), +); + +// The only 2 mandatory parameters here are process and signer +await message({ + /* + The arweave TXID of the process, this will become the "target". + This is the process the message is ultimately sent to. + */ + process: "process-id", + // Tags that the process will use as input. + tags: [ + { name: "Your-Tag-Name-Here", value: "your-tag-value" }, + { name: "Another-Tag", value: "another-value" }, + ], + // A signer function used to build the message "signature" + signer: createDataItemSigner(wallet), + /* + The "data" portion of the message + If not specified a random string will be generated + */ + data: "any data", +}) + .then(console.log) + .catch(console.error); +``` + +## 在浏览器中发送消息 + +```js +import { message, createDataItemSigner } from "@permaweb/aoconnect"; + +// The only 2 mandatory parameters here are process and signer +await message({ + /* + The arweave TXID of the process, this will become the "target". + This is the process the message is ultimately sent to. + */ + process: "process-id", + // Tags that the process will use as input. + tags: [ + { name: "Your-Tag-Name-Here", value: "your-tag-value" }, + { name: "Another-Tag", value: "another-value" }, + ], + // A signer function used to build the message "signature" + signer: createDataItemSigner(globalThis.arweaveWallet), + /* + The "data" portion of the message. + If not specified a random string will be generated + */ + data: "any data", +}) + .then(console.log) + .catch(console.error); +``` diff --git a/src/zh/guides/aoconnect/spawning-processes.md b/src/zh/guides/aoconnect/spawning-processes.md new file mode 100644 index 0000000..396250a --- /dev/null +++ b/src/zh/guides/aoconnect/spawning-processes.md @@ -0,0 +1,65 @@ +# 创建一个进程 + +可以在[ao Processes](../../concepts/processes.md) 概念中找到关于进程的深入介绍。本指南聚焦于使用 ao connect 创建一个进程。 + +为了创建一个进程,你必须拥有已经上传到 Arweave 的一个 ao 模块的 TXID。模块是进程的源代码。进程本身是那个源码的实例化。 + +你还必须拥有一个调度单元(Scheduler Unit, SU)的钱包地址。指定的 SU 将作为此进程的调度器。这意味着系统中的所有节点都可以知道它们需要为此进程读写到这个 SU。你可以使用下面的地址。 + +## 调度器的钱包地址 + +```sh +fcoN_xJeisVsPXA-trzVAuIiqO3ydLQxM-L4XbrQKzY +``` + +## 在 NodeJS 中创建一个进程 + +```js +import { readFileSync } from "node:fs"; + +import { createDataItemSigner, spawn } from "@permaweb/aoconnect"; + +const wallet = JSON.parse( + readFileSync("/path/to/arweave/wallet.json").toString(), +); + +const processId = await spawn({ + // The Arweave TXID of the ao Module + module: "module TXID", + // The Arweave wallet address of a Scheduler Unit + scheduler: "TZ7o7SIZ06ZEJ14lXwVtng1EtSx60QkPy-kh-kdAXog", + // A signer function containing your wallet + signer: createDataItemSigner(wallet), + /* + Refer to a Processes' source code or documentation + for tags that may effect its computation. + */ + tags: [ + { name: "Your-Tag-Name-Here", value: "your-tag-value" }, + { name: "Another-Tag", value: "another-value" }, + ], +}); +``` + +## 在浏览器中创建一个进程 + +```js +import { createDataItemSigner, spawn } from "@permaweb/ao-sdk"; + +const processId = await spawn({ + // The Arweave TXID of the ao Module + module: "module TXID", + // The Arweave wallet address of a Scheduler Unit + scheduler: "TZ7o7SIZ06ZEJ14lXwVtng1EtSx60QkPy-kh-kdAXog", + // A signer function containing your wallet + signer: createDataItemSigner(globalThis.arweaveWallet), + /* + Refer to a Processes' source code or documentation + for tags that may effect its computation. + */ + tags: [ + { name: "Your-Tag-Name-Here", value: "your-tag-value" }, + { name: "Another-Tag", value: "another-value" }, + ], +}); +``` diff --git a/src/zh/guides/aos/aolink-compute-example.png b/src/zh/guides/aos/aolink-compute-example.png new file mode 100644 index 0000000..d2d0853 Binary files /dev/null and b/src/zh/guides/aos/aolink-compute-example.png differ diff --git a/src/zh/guides/aos/aolink-list-example.png b/src/zh/guides/aos/aolink-list-example.png new file mode 100644 index 0000000..fa2a5bc Binary files /dev/null and b/src/zh/guides/aos/aolink-list-example.png differ diff --git a/src/zh/guides/aos/aolink-message-details.png b/src/zh/guides/aos/aolink-message-details.png new file mode 100644 index 0000000..9fac313 Binary files /dev/null and b/src/zh/guides/aos/aolink-message-details.png differ diff --git a/src/zh/guides/aos/aolink-process-details.png b/src/zh/guides/aos/aolink-process-details.png new file mode 100644 index 0000000..86b0925 Binary files /dev/null and b/src/zh/guides/aos/aolink-process-details.png differ diff --git a/src/zh/guides/aos/aolink.png b/src/zh/guides/aos/aolink.png new file mode 100644 index 0000000..c5582c6 Binary files /dev/null and b/src/zh/guides/aos/aolink.png differ diff --git a/src/zh/guides/aos/blueprints/chatroom.md b/src/zh/guides/aos/blueprints/chatroom.md new file mode 100644 index 0000000..4d265c0 --- /dev/null +++ b/src/zh/guides/aos/blueprints/chatroom.md @@ -0,0 +1,48 @@ +# 聊天室蓝图 (Chatroom Blueprint) + +聊天室蓝图是预先设计好的模板,可帮助你在 ao 中快速构建聊天室。蓝图是入门的绝佳方式,并且可以根据你的需求进行定制。 + +## 解析聊天室蓝图 + +- **Members**: `Members`数组用于存储已注册聊天室的用户。 + +- **Register Handler**: `register` handler 用于进程加入聊天室。当进程发送带有标签 `Action = "Register"` 的消息时,handler会将进程添加到 `Members` 数组中,并向进程发送一条消息确认注册。 + +- **Broadcast Handler**: `broadcast` handler支持进程向聊天室的所有成员发送消息。当进程发送带有标签 Action = "Broadcast" 的消息时,handler会将该消息发送给聊天室的所有成员。 + +### 如何使用: + +1. 打开文本编辑器。 +2. 打开终端。 +3. 启动你的 `aos` 进程。 +4. 输入 `.load-blueprint chatroom` + +### 验证蓝图是否已加载: + +输入 `Handlers.list` 查看新加载的 Handlers。 + +## 聊天室蓝图中包含的内容: + +```lua +Members = Members or {} + +Handlers.add( + "register", + Handlers.utils.hasMatchingTag("Action", "Register"), + function (msg) + table.insert(Members, msg.From) + Handlers.utils.reply("registered")(msg) + end +) + +Handlers.add( + "broadcast", + Handlers.utils.hasMatchingTag("Action", "Broadcast"), + function (msg) + for _, recipient in ipairs(Members) do + ao.send({Target = recipient, Data = msg.Data}) + end + Handlers.utils.reply("Broadcasted.")(msg) + end +) +``` diff --git a/src/zh/guides/aos/blueprints/index.md b/src/zh/guides/aos/blueprints/index.md new file mode 100644 index 0000000..9f4b54a --- /dev/null +++ b/src/zh/guides/aos/blueprints/index.md @@ -0,0 +1,22 @@ +--- +prev: + text: "创建代币" + link: "../token" +next: + text: "Token 蓝图" + link: "./token" +--- + +# 蓝图 Blueprints + +蓝图是预先设计好的模板,可帮助你在 ao 中快速构建。蓝图是入门的绝佳方式,并且可以根据你的需求进行定制。 + +## 可用的蓝图 + +- [Token 蓝图](token) + +- [聊天室蓝图](chatroom) + +- [质押蓝图](staking) + +- [投票蓝图](voting) diff --git a/src/zh/guides/aos/blueprints/staking.md b/src/zh/guides/aos/blueprints/staking.md new file mode 100644 index 0000000..6258eb2 --- /dev/null +++ b/src/zh/guides/aos/blueprints/staking.md @@ -0,0 +1,96 @@ +# 质押蓝图 (Staking Blueprint) + +质押蓝图是预先设计好的模板,可帮助你在 ao 中快速构建质押系统。蓝图是入门的绝佳方式,并且可以根据你的需求进行定制。 + +## 前提条件 + +质押蓝图要求先加载 [token 蓝图](./token.md)。 + +## 解析 staking 蓝图 + +- **Stakers**: `Stakers` 数组用于存储质押者。 + +- **Unstaking**:`Unstaking` 数组用于存储参与者的解除质押请求。 + +- **Stake Action Handler**: `stake`handler 用于进程质押代币。当进程发送带有标签`Action = "Stake"`的消息时,处理程序会将质押代币添加到`Stakers` 数组中,并向进程发送一条消息确认质押。 + +- **Unstake Action Handler**: `unstake` handler 用于进程解除质押代币。当进程发送带有标签`Action = "Unstake"`的消息时,处理程序会将解除质押请求添加到 `Unstaking` 数组中,并向进程发送一条消息确认解除质押。 + +- **Finalization Handler**: `finalize` handler支持进程完成质押过程。当进程发送带有标签 `Action = "Finalize"`的消息时,处理程序会处理解除质押请求并完成质押过程。 + +### 如何使用: + +1. 打开文本编辑器。 + +2. 打开终端。 + +3. 启动`aos`流程。 + +4. 输入 `.load-blueprint staking` + +### 验证蓝图已加载: + +输入`Handlers.list`查看新加载的Handler。 + +## 质押蓝图中的内容: + +```lua +Stakers = Stakers or {} +Unstaking = Unstaking or {} + +-- Stake Action Handler +Handlers.stake = function(msg) + local quantity = tonumber(msg.Tags.Quantity) + local delay = tonumber(msg.Tags.UnstakeDelay) + local height = tonumber(msg['Block-Height']) + assert(Balances[msg.From] and Balances[msg.From] >= quantity, "Insufficient balance to stake") + Balances[msg.From] = Balances[msg.From] - quantity + Stakers[msg.From] = Stakers[msg.From] or {} + Stakers[msg.From].amount = (Stakers[msg.From].amount or 0) + quantity + Stakers[msg.From].unstake_at = height + delay +end + +-- Unstake Action Handler +Handlers.unstake = function(msg) + local quantity = tonumber(msg.Tags.Quantity) + local stakerInfo = Stakers[msg.From] + assert(stakerInfo and stakerInfo.amount >= quantity, "Insufficient staked amount") + stakerInfo.amount = stakerInfo.amount - quantity + Unstaking[msg.From] = { + amount = quantity, + release_at = stakerInfo.unstake_at + } +end + +-- Finalization Handler +local finalizationHandler = function(msg) + local currentHeight = tonumber(msg['Block-Height']) + -- Process unstaking + for address, unstakeInfo in pairs(Unstaking) do + if currentHeight >= unstakeInfo.release_at then + Balances[address] = (Balances[address] or 0) + unstakeInfo.amount + Unstaking[address] = nil + end + end + +end + +-- wrap function to continue handler flow +local function continue(fn) + return function (msg) + local result = fn(msg) + if (result) == -1 then + return 1 + end + return result + end +end + +-- Registering Handlers +Handlers.add("stake", + continue(Handlers.utils.hasMatchingTag("Action", "Stake")), Handlers.stake) +Handlers.add("unstake", + continue(Handlers.utils.hasMatchingTag("Action", "Unstake")), Handlers.unstake) +-- Finalization handler should be called for every message +Handlers.add("finalize", function (msg) return -1 end, finalizationHandler) +``` diff --git a/src/zh/guides/aos/blueprints/token.md b/src/zh/guides/aos/blueprints/token.md new file mode 100644 index 0000000..50edf98 --- /dev/null +++ b/src/zh/guides/aos/blueprints/token.md @@ -0,0 +1,203 @@ +# Token 蓝图 + +token 蓝图是预先设计好的模板,可帮助你在 ao 中快速创建一个 token。蓝图是入门的绝佳方式,并且可以根据你的需求进行定制。 + +## 解析 token 蓝图 + +- **Balances**: `Balances` 数组用于存储参与者的 token 余额。 + +- **Info Handler**: `info` handler 可以检索 token 参数,例如名称、代码、Logo 和面值。 + +- **Balance Handler**: `balance` handler 检索单个用户的 token 余额。 + +- **Balances Handler**: `balances` handler 检索所有用户的 token 余额。 + +- **Transfer Handler**: `transfer` handler 向其他用户发送 token 。 + +- **Mint Handler**: `mint` handler 铸造新的 token 。 + +### 如何使用: + +1. 打开你的文本编辑器。 +2. 打开终端。 +3. 启动你的`aos`进程. +4. 输入`.load-blueprint token` + +### 验证蓝图是否已加载: + +输入 `Handlers.list` 查看新加载的handlers。 + +## token 蓝图包含的内容: + +```lua +local bint = require('.bint')(256) +local ao = require('ao') +--[[ + This module implements the ao Standard Token Specification. + + Terms: + Sender: the wallet or Process that sent the Message + + It will first initialize the internal state, and then attach handlers, + according to the ao Standard Token Spec API: + + - Info(): return the token parameters, like Name, Ticker, Logo, and Denomination + + - Balance(Target?: string): return the token balance of the Target. If Target is not provided, the Sender + is assumed to be the Target + + - Balances(): return the token balance of all participants + + - Transfer(Target: string, Quantity: number): if the Sender has a sufficient balance, send the specified Quantity + to the Target. It will also issue a Credit-Notice to the Target and a Debit-Notice to the Sender + + - Mint(Quantity: number): if the Sender matches the Process Owner, then mint the desired Quantity of tokens, adding + them the Processes' balance +]] +-- +local json = require('json') + +--[[ + Initialize State + + ao.id is equal to the Process.Id + ]] +-- +if not Balances then Balances = { [ao.id] = tostring(bint(10000 * 1e12)) } end + +if Name ~= 'Points Coin' then Name = 'Points Coin' end + +if Ticker ~= 'Points' then Ticker = 'PNTS' end + +if Denomination ~= 12 then Denomination = 12 end + +if not Logo then Logo = 'SBCCXwwecBlDqRLUjb8dYABExTJXLieawf7m2aBJ-KY' end + +--[[ + Add handlers for each incoming Action defined by the ao Standard Token Specification + ]] +-- + +--[[ + Info + ]] +-- +Handlers.add('info', Handlers.utils.hasMatchingTag('Action', 'Info'), function(msg) + ao.send({ + Target = msg.From, + Name = Name, + Ticker = Ticker, + Logo = Logo, + Denomination = tostring(Denomination) + }) +end) + +--[[ + Balance + ]] +-- +Handlers.add('balance', Handlers.utils.hasMatchingTag('Action', 'Balance'), function(msg) + local bal = '0' + + -- If not Target is provided, then return the Senders balance + if (msg.Tags.Target and Balances[msg.Tags.Target]) then + bal = Balances[msg.Tags.Target] + elseif Balances[msg.From] then + bal = Balances[msg.From] + end + + ao.send({ + Target = msg.From, + Balance = bal, + Ticker = Ticker, + Account = msg.Tags.Target or msg.From, + Data = bal + }) +end) + +--[[ + Balances + ]] +-- +Handlers.add('balances', Handlers.utils.hasMatchingTag('Action', 'Balances'), + function(msg) ao.send({ Target = msg.From, Data = json.encode(Balances) }) end) + +--[[ + Transfer + ]] +-- +Handlers.add('transfer', Handlers.utils.hasMatchingTag('Action', 'Transfer'), function(msg) + assert(type(msg.Recipient) == 'string', 'Recipient is required!') + assert(type(msg.Quantity) == 'string', 'Quantity is required!') + assert(bint.__lt(0, bint(msg.Quantity)), 'Quantity must be greater than 0') + + if not Balances[msg.From] then Balances[msg.From] = "0" end + if not Balances[msg.Recipient] then Balances[msg.Recipient] = "0" end + + local qty = bint(msg.Quantity) + local balance = bint(Balances[msg.From]) + if bint.__le(qty, balance) then + Balances[msg.From] = tostring(bint.__sub(balance, qty)) + Balances[msg.Recipient] = tostring(bint.__add(Balances[msg.Recipient], qty)) + + --[[ + Only send the notifications to the Sender and Recipient + if the Cast tag is not set on the Transfer message + ]] + -- + if not msg.Cast then + -- Send Debit-Notice to the Sender + ao.send({ + Target = msg.From, + Action = 'Debit-Notice', + Recipient = msg.Recipient, + Quantity = tostring(qty), + Data = Colors.gray .. "You transferred " .. Colors.blue .. msg.Quantity .. Colors.gray .. " to " .. Colors.green .. msg.Recipient .. Colors.reset + }) + -- Send Credit-Notice to the Recipient + ao.send({ + Target = msg.Recipient, + Action = 'Credit-Notice', + Sender = msg.From, + Quantity = tostring(qty), + Data = Colors.gray .. "You received " .. Colors.blue .. msg.Quantity .. Colors.gray .. " from " .. Colors.green .. msg.Recipient .. Colors.reset + }) + end + else + ao.send({ + Target = msg.From, + Action = 'Transfer-Error', + ['Message-Id'] = msg.Id, + Error = 'Insufficient Balance!' + }) + end +end) + +--[[ + Mint + ]] +-- +Handlers.add('mint', Handlers.utils.hasMatchingTag('Action', 'Mint'), function (msg) + assert(type(msg.Quantity) == 'string', 'Quantity is required!') + assert(bint.__lt(0, msg.Quantity), 'Quantity must be greater than zero!') + + if not Balances[ao.id] then Balances[ao.id] = "0" end + + if msg.From == ao.id then + -- Add tokens to the token pool, according to Quantity + Balances[msg.From] = tostring(bint.__add(Balances[Owner], msg.Quantity)) + ao.send({ + Target = msg.From, + Data = Colors.gray .. "Successfully minted " .. Colors.blue .. msg.Quantity .. Colors.reset + }) + else + ao.send({ + Target = msg.From, + Action = 'Mint-Error', + ['Message-Id'] = msg.Id, + Error = 'Only the Process Owner can mint new ' .. Ticker .. ' tokens!' + }) + end +end) + +``` diff --git a/src/zh/guides/aos/blueprints/voting.md b/src/zh/guides/aos/blueprints/voting.md new file mode 100644 index 0000000..06b40d0 --- /dev/null +++ b/src/zh/guides/aos/blueprints/voting.md @@ -0,0 +1,77 @@ +# 投票蓝图 (Voting Blueprint) + +投票蓝图是预先设计好的模板,可帮助你在 ao 中快速构建投票系统。蓝图是入门的绝佳方式,并且可以根据你的需求进行定制。 + +## 前提条件 + +投票蓝图要求先加载 [Token 蓝图](./token.md) + +## 解析投票蓝图 + +- **Balances**: The `Balances` 数组用于存储参与者的代币余额。 + +- **Votes**: `Votes` 数组用于存储投票参与者地址。 + +- **Vote Action Handler**: `vote` handler 用户进程进行投票。当进程发送带有标记 `Action = "Vote"` 的消息时,handler 会将投票信息添加到`Votes`数组中,并向流程发送消息,确认投票。 + +- **Finalization Handler**: `finalize` handler支持进程完成投票过程。当进程发送带有标签 `Action = "Finalize"`的消息时,Handler会处理投票信息并完成投票过程。 + +### 如何使用: + +1. 打开你的文本编辑器。 +2. 打开终端。 +3. 启动你的`aos`进程. +4. 输入`.load-blueprint voting` + +### 验证蓝图是否已加载: + +输入 `Handlers.list` 查看新加载的 handlers。 + +## 投票蓝图包含的内容: + +```lua +Balances = Balances or {} +Votes = Votes or {} + +-- Vote Action Handler +Handlers.vote = function(msg) + local quantity = Stakers[msg.From].amount + local target = msg.Tags.Target + local side = msg.Tags.Side + local deadline = tonumber(msg['Block-Height']) + tonumber(msg.Tags.Deadline) + assert(quantity > 0, "No staked tokens to vote") + Votes[target] = Votes[target] or { yay = 0, nay = 0, deadline = deadline } + Votes[target][side] = Votes[target][side] + quantity +end + +-- Finalization Handler +local finalizationHandler = function(msg) + local currentHeight = tonumber(msg['Block-Height']) + -- Process voting + for target, voteInfo in pairs(Votes) do + if currentHeight >= voteInfo.deadline then + if voteInfo.yay > voteInfo.nay then + print("Handle Vote") + end + -- Clear the vote record after processing + Votes[target] = nil + end + end +end + +-- wrap function to continue handler flow +local function continue(fn) + return function (msg) + local result = fn(msg) + if (result) == -1 then + return 1 + end + return result + end +end + +Handlers.add("vote", + continue(Handlers.utils.hasMatchingTag("Action", "Vote")), Handlers.vote) +-- Finalization handler should be called for every message +Handlers.add("finalize", function (msg) return -1 end, finalizationHandler) +``` diff --git a/src/zh/guides/aos/cli.md b/src/zh/guides/aos/cli.md new file mode 100644 index 0000000..2158244 --- /dev/null +++ b/src/zh/guides/aos/cli.md @@ -0,0 +1,64 @@ +# CLI + +这里有一些命令参数来与 aos 进行交互: + +- [name] - 使用你的钱包地址创建或者加载一个进程 +- --load [file] - 加载一个文件,可以多次调用 +- --cron [interval] - 只在创建进程的时候使用 +- --wallet [walletfile] - 使用一个指定的钱包 + +## 使用 aos 管理多进程 + +```sh +aos +``` + +使用默认`default`名称创建或者连接到进程 + +```sh +aos chatroom +``` + +使用 `chatroom` 创建或者连接进程 + +```sh +aos treasureRoom +``` + +使用 `treasureRoom` 创建或者连接进程 + +## load 标志 + +```sh +aos treasureRoom --load greeting.lua --load treasure.lua --load puzzle.lua +``` + +可以使用 `load` 标志来加载一个或者多个代码文件到我的进程 + +## CRON 标志 + +如果你想让进程创建定时响应函数,需要在创建进程是调用 `cron` 标志 + +```sh +aos chatroom --cron 2-minutes +``` + +## Tag 标志 + +使用 tag 标志,你可以为进程创建用户标识。(例如将它们用作静态环境变量): + +```sh +aos chatroom --tag-name Chat-Theme --tag-value Dark --tag-name Chat-Name --tag-value Mychat +``` + +上面的命令会在你创建进程的时候添加用户定义标识 + +```ts +// process data item tags +[ + ... + { name: "Chat-Theme", value: "Dark" }, + { name: "Chat-Name", value: "Mychat" } + ... +] +``` diff --git a/src/zh/guides/aos/editor.md b/src/zh/guides/aos/editor.md new file mode 100644 index 0000000..caf0fe4 --- /dev/null +++ b/src/zh/guides/aos/editor.md @@ -0,0 +1,24 @@ +# 编辑器初始化 + +记住所有的内建函数和工具集方法是非常困难的。为了改善你的开发体验,我们推荐将 [Lua Language Server](https://luals.github.io) 扩展添加到您的编辑器,并添加 [ao addon](https://github.com/martonlederer/ao-definitions) 插件。它支持所有的 aos [内置模块](../aos/modules/index)和[全局变量](../aos/intro#globals)。 + +## VS Code + +安装 [sumneko.lua](https://marketplace.visualstudio.com/items?itemName=sumneko.lua) 扩展: + +1. 在扩展市场中搜索 sumneko 的“Lua”插件 +2. 下载并且安装插件 +3. 使用 `Shift + Command + P` (Mac) / `Ctrl + Shift + P` (Windows/Linux) 打开 VS Code 命令面板,然后运行以下命令: + +``` +> Lua: Open Addon Manager +``` + +4. 在插件管理器中搜索“ao”,选中第一个结果,单击“启用”完成自动安装。 + +## 其他编辑器 + + +1. 验证你的编辑器是否支持[语言服务器协议](https://microsoft.github.io/language-server-protocol/implementors/tools/) +2. 安装[luals.github.io](https://luals.github.io/#install)中的 Lua 语言服务 +3. 安装 "ao" 语言扩展服务 diff --git a/src/zh/guides/aos/faq.md b/src/zh/guides/aos/faq.md new file mode 100644 index 0000000..bab63e0 --- /dev/null +++ b/src/zh/guides/aos/faq.md @@ -0,0 +1,32 @@ +# FAQ + +## 所有权 (Ownership) + +
+ 进程的所有权 + +使用 aos 控制台创建一个新的进程,你的钱包地址就是进程的所有者。**aos** 使用 **Owner** 全局变量来标记进程的所有者。如果你想转移进程的所有权,或者将它变成无人可以控制的进程,你可以重定义 **Owner** 变量将它给其他钱包地址,或者设置为 **nil**。 + +
+ +## JSON + +
+ 将数据编码为 json 格式 + +当你向其他进程或者外部服务发送数据,你可能希望使用 JSON 作为数据编码格式。使用 lua 的 json 模块,可以对 lua Table 中的值进行 **encode** 和 **decode** 操作。 + +```lua +Send({Target = Router, Data = require('json').encode({hello = "world"})}) +``` + +
+ +## Send 和 ao.send 对比 + +
+ 什么时候使用 Send 或 ao.send + +这两个方法都会将消息发送到一个进程,区别是 `ao.send` 可以返回消息,以便于记录日志或者进行故障排查。`Send` 哈数通常在控制台中使用,更方便访问。在 `handlers` 中更推荐使用 `ao.send`,但他们在 `aos`中是可以相互替换的。 + +
diff --git a/src/zh/guides/aos/inbox-and-handlers.md b/src/zh/guides/aos/inbox-and-handlers.md new file mode 100644 index 0000000..cd9877d --- /dev/null +++ b/src/zh/guides/aos/inbox-and-handlers.md @@ -0,0 +1,49 @@ +# 理解收件箱(Inbox) + +在 aos 中,进程通过 handlers 获取消息并且执行。未处理的消息将进入进程的收件箱(Inbox) + +## 什么是 Handlers? + +Handler 是一个函数,接收并且处理进程消息。它处理这些消息是通过将消息作为参数来进行的。 + +```lua +function main(Message, ao) + ...dostuff + return { + Output = ..., + Messages = {}, + Spawns = {} + } + +end +``` + +`main` 函数返回一个 lua Table,包含 `Output, Message, Spawns` 或者 `Error`。在 aos 中你可以给进程添加 `Handler` 方法来处理逻辑,`Handler` 中有三个参数: + +1. Handler 名字 +2. 匹配函数 +3. 处理函数 + +```lua +Handlers.add("name", + function (Msg) + -- Does this message match (return true or false) + return Msg.Action == "Register" + end, + function (Msg) + print("Registered User.") + table.insert(Members, Msg.From) + ao.send({Target = Msg.From, Data = "Registered."}) + end +) +``` + +## 关于收件箱 + +收件箱会存储尚未处理的消息,它们在这里等待被处理。一旦消息被处理,它就会被从收件箱中移除。 + +> 示例:将收件箱视为语音邮件。 正如未接听的电话会被转至语音信箱,让你稍后处理一样,你的进程不立即处理的消息都会进入收件箱。未处理的消息将被存储在这里,直到你处理他们。 + +## 总结 + +一开始似乎所有邮件都会进入你的收件箱,如果它们在处理后就被删除,这可能会令人疑惑。 语音邮件的类比应该可以解释这种设计:就像你接听的电话不会转到语音邮件一样,你处理过的消息也不会出现在收件箱中。 这说明了收件箱和 Handler 的功能。 diff --git a/src/zh/guides/aos/index.md b/src/zh/guides/aos/index.md new file mode 100644 index 0000000..c872fdc --- /dev/null +++ b/src/zh/guides/aos/index.md @@ -0,0 +1,41 @@ +--- +prev: + text: "指南" + link: "../" +next: + text: "概览" + link: "zh/guides/aos/intro" +--- + +# aos + +`ao` 是一台分布式计算的并行计算机,`aos` 是该计算机上的操作系统。 + +使用 `aos` 你可以与进程(processes)交互,并且非常简单地编写进程代码。 你所需要的只是一个终端和一个编辑器。 + +`aos` 选择 [lua](../../concepts/lua.md) 作为开发语言,这是一种健壮的动态语言,使用起来非常有趣。 + +如果你还没有这么做,请花 15 分钟浏览我们的[教程](../../tutorials/index)。 + +## 深入了解 `aos` + +- [aos 概览](./intro) +- [安装](./installing) +- [`aos` CLI工具](./cli) +- [自定义提示符(prompt)](./prompt) +- [构建一个 Ping-Pong 服务](./pingpong) + +## 开发者指南 + +- [编辑器初始化](editor) +- [使用 ao.link 进行故障排除](troubleshooting) +- [理解收件箱(Inbox)](inbox-and-handlers) +- [常见问题](faq) + +### [**模块**](modules/index) + +- [JSON](modules/json) +- [`ao`](modules/ao) +- [Base64](modules/base64) +- [Pretty](modules/pretty) +- [Utils](modules/utils) diff --git a/src/zh/guides/aos/installing.md b/src/zh/guides/aos/installing.md new file mode 100644 index 0000000..c7d6f1e --- /dev/null +++ b/src/zh/guides/aos/installing.md @@ -0,0 +1,11 @@ +# 安装 aos + +安装 aos 只需要 `NodeJS` - https://nodejs.org + +> 注意: 如果你使用的是 Windows 系统,最后使用 WSL 命令行. + +```sh +npm i -g https://get_ao.g8way.io +``` + +安装完之后你可以输入 `aos` 来运行系统 diff --git a/src/zh/guides/aos/intro.md b/src/zh/guides/aos/intro.md new file mode 100644 index 0000000..5af7385 --- /dev/null +++ b/src/zh/guides/aos/intro.md @@ -0,0 +1,74 @@ +# 概览 + +aos 使用了与众不同的方式构建进程与合约,ao 计算机是一个去中心的计算机网络,可以在任何地方进行计算,aos 是一个统一的交互终端。 你可以使用 aos 作为你的个人操作系统、构建 ao 进程的开发环境、以及创建您的机器人军团。 + +让我们来回顾一些基本指令。 + +## 变量 + +如果你想通过控制台显示变量的内容,可以直接输入变量的名称 + +```lua +Name +``` + +## 收件箱 (Inbox) + +`收件箱 (Inbox)` 是你进程收到的消息集合。 + +```lua +Inbox[1] +``` + +如果您想查看消息个数,在 `Inbox` 前面加入 `#` 符号 + +```lua +#Inbox +``` + +查看收件箱的消息个数是个非常常见的功能,为了更简单,您可以创建一个函数,返回收件箱的消息个数并显示出来。 + +使用 `.editor` 或者 `.load file` 来将这个函数加载到您的进程。 + +```lua +function Prompt() + return "Inbox: " .. #Inbox .. " > " +end +``` + +**预期的结果:** + +```sh +undefined +Inbox: 2 > +``` + +您的 Prompt 函数已经可以显示收件箱中的消息数量。 + +## 全局定义 + +aos 中定义了一些全局变量、函数可以让开发变得更加直观。 + +| 名称 | 描述 | 类型 | +| ---------------------- | ---------------------------------------------------------------------------- | ------------ | +| Inbox | 这是一个 lua 表,存储所有接收到的消息 | Table(Array) | +| Send(Message) | 这是交互环境中使用的全局函数,可以向其他进程发送消息 | function | +| Spawn(Module, Message) | 这是 aos 中的全局函数,可以创建一个进程 | +| Name | 初始化您的进程名称 | string | +| Owner | 这个字符串标识了进程的所有者,如果修改这个字段,可能会影响您与进程交互的能力 | string | +| Handlers | 一个 lua 表,可以让您创建消息的处理函数 | table | +| Dump | 接受 lua 中的表(Table)类型,并打印用户友好的数据内容 | function | +| Utils | 一个功能实用库,具有map、reduce、filter等功能 | module | +| ao | 这是用于发送消息和生成进程的核心函数库 | module | + +## 模块 + +在aos中,有一些内置的 lua 模块可以使用,这些模块可以通过“require”函数引用。 + +| 名称 | 描述 | +| ------- | ------------------------------------------ | +| json | json 数据的编码和解码 | +| ao | ao 核心函数库,包括 send 、 spawn | +| .base64 | base64 数据的编解码 | +| .pretty | 格式化输出数据,使用 tprint 函数格式化数据 | +| .utils | 一个实用工具库 | diff --git a/src/zh/guides/aos/load.md b/src/zh/guides/aos/load.md new file mode 100644 index 0000000..be41c26 --- /dev/null +++ b/src/zh/guides/aos/load.md @@ -0,0 +1,25 @@ +# .load 命令 + +这个功能可以让你加载本地环境中的 lua 代码,给 aos 的开发者带来更好的开发体验。 + +在创建处理程序时你可能有大量代码,希望使用 vscode 等集成开发环境来管理它们。 你还可以安装 lua 扩展插件来做一些语法检查。 + +那么如何将本地的 lua 代码发布到你的 ao 进程中呢? 这就是“.load”命令发挥作用的地方。 + +hello.lua + +```lua +Handlers.add( + "ping", + Handlers.utils.hasMatchingData("ping"), + Handlers.utils.reply("pong") +) +``` + +aos 脚本输入 + +```sh +.load hello.lua +``` + +十分简单! 🐶 diff --git a/src/zh/guides/aos/modules/ao.md b/src/zh/guides/aos/modules/ao.md new file mode 100644 index 0000000..4a8ad0e --- /dev/null +++ b/src/zh/guides/aos/modules/ao.md @@ -0,0 +1,143 @@ +# ao + +内置全局库,用于发送消息、创建进程等。 + +### 示例 + +全局对象 `ao` 在你的进程中的任何地方都可以访问: + +```lua +-- sends a message to another process ("Transfer" action) +ao.send({ + Target = "usjm4PCxUd5mtaon7zc97-dt-3qf67yPyqgzLnLqk5A", + Action = "Transfer", + Recipient = "XjvCPN31XCLPkBo9bUeB7vAK0VC6-eY52-CS-6Iho8F", + Quantity = tostring(1045) +}) +``` + +## 模块变量 + +- `ao.id`: `{string}` 保存你进程中的 Arweave ID +- `ao.authorities`: `{table}` 授权的调用者数组 +- `ao._module`: `{string}` 进程的 WASM 基础模块,每次调用都会执行。 +- `ao._ref`: `{number}` 消息计数器,记录发出消息的总数。 +- `ao._version`: `{string}` ao global 库版本。 +- `ao.env`: `{table}` 进程初始化环境信息。 + +### `ao.env` + +全局变量`ao.env`保存了有关进程初始化消息的信息。它遵循以下结构: + +```json +{ + "type": "object", + "properties": { + "Process": { + "type": "object", + "properties": { + "Id": { + "type": "string", + "example": "A1b2C3d4E5f6G7h8I9j0K1L2M3N4O5P6Q7R8S9T0" + }, + "Owner": { + "type": "string", + "example": "Xy9PqW3vR5sT8uB1nM6dK0gF2hL4jC7iE9rV3wX5" + }, + "TagArray": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "example": "App-Name" + }, + "value": { + "type": "string", + "example": "aos" + } + } + }, + "example": [{ "name": "App-Name", "value": "aos" }] + }, + "Tags": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "patternProperties": { + "": { + "type": "string" + } + }, + "example": { + "App-Name": "aos" + } + } + } + } + } +} +``` + +## 模块函数 + +### `log()` + +log 将值或消息添加到 `Results.Output` 表中,后面可以使用 [`aoconnect`](/guides/aoconnect/aoconnect.html) 库进行读取,对于调试很有帮助。 + +- **Parameters:** + - `txt`: `{any}` log 的值/消息内容 +- **Returns:** `{void}` + +#### Examples + +```lua +... +ao.log("Breakpoint reached") +... +ao.log({ + Message = "Transferred " .. quantity .. " tokens to " .. target, + Quantity = quantity, + Recipient = target +}) +``` + +### `send()` + +向另一个进程发送消息。将消息放入进程的 outbox,并根据 AO 标准(ao specs compliant ) 标注消息的 tag + +- **Parameters:** + - `msg`: `{table}` 待发送的消息 +- **Returns:** 已发送的消息,包含已应用的标签和`DataItem`字段。 + +> **Note:** `msg` 表的每个字段都会作为 `DataItem` 标签,但以下字段除外:`"Target"`, `"Data"`, `"Anchor"`, `"Tags"`,因为这些字段将直接作为根级别的`DataItem`字段使用。 + +#### Example + +```lua +-- sends a message to "XjvCPN31XCLPkBo9bUeB7vAK0VC6-eY52-CS-6Iho8F" +-- with the tag { "name": "Action", "value": "Ping" } +ao.send({ + Target = "XjvCPN31XCLPkBo9bUeB7vAK0VC6-eY52-CS-6Iho8F", + Action = "Ping" +}) +``` + +### `spawn()` + +创建新进程。 + +- **Parameters:** + - `module`: `{string}` 新进程使用的模块的 Arweave 交易 ID + - `msg`: `{table}` 初始化进程的消息,格式在上面`send`部分中 +- **Returns:** 初始化的消息 + +#### Example + +```lua +ao.spawn("n0BFH80b73mi9VAWUzyuG9gEC3LI2zU2BFxum0N8A9s", { + ["Custom-Tag"]: "Custom-Value" +}) +``` diff --git a/src/zh/guides/aos/modules/base64.md b/src/zh/guides/aos/modules/base64.md new file mode 100644 index 0000000..5aa727e --- /dev/null +++ b/src/zh/guides/aos/modules/base64.md @@ -0,0 +1,112 @@ +# Base64 编码模块 + +一个小型 `base64` 模块,可以用于编码和解码 Base64 格式的文本。 + +> **注意:** 为了优化处理大段文本的性能,建议开启缓存功能,最高可以将效率提升一倍。 + +### 使用示例 +```lua +local base64 = require(".base64") + +local str = "This will be encoded" + +-- is: "VGhpcyB3aWxsIGJlIGVuY29kZWQ=" +local encoded = base64.encode(str) + +-- is: "This will be encoded" +local decoded = base64.decode(encoded) + +assert(decoded == str) +``` + +## 模块函数 + +### `encode()` + +此函数使用默认编码器表对提供的字符串进行编码。 编码器可以自定义,并且可以为较大的数据块提供缓存功能。 + +- **Parameters:** + - `str`: `{string}` 需要编码的字符串 + - `encoder`: `{table}` 自定义编码表(可选) + - `usecache`: `{boolean}` 可选的针对大字符串使用的缓存 (默认关闭) +- **Returns:** Base64 编码后的字符串 + +#### 示例 +```lua +-- prints: "SGVsbG8gd29ybGQ=" +print(base64.encode("Hello world")) + +-- customize encoder and allow caching +base64.encode( + "Hello world", + base64.makeencoder(nil, "-"), + true +) +``` + +### `decode()` + +此函数使用默认解码器表对Base64编码的字符串进行解码。解码器可以自定义,并提供缓存功能。 + +- **Parameters:** + - `str`: `{string}` 待解码的 Base64 编码字符串 + - `decoder`: `{table}` 可选的自定义解码表 + - `usecache`: `{boolean}` 可选的针对大字符串使用的缓存(默认关闭) +- **Returns:** 解码后的字符串 + +#### 示例 +```lua +-- prints: "Hello world" +print(base64.decode("SGVsbG8gd29ybGQ=")) + +-- customize decoder and allow caching +base64.decode( + "SGVsbG8gd29ybGQ=", + base64.makedecoder(nil, "-"), + true +) +``` + +### `makeencoder()` + +此函数支持创建一个自定义编码表,以定制 [`encode()`](#encode)函数的输出结果。 + +- **参数:** + - `s62`: `{string}` 可选的自定义字符,用于替换标准的字符 62(默认为 "+") + - `s63`: `{string}` 可选的自定义字符,用于替换标准的字符 63(默认为 "/") + - `spad`: `{string}` 可选的自定义填充字符,用于替换标准的填充字符 "=" +- **Returns:** 自定义的编码表 + +#### 示例 + +```lua +-- create custom encoder +local encoder = base64.makeencoder(nil, nil, "~") + +-- prints "SGVsbG8gd29ybGQ~" instead of "SGVsbG8gd29ybGQ=" +print(base64.encode("Hello world", encoder)) +``` + +### `makedecoder()` + + +创建一个自定义解码表,以便解码[自定义编码](#makeencoder) 的 base64 字符串。 + +- **Parameters:** + - `s62`: `{string}` 可选的自定义字符,用于替换标准的字符 62(默认为 "+") + - `s63`: `{string}` 可选的自定义字符,用于替换标准的字符 63(默认为 "/") + - `spad`: `{string}` 可选的自定义字符,用于替换标准的填充字符 "=" +- **Returns:** 自定义解码器表 + +#### 示例 + +```lua +local encoder = base64.makeencoder(nil, nil, "~") +local decoder = base64.makedecoder(nil, nil, "~") + +-- "SGVsbG8gd29ybGQ~" +local encoded = base64.encode("Hello world", encoder) + +-- prints "Hello world" +print(base64.decode(encoded, decoder)) +``` diff --git a/src/zh/guides/aos/modules/index.md b/src/zh/guides/aos/modules/index.md new file mode 100644 index 0000000..9674b0f --- /dev/null +++ b/src/zh/guides/aos/modules/index.md @@ -0,0 +1,18 @@ +--- +prev: + text: "质押蓝图" + link: "../blueprints/staking" +next: + text: "JSON" + link: "./json" +--- + +# 模块 + +aos 内置模块文档。 + +- [JSON](./json) +- [ao](./ao) +- [Base64](./base64) +- [Pretty](./pretty) +- [Utils](./utils) diff --git a/src/zh/guides/aos/modules/json.md b/src/zh/guides/aos/modules/json.md new file mode 100644 index 0000000..baeabe0 --- /dev/null +++ b/src/zh/guides/aos/modules/json.md @@ -0,0 +1,61 @@ +# JSON + +JSON 模块支持你使用 JavaScript 对象表示法对对象进行编码和解码。 + +### 示例用法 + +```lua +local json = require("json") + +json.encode({ + a_string = "This is a string", + nums = { 1, 2, 3 } +}) +``` + +## 模块函数 + +### `encode()` + +将 Lua 对象转换为 JSON 字符串。 + +- **参数:** + - `val`: `{any}` 需要格式化的对象 +- **返回值:** 对象的 JSON 格式字符串 + +#### 示例 + +```lua +--[[ + prints: + "[{"name":"John Doe","age":23},{"name":"Bruce Wayne",age:34}]" +]]-- +print(json.encode({ + { name = "John Doe", age = 23 }, + { name = "Bruce Wayne", age = 34 } +})) + +-- prints "false" +print(json.encode(false)) +``` + +### `decode()` + +此函数用于解析 JSON 字符串并转换为 Lua 对象。 + +- **参数:** + - `val`: `{any}` 待解码的 JSON 字符串 +- **返回值:** JSON 字符串对应的 Lua 对象(对于无效的 JSON 字符串会抛出错误) + +#### 示例 + +```lua +--[[ + creates the following table: + { hello = "world" } +]]-- +json.decode('{ "hello": "world" }') + +-- creates a boolean with true value +json.decode("true") +``` diff --git a/src/zh/guides/aos/modules/pretty.md b/src/zh/guides/aos/modules/pretty.md new file mode 100644 index 0000000..c84829c --- /dev/null +++ b/src/zh/guides/aos/modules/pretty.md @@ -0,0 +1,26 @@ +# Pretty 模块 + +这个模块的作用是将输出格式化、易于理解和阅读 + +## 模块函数 +### `tprint()` +将 table 结构转换为格式化的字符串 + +- **Parameters:** + - `tbl`: `{table}` 需要格式化 table 结构 + - `indent`: `{number}` 可选参数,表格每一层的缩进量 +- **Returns:** 转为字符串格式的表格结构 + +#### 示例 +```lua +local pretty = require(".pretty") + +local formatted = pretty.tprint({ + name = "John Doe", + age = 22, + friends = { "Maria", "Victor" } +}, 2) + +-- prints the formatted table structure +print(formatted) +``` diff --git a/src/zh/guides/aos/modules/utils.md b/src/zh/guides/aos/modules/utils.md new file mode 100644 index 0000000..28410e9 --- /dev/null +++ b/src/zh/guides/aos/modules/utils.md @@ -0,0 +1,288 @@ +# Utils 工具库 + +该工具库提供了通用的表格操作和验证功能。它同时支持链式调用 (curry-style) 和传统编程方式。 + +> **注意**: 务必确保提供给以下函数的输入与期望的类型相匹配。 + +### 使用示例 + +```lua +local utils = require(".utils") + +local totalSupply = utils.reduce( + function (acc, v) return acc + v end, + 0, + { 2, 4, 9 } +) + +print(totalSupply) -- prints 15 +``` + +## 模块函数 + +### `concat()` + +此函数将数组 `b` 连接到数组 `a`。 + +- **Parameters:** + - `a`: `{table}` 基础数组 + - `b`: `{table}` 要连接到基础数组的数组 +- **Returns:** 由 `a` 和 `b` 合并而成的统一数组 + +#### 示例 + +```lua +-- returns { 1, 2, 3, 4, 5, 6 } +concat({ 1, 2, 3 })({ 4, 5, 6 }) + +-- returns { "hello", "world", "and", "you" } +concat({ "hello", "world" }, { "and", "you" }) +``` + +### `reduce()` + +此函数对数组中的所有元素执行 reducer 函数,最终返回一个 (统一的) 结果。 + +- **Parameters:** + - `fn`: `{function}` reducer 函数,它按顺序接收之前的结果、当前元素的值和键。 + - `initial`: `{any}` (可选) 初始值 + - `t`: `{table}` 要处理的数组 +- **Returns:** 通过对所有表格元素运行 reducer 函数所得的单个结果 + +#### 示例 + +```lua +local sum = utils.reduce( + function (acc, v) return acc + v end, + 0, + { 1, 2, 3 } +) + +print(sum) -- prints 6 +``` + +```lua +local sum = utils + .reduce(function (acc, v) return acc + v end)(0)({ 5, 4, 3 }) + +print(sum) -- prints 12 +``` + +### `map()` + +此函数创建一个新数组,其中包含了对提供的数组中的每个元素调用指定映射函数的结果。 + +- **Parameters:** + - `fn`: `{function}` map 函数,接收当前数组元素和键。 + - `data`: `{table}` 要映射的数组 +- **Returns:** 由映射函数处理后的结果组成的的新数组 + +#### 示例 + +```lua +-- returns { "Odd", "Even", "Odd" } +utils.map( + function (val, key) + return (val % 2 == 0 and "Even") or "Odd" + end, + { 3, 4, 7 } +) +``` + +```lua +-- returns { 4, 8, 12 } +utils.map(function (val, key) return val * 2 end)({ 2, 4, 6 }) +``` + +### `filter()` + +此函数根据提供的函数来处理原数组,并创建一个只包含通过过滤条件的元素的新数组。 + +- **Parameters:** + - `fn`: `{function}` 过滤函数。它接收当前数组中的元素作为参数,并应该返回一个布尔值,以决定该元素应被保留 (`true`) 还是过滤掉(`false`)。 + - `data`: `{table}` 要过滤的数组 +- **Returns:** 经过过滤的新数组 + +#### 示例 + +```lua +-- keeps even numbers +utils.filter( + function (val) return val % 2 == 0 end, + { 3, 4, 7 } +) +``` + +```lua +-- keeps only numbers +utils.filter( + function (val) return type(val) == "number" end, + { "hello", "world", 13, 44 } +) +``` + +### `find()` + +该函数查找匹配指定条件的第一个元素并返回。 + +- **Parameters:** + - `fn`: `{function}` 查找函数。它接收当前数组元素作为参数,如果该元素满足条件则返回`true`,否则返回 `false`。 + - `t`: `{table}` 要查找元素的数组 +- **Returns:** 找到的符合条件的第一个元素,如果没有找到则返回 `nil` (表示空值)。 + +#### Examples + +```lua +local users = { + { name = "John", age = 50 }, + { name = "Victor", age = 37 }, + { name = "Maria", age = 33 } +} + +-- returns the user "John" +utils.find( + function (val) return user.name == "John" end, + users +) +``` + +```lua +-- returns the user "Maria" +utils.find(function (val) return user.age == 33 end)(users) +``` + +### `reverse()` + +将数组转换为反序。 + +- **Parameters:** + - `data`: `{table}` 需要反序的数组 +- **Returns:** 反序的数组 + +#### 示例 + +```lua +-- is: { 3, 2, 1 } +utils.reverse({ 1, 2, 3 }) +``` + +### `includes()` + +判断值是否在数组中。 + +- **Parameters:** + - `val`: `{any}` 需要检查的元素 + - `t`: `{table}` 需要检查的数组 +- **Returns:** 一个布尔值,判断提供的 val 值是否属于该数组。true 表示存在,false 表示不存在。 + +#### 示例 + +```lua +-- this is true +utils.includes("John", { "Victor", "John", "Maria" }) +``` + +```lua +-- this is false +utils.includes(4)({ 3, 5, 7 }) +``` + +### `keys()` + +返回表格的键值。 + +- **Parameters:** + - `table`: `{table}` 要获取键值的表格 +- **Returns:** 键数组 + +#### 示例 + +```lua +-- returns { "hello", "name" } +utils.keys({ hello = "world", name = "John" }) +``` + +### `values()` + +返回表格的值。 + +- **Parameters:** + - `table`: `{table}` 需要获取值的表格 +- **Returns:** 值数组 + +#### 示例 + +```lua +-- returns { "world", "John" } +utils.values({ hello = "world", name = "John" }) +``` + +### `propEq()` + +该函数检查表格中指定属性的值是否等于提供的数值。 + +- **Parameters:** + - `propName`: `{string}` 要比较的属性名称 + - `value`: `{any}` 要比较的值 + - `object`: `{table}` 要从中选择属性的对象(表格) +- **Returns:** 一个布尔值,判断属性值是否等于提供的数值,如果属性值存在且等于提供的数值,则返回 True,否则返回 False。 + +#### 示例 + +```lua +local user = { name = "John", age = 50 } + +-- returns true +utils.propEq("age", 50, user) +``` + +```lua +local user = { name = "Maria", age = 33 } + +-- returns false +utils.propEq("age", 45, user) +``` + +### `prop()` + +该函数的作用是从对象(表格)中获取指定属性的值。 + +- **Parameters:** + - `propName`: `{string}` 要获取的属性名称 + - `object`: `{table}` 要从中选择属性值的对象 +- **Returns:** 属性值,如果未找到,则返回 `nil`。 + +#### 示例 + +```lua +local user = { name = "Maria", age = 33 } + +-- returns "Maria" +utils.prop("name", user) +``` + +```lua +local user = { name = "John", age = 50 } + +-- returns 50 +utils.prop("age")(user) +``` + +### `compose()` + +此函数支持你将多个数组操作链接在一起,然后以逆序的方式对提供的数组执行这些操作。 + +- **Parameters:** + - `...`: `{function[]}` 一组数组操作函数 + - `v`: `{table}` 要执行这些函数的数组 +- **Returns:** 来自所提供操作的最终结果 + +#### 示例 + +```lua +-- returns 12 +utils.compose( + utils.reduce(function (acc, val) return acc + val end, 0), + utils.map(function (val) return val * 2 end) +)({ 1, 2, 3 }) +``` diff --git a/src/zh/guides/aos/ownership.md b/src/zh/guides/aos/ownership.md new file mode 100644 index 0000000..e69de29 diff --git a/src/zh/guides/aos/pingpong.md b/src/zh/guides/aos/pingpong.md new file mode 100644 index 0000000..44559c6 --- /dev/null +++ b/src/zh/guides/aos/pingpong.md @@ -0,0 +1,70 @@ +# 创建一个 Pingpong 进程 + +这个教程将指导你在 aos 中创建一个简单的“ping-pong”过程。 在此过程中,每当进程收到带有数据“ping”的消息时,它都会自动回复“pong”。 这是 aos 中消息处理和进程间交互的基本示例。 + +## 步骤 1: 打开 `aos` CLI + +- 首先打开命令行界面并输入 `aos` 进入 aos 环境。 + +## 步骤 2: 打开内置编辑器 + +- 在 aos CLI 中输入“.editor”打开内置文本编辑器。 你将在此处编写 pingpong 处理程序代码。 + +## 步骤 3: 添加 Pingpong Handler + +- 在编辑器中,输入以下 Lua 代码来添加 pingpong 模式的处理程序: + + ```lua + Handlers.add( + "pingpong", + Handlers.utils.hasMatchingData("ping"), + Handlers.utils.reply("pong") + ) + ``` + +- 这个 lua 脚本做了三个操作: + 1. 添加一个新的名为"pingpong"的 handler。 + 2. 它使用 `Handlers.utils.hasMatchingData("ping")` 来检查收到的消息中是否有 "ping" 字段. + 3. 如果消息中包含 "ping", `Handlers.utils.reply("pong")` 会自动返回 "pong" 的消息. + +## 步骤 4: 退出编辑器 + +- 在编写完代码后,输入 `.done` 并输入 Enter 来退出编辑器并运行脚本。 + +## 步骤 5: 测试 Pingpong 进程 + +- 为了测试进程,需要向进程发送带有 `ping` 的消息。你可以在 CLI 中使用下面的命令: + ```lua + Send({ Target = ao.id, Data = "ping" }) + ``` +- 进程将在收件箱 `Inbox` 中收到带有 "pone" 的恢复消息。 + +## 步骤 6: 查看收件箱 Inbox + +- 查看你的收件箱中是否有 "ping" 的消息,并确认是否发出了 “pong” 的回复消息。 + +```lua +Inbox[#Inbox].Data +``` + +## 步骤 7: 测试和观察 + +- 发送不同的消息进行测试,并观察“ping”消息如何触发“pong”响应。 + +## 步骤 8: 保存进程 (可选) + +- 如果你想在将来使用此过程,请将处理程序代码保存在Lua文件中以便于加载 + +进入 aos 会话。 + +::: info + +**额外提示:** + +- **处理程序效率**: 简单的处理函数是关键. 确保它的执行效率并且只在正确的时候触发。 + +::: + +## 结论 + +恭喜! 您已经在 aos 中创建了一个基础的"ping-pong"进程。 本教程给理解 在aos 环境中的消息处理和进程交互奠定了基础。 随着您对这些概念越来越熟悉,您可以扩展到更复杂的流程和交互,探索 aos 的全部潜力。 diff --git a/src/zh/guides/aos/prompt.md b/src/zh/guides/aos/prompt.md new file mode 100644 index 0000000..2a32657 --- /dev/null +++ b/src/zh/guides/aos/prompt.md @@ -0,0 +1,46 @@ +# aos 中的自定义提示符(Prompt) + +## 步骤 1: 启动 aos 并打开编辑器 + +- 登录 aos 命令行 +- 输入 `.editor` 打开内置编辑器 + +## 步骤 2: 编写自定义提示符函数 + +- 在编辑器中,定义你的用户提示符函数,例如: + ```lua + function Prompt() + return "YourName@aos> " + end + ``` + 将 `"YourName@aos> "` 定义为你喜欢的文本。 + +## 步骤 3: 退出并运行你的代码 + +- 输入 `.done` 并输入 Enter 来退出编辑器并执行代码 +- 你的 aos 将显示新的自定义提示符 + +## 步骤 4: 保存以供将来使用 (可选) + +- 如果你希望在将来的 aos 会话中使用这个提示符,请将脚本保存在 Lua 文件中。 +- 在后续会话中,加载此脚本以应用自定义提示。 + +## 更多提示符功能 + +自定义提示可以带来很多实用性和创造力。 你可以在提示符中执行以下操作: + +- 创建一个函数来跟踪收件箱中的未读消息数量: + + ```lua + --Example: + function Prompt() + return "YourName Inbox: [" .. #Inbox .. "] > " + end + ``` + +- 跟踪你聊天室进程中的成员数量。 +- 跟踪你进程地址持有的指定Token的数量。 + +## 结论 + +现在你已经了解了如何使用自定义提示符,现在你已经迈出了优化 ao 开发体验的关键一步。 diff --git a/src/zh/guides/aos/tables-and-json.md b/src/zh/guides/aos/tables-and-json.md new file mode 100644 index 0000000..e69de29 diff --git a/src/zh/guides/aos/token.md b/src/zh/guides/aos/token.md new file mode 100644 index 0000000..e900146 --- /dev/null +++ b/src/zh/guides/aos/token.md @@ -0,0 +1,309 @@ +# 在 ao 中创建 Token + +创建 token 时,我们将遵循[Token 规范](../../references/token.md)中概述的原则,并使用ao中的[Lua 语言](../../references/lua.md)铸造。 + +### 创建 Token 的两种方式: + +**1 - 使用 token 蓝图(token blueprint):** + +`.load-blueprint token` +使用蓝图将创建一个 token,其中所有 Handler 和状态都已经定义。这是创建 token 最简单的方法。加载蓝图后,你可以自定义这些 Handler 和状态。 + +你可以在此处了解更多有关蓝图的信息:[蓝图](../aos/blueprints/index.md) + +::: info +虽然使用 token 蓝图虽然可以快速创建,但你仍然需要了解如何进行[加载和测试](token.html#loading-and-testing), 以便根据需要进行自定义。 +::: + +**2 - 从零开始构建:** + +以下指南将指导你完成从零开始创建 token。这是创建 token 的一种更高级的方法,可以让你更好地理解 token 的工作原理。 + +## 准备工作 + +### **步骤 1:初始化 Token** + +- 打开你的文本编辑器,最好是在与之前教程中使用的文件夹相同的位置中打开。 +- 创建一个名为 `token.lua` 的新文件。 +- 在 `token.lua` 文件中,首先,初始化 token 的状态,定义其余额、名称、代码等: + +```lua +local json = require('json') + +if not Balances then Balances = { [ao.id] = 100000000000000 } end + +if Name ~= 'My Coin' then Name = 'My Coin' end + +if Ticker ~= 'COIN' then Ticker = 'COIN' end + +if Denomination ~= 10 then Denomination = 10 end + +if not Logo then Logo = 'optional arweave TXID of logo image' end +``` + +![token.lua image 1](./token1.png) + +让我们梳理一下我们所做的工作: + +- `local json = require('json')`: 这行代码首先导入了一个模块供以后使用。 + +- `if not Balances then Balances = { [ao.id] = 100000000000000 } end`: 第2行代码正在初始化一个叫做 Balances 表格,此表格用于记录谁拥有 Token 和持有的数量。设置 ao.id 账户为初始的 token 持有者,并拥有所有 token 的初始余额。 + +- 接下来的 4 行,除了 `if Denomination`外,`if Name`、`if Ticker`、`if Denomination` 和 `if not Logo` 都是可选项,它们分别用于定义 token 的名称、代码、最小单位和LOGO。 + +::: info + +`if Denomination ~= 10 then Denomination = 10 end` 表示我们应该将多少个 token 视为一个单位。 +::: + +### **步骤 2:信息和余额的 Handlers** + +#### 消息的 Handlers + +现在让我们添加第一个 Handler 来处理收到的消息。 + +```lua +Handlers.add('info', Handlers.utils.hasMatchingTag('Action', 'Info'), function(msg) + ao.send( + { Target = msg.From, Tags = { Name = Name, Ticker = Ticker, Logo = Logo, Denomination = tostring(Denomination) } }) +end) +``` + +![Token.lua image 2](./token2.png) + +::: info + +此时,你可能已经注意到,我们正在 `token.lua` 文件中构建所有 Handler,而不是使用 `.editor`。 + +对于许多 handlers 和进程来说,使用 `.editor` 创建 handler 没有任何问题,但由于我们正在创建一个完整的进程来初始化 token 、设置信息、处理余额、转移,和铸造 Handler,因此最好将所有内容都保存在一个文件中。 + +这么做是为了让我们保持一致性,因为每次我们将 `token.lua` 文件重新加载到 `aos` 中时,每个 handler 都会更新。 +::: + +这段代码的意思是,如果有人发送一条带有标签 Action = "info" 的消息,那么我们的 token 将返回一条消息,其中包含所有之前定义的信息。注意的是, Target = msg.From,这是在告诉 ao 我们要回复向我们发送此消息的进程。 + +#### 信息(Info)和 token 余额(Balance)的Handlers + +现在我们可以添加两个用于提供 token 余额信息的 Handler。 + +```lua +Handlers.add('balance', Handlers.utils.hasMatchingTag('Action', 'Balance'), function(msg) + local bal = '0' + + -- If not Target is provided, then return the Senders balance + if (msg.Tags.Target and Balances[msg.Tags.Target]) then + bal = tostring(Balances[msg.Tags.Target]) + elseif Balances[msg.From] then + bal = tostring(Balances[msg.From]) + end + + ao.send({ + Target = msg.From, + Tags = { Target = msg.From, Balance = bal, Ticker = Ticker, Data = json.encode(tonumber(bal)) } + }) +end) + +Handlers.add('balances', Handlers.utils.hasMatchingTag('Action', 'Balances'), + function(msg) ao.send({ Target = msg.From, Data = json.encode(Balances) }) end) + +``` + +以上代码的第一个Handler `Handlers.add('balance)'` 处理来自某个进程或用户查询自身余额的请求,然后它会回复一条包含余额信息的消息。第二个Handler `Handlers.add('balances)'` 获取整个 Balances 表。 + +### **步骤 3:转账(Transfer) Handlers** + +在开始测试之前,我们将添加另外 2 个Handler,其中一个允许在进程或用户之间转移 token 。 + +```lua +Handlers.add('transfer', Handlers.utils.hasMatchingTag('Action', 'Transfer'), function(msg) + assert(type(msg.Tags.Recipient) == 'string', 'Recipient is required!') + assert(type(msg.Tags.Quantity) == 'string', 'Quantity is required!') + + if not Balances[msg.From] then Balances[msg.From] = 0 end + + if not Balances[msg.Tags.Recipient] then Balances[msg.Tags.Recipient] = 0 end + + local qty = tonumber(msg.Tags.Quantity) + assert(type(qty) == 'number', 'qty must be number') + + if Balances[msg.From] >= qty then + Balances[msg.From] = Balances[msg.From] - qty + Balances[msg.Tags.Recipient] = Balances[msg.Tags.Recipient] + qty + + --[[ + Only Send the notifications to the Sender and Recipient + if the Cast tag is not set on the Transfer message + ]] -- + if not msg.Tags.Cast then + -- Send Debit-Notice to the Sender + ao.send({ + Target = msg.From, + Tags = { Action = 'Debit-Notice', Recipient = msg.Tags.Recipient, Quantity = tostring(qty) } + }) + -- Send Credit-Notice to the Recipient + ao.send({ + Target = msg.Tags.Recipient, + Tags = { Action = 'Credit-Notice', Sender = msg.From, Quantity = tostring(qty) } + }) + end + else + ao.send({ + Target = msg.Tags.From, + Tags = { Action = 'Transfer-Error', ['Message-Id'] = msg.Id, Error = 'Insufficient Balance!' } + }) + end +end) +``` + +总之,这段代码会检查收件人标签 (Recipient Tag) 和数量标签 (Quantity Tag) 是否已提供。如果发送人和接收者的余额不存在,则初始化他们的余额。然后尝试将指定数量的 token 转移到接收者的余额账户。 + +```lua +Balances[msg.From] = Balances[msg.From] - qty +Balances[msg.Tags.Recipient] = Balances[msg.Tags.Recipient] + qty +``` + +如果转账成功,则向原始消息的发送者发送借记通知 (Debit-Notice),并向接收者发送贷记通知 (Credit-Notice)。 + +```lua +-- Send Debit-Notice to the Sender +ao.send({ + Target = msg.From, + Tags = { Action = 'Debit-Notice', Recipient = msg.Tags.Recipient, Quantity = tostring(qty) } +}) +-- Send Credit-Notice to the Recipient +ao.send({ + Target = msg.Tags.Recipient, + Tags = { Action = 'Credit-Notice', Sender = msg.From, Quantity = tostring(qty) } +}) +``` + +如果账户余额不足 (insufficient balance) ,则会发送交易失败的消息。 + +```lua +ao.send({ + Target = msg.Tags.From, + Tags = { Action = 'Transfer-Error', ['Message-Id'] = msg.Id, Error = 'Insufficient Balance!' } +}) +``` + +`if not msg.Tags.Cast then` 这行代码表示在 AO 协议中,如果设置了 Cast 标签就不会推送任何消息。 + +### **第 4 步:铸造(Mint) Handler** + +最后,我们将添加一个 Handler,用于铸造新的 Token。 + +```lua +Handlers.add('mint', Handlers.utils.hasMatchingTag('Action', 'Mint'), function(msg, env) + assert(type(msg.Tags.Quantity) == 'string', 'Quantity is required!') + + if msg.From == env.Process.Id then + -- Add tokens to the token pool, according to Quantity + local qty = tonumber(msg.Tags.Quantity) + Balances[env.Process.Id] = Balances[env.Process.Id] + qty + else + ao.send({ + Target = msg.Tags.From, + Tags = { + Action = 'Mint-Error', + ['Message-Id'] = msg.Id, + Error = 'Only the Process Owner can mint new ' .. Ticker .. ' tokens!' + } + }) + end +end) +``` + +这段代码会检查数量标签(Quantity Tag)是否已提供,然后将指定的数量添加到 Balances 表。 + +## 加载和测试 + +当你创建了 `token.lua` 文件,或者你使用了 `.load-blueprint token` 命令,那么你就可以开始测试了。 + +#### 1 - 启动 aos 进程 + +确保你已通过在终端中运行 aos 来启动 aos 进程。 + +#### 2 - 加载 token.lua 文件 + +如果按照操作指南进行操作,则在 aos 进程所在的目录中会有一个 `token.lua` 文件。从 aos 提示符处加载此文件。 + +```sh +.load token.lua +``` + +#### 3 - 测试 Token + +现在我们可以从同一个 aos 提示符向我们的 aos 进程 ID 发送消息,以查看是否正常工作。如果我们将 ao.id 用作目标,那么我们就是在向自己发送消息。 + +```sh +Send({ Target = ao.id, Action = "Info" }) +``` + +这里应该会打印合约中定义的信息。检查最新收件箱消息以获取回复。 + +```sh +Inbox[#Inbox].Tags +``` + +这里应该会打印合约中定义的信息。 + +::: info +为了确保你能准确查看最新消息,请先运行`#Inbox`查看收件箱中的消息总数。 +然后,运行最后一个消息编号以查看数据。 + +** Example: ** +如果 `#Inbox`返回 `5`,则运行 `Inbox[5].Data` 查看数据。 +::: + +#### 4 - 转账 + +现在,尝试将 token 余额转账到另一个钱包或进程 ID。 + +::: info +如果你需要另一个进程 ID,可以在另一个终端窗口运行`aos [name]` 获取新的进程 ID。确保它与你当前使用的`aos [name]` 不同。 + +**示例:** + +如果你在一个终端窗口中使用 aos,可以在另一个终端窗口中运行`aos test`获取新的进程 ID。 +::: + +```sh +Send({ Target = ao.id, Tags = { Action = "Transfer", Recipient = 'another wallet or processid', Quantity = '10000' }}) +``` + +发送之后,终端会打印一条消息,发送方会看到类似于`借记通知 (Debit-Notice)` 的内容,而接收方则会看到`贷记通知 (Credit-Notice)` 的内容。 + +#### 5 - 检查余额 + +现在你已经转了一些 token ,让我们检查一下余额。 + +```sh +Send({ Target = ao.id, Tags = { Action = "Balances" }}) +``` + +```sh +Inbox[#Inbox].Data +``` + +你会看到两个进程 ID 或钱包地址,每个地址都显示余额。第一个应该是你的发送进程 ID,第二个应该是接收者的进程 ID。 + +#### 6 - 铸造 token + +最后,尝试铸造一些 token 。 + +```sh +Send({ Target = ao.id, Tags = { Action = "Mint", Quantity = '1000' }}) +``` + +然后再次检查余额。 + +```sh +Send({ Target = ao.id, Tags = { Action = "Balances" }}) +Inbox[#Inbox].Data +``` + +然后你会看到铸造 token 的进程 ID 的余额增加了。 + +## 结论 + +“创建 token”指南到此结束。学习构建自定义 token 可以为你的项目释放巨大的潜力;无论你是想创建新货币、游戏 token 、治理 token ,还是任何其他你能想到的东西。 diff --git a/src/zh/guides/aos/token1.png b/src/zh/guides/aos/token1.png new file mode 100644 index 0000000..04748b9 Binary files /dev/null and b/src/zh/guides/aos/token1.png differ diff --git a/src/zh/guides/aos/token2.png b/src/zh/guides/aos/token2.png new file mode 100644 index 0000000..bf5f514 Binary files /dev/null and b/src/zh/guides/aos/token2.png differ diff --git a/src/zh/guides/aos/troubleshooting.md b/src/zh/guides/aos/troubleshooting.md new file mode 100644 index 0000000..840cd43 --- /dev/null +++ b/src/zh/guides/aos/troubleshooting.md @@ -0,0 +1,59 @@ +# 使用 ao.link 进行故障排除 + +使用去中心计算机和网络,你需要能够排除故障,而不仅仅是你自己的代码。你需要跟踪进程和消息,这就是 [https://ao.link](https://ao.link) 工具箱的必要之处。 + +![ao.link homepage displaying ao network stats](aolink.png) + +## 分析 + +AOLink 4种分析指标: + +- 消息总数 (Total Messages) +- 用户总数 (Total Users) +- 进程总数 (Total Processes) +- 模块总数 (Total Modules) + +这些指标可以让你快速了解 ao 网络的总体运行状况。 + +## 事件 (Events) + +下面是 ao 计算机里的最新事件。 它们是一个已执行的消息列表。 这些事件可以是任何 ao 数据协议类型。 你可以单击进程 ID 或消息 ID 来获取详细信息。 + +![ao.link list of events](aolink-list-example.png) + +### 消息细节 + +![ao.link message details displaying the message processed](aolink-message-details.png) + +消息细节包括以下关键信息: + +- From +- To +- Block Height +- Created +- Tags +- Data +- Result Type +- Data + +如果你想进一步排除故障和调试,你可以通过单击 “Compute” 来查看 CU(计算单元)的结果。 + +![ao.link compute result example for debugging](aolink-compute-example.png) + +### 进程细节 + +![ao.link displaying a process in details](aolink-process-details.png) + +进程细节提供进程的详细信息,在标签(Tag)中查看该进程的实例化所使用的各个模块,这非常有用 +左侧的图表显示的是进程的交互图。 +在本例中,这是 DevChat,你可以看到通过注册和广播消息进行交互的所有进程。 +这个例子是 DevChat 进程的实例,你可以看到通过注册和消息广播与之交互的所有进程 + +## 更多问题? + +请随时访问 DataOS 的 Discord 社区。 +https://discord.gg/4kF9HKZ4Wu + +## 总结 + +AOLink 是一款很好的工具,用于追踪 ao 计算机中的事件,请试一试。此外 permaweb 上还有另一个扫描工具:https://ao_marton.g8way.io/ - 试一试吧! diff --git a/src/zh/guides/index.md b/src/zh/guides/index.md new file mode 100644 index 0000000..4cdab36 --- /dev/null +++ b/src/zh/guides/index.md @@ -0,0 +1,17 @@ +--- +prev: + text: "扩建竞技场" + link: "zh/tutorials/bots-and-games/build-game" +next: + text: "aos" + link: "./aos/index" +--- + +# 指南 + +本指南旨在帮助你导览 **ao**和 **aos**,可以帮助你构建聊天室、去中心自治机器人等程序。 + +## 章节列表 + +- [aos](aos/index) +- [aoconnect](aoconnect/aoconnect) diff --git a/src/zh/index.md b/src/zh/index.md new file mode 100644 index 0000000..e0e468d --- /dev/null +++ b/src/zh/index.md @@ -0,0 +1,26 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: "" + text: "超级并行计算机" + tagline: "任何规模的去中心化计算。Arweave 上独有。" + actions: + - theme: brand + text: 即刻体验! + link: /zh/welcome/index + +features: + - title: 教程 + details: 手把手教你入门 ao + link: /zh/tutorials/index + + - title: 指南 + details: 各个功能部件的详细说明 + link: /zh/guides/index + + - title: 概念 + details: 搞懂 ao 网络背后的运行原理 + link: /zh/concepts/index +--- diff --git a/src/zh/references/ao.md b/src/zh/references/ao.md new file mode 100644 index 0000000..c60c7bd --- /dev/null +++ b/src/zh/references/ao.md @@ -0,0 +1,206 @@ +# ao 模块 + +版本: 0.0.3 + +`ao` 进程通信通过消息进行处理,每个进程用 ANS-104 DataItems 的格式接收消息,并且需要能够执行以下常见操作。 + +- isTrusted(msg) - 检查消息是否可信 +- send(msg) - 将消息发给另一个进程 +- spawn(module, msg) - 创建一个进程 + +这个 library 为 `ao` 开发者工具包提供了这些核心功能。开发者可以按需使用这个 library,但它是默认集成在开发者工具包里的。 + +## 属性 + +| 名称 | 描述 | 类型 | +| ----------- | -------------------------------------- | ------ | +| id | 进程标识符 (TXID) | string | +| \_module | 模块标识符 (TXID) | string | +| authorities | 可信任的交易集合 | string | +| \_version | library 的版本 | string | +| env | 交易评估环境 | string | +| outbox | 传出消息和生成新进程请求的发件箱 | object | + +## 方法 + +### send(msg: Message\) : Message\
+ + send 方法接收一个完整的 Message 对象,或者包含部分属性的 Message 对象作为参数。它会在这个对象上另外添加特定的 `ao` 标签,并返回一个完整的消息对象,同时将它插入到 ao.outbox.Messages 的表中。 + +**传入参数** + +- msg + +Schema + +```json +{ + "type": "object", + "properties": { + "Target": { + "type": "string", + "description": "Process/Wallet to send message to" + }, + "Data": { + "type": "any", + "description": "data to send in message DataItem" + }, + "Tags": { + "type": "object or array" + "description": "This property can be an array of name,value objects or an object" + } + } +} +``` + +例子 1 + +```lua +local message = ao.send({ + Target = msg.From, + Data = "ping", + Tags = { + { + name = "Content-Type", + value = "text/plain" + } + } +}) +``` + +例子 2 + +```lua +local message = ao.send({ + Target = msg.From, + Data = "ping", + Tags = { + "Content-Type" = "text/plain" + } +}) +``` + +**返回值** + +Schema + +```json +{ + "type": "object", + "properties": { + "Target": { + "type": "string" + }, + "Data": { + "type": "any" + }, + "Tags": { + "type": "array" + "description": "name/value array", + "items": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "value":{"type":"string"} + } + } + } + } +} +``` + +### spawn(module : string, spawn : Spawn\
) : Spawn\
+ +`spawn` 方法接收一个 TXID 模块作为第一个参数,以及一个完整的 Spawn 表,或者包含部分属性的 Spawn 表作为第二个参数。结果将返回一个完整的 Spawn 表。spawn 方法还会生成一个带有唯一引用标识符的 `Ref_` 标签。 + +**传入参数** + +| 名称 | 描述 | 类型 | +| ------ | --------------------------------------------------------------------------------------- | ------ | +| module | TXID 是一个模块二进制文件的标识符,用于实例化进程 | string | +| spawn | 包含完整或部分 `Data` 和 `Tags` 属性的 `spawn` 表 | table | + +Schema + +module + +```json +{ + "type": "string" +} +``` + +spawn + +```json +{ + "type": "object", + "properties": { + "Data": { "type": "any" }, + "Tags": { + "type": "object or array", + "description": "can be either array, or object" + } + } +} +``` + +**返回值** + +Schema + +```json +{ + "type": "object", + "properties": { + "Data": { "type": "any" }, + "Tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "value": { "type": "string" } + } + } + } + } +} +``` + +### isTrusted(msg : Message\
) : boolean + +在生成进程时,可以提供 0 个或多个 Authority 标签,ao library 会将这些值添加到 `ao` 属性中名为 `authorities` 的 table 数组中。这个数组为 ao.TN.1 提供了“权威证明”(Proof of Authority)功能。当消息到达 `handle` 方法时,开发者可以调用 `ao.isTrusted` 来验证消息是否来自可信来源。 + +**传入参数** + +| 名称 | 描述 | 类型 | +| ---- | ------------------------------------------- | ----- | +| msg | 用于检测这个进程是否可信的 Message | table | + +Schema + +```json +{ + "type": "object", + "properties": { + "Target": { + "type": "string" + }, + "Data": { + "type": "any" + }, + "Tags": { + "type": "array" + "description": "name/value array", + "items": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "value":{"type":"string"} + } + } + } + } +} +``` diff --git a/src/zh/references/cron.md b/src/zh/references/cron.md new file mode 100644 index 0000000..8aa7a28 --- /dev/null +++ b/src/zh/references/cron.md @@ -0,0 +1,35 @@ +# 定时消息(Cron Messages) + +ao 具有在指定时间间隔生成消息的能力,这个时间间隔可以是秒、分钟、小时或区块。有一个监控进程会自动解释和运行(eval)这些消息,然后通知 Process 根据时间处理(eval)这些消息。此时会产生一个实时进程,它可以与完整的 ao 网络或外部网络中的预言机进行通信。 + +## 在进程中设置定时消息(Cron Messages) + +创建这些定时消息(Cron Messages)的最简单方法,是在 aos 控制台中生成一个新进程并定义时间间隔。 + +```sh +aos [myProcess] --cron 5-minutes +``` + +在生成新进程时,你可以在命令行中传递一个 cron 参数,后面加上你希望 cron 触发的时间间隔。如果你希望消息实时触发,你必须启动一个监控事件。在 aos 中,你只需调用`.monitor`,它会在 `mu` 上启动一个工作进程,从 `cu` 触发定时(cron) 消息。然后你的进程将会每隔 `x-间隔` 收到定时(cron)消息。 + +```lua +.monitor +``` + +如果你希望停止触发定时(cron) 消息,只需调用 `.unmonitor`,这会停止触发过程。但下次你发送消息时,生成的定时(cron) 消息仍将创建和处理进程。 + +## 处理定时消息 + +每条定时(cron) 消息都有一个值为 `Cron` 的 `Action` 标签。根据定义,[处理器](handlers.md)在每次收到定时(cron) 消息时,都会自动执行特定任务。 + +```lua +Handlers.add( + "CronTick", -- handler 的名称 + Handlers.utils.hasMatchingTag("Action", "Cron"), -- 识别定时(cron) 消息的 handler 匹配函数 + function () -- 需要定时执行的 handler 任务 + -- 要执行的内容 + end +) +``` + +定时消息(Cron Messages)是一个强大的实用工具,可以用来创建具有广泛功能的“自主代理”。 diff --git a/src/zh/references/data.md b/src/zh/references/data.md new file mode 100644 index 0000000..e8e8037 --- /dev/null +++ b/src/zh/references/data.md @@ -0,0 +1,49 @@ +# 使用 ao 访问 Arweave 上的数据 + +在你的 ao 开发工作流程中,可能有时候你想要访问 arweave 上的数据。你的进程可以使用 ao 发送一条消息,然后 ao 网络会通过一个 Message 对象将数据提供给你的 Process 对象。 + +为了从 arweave 请求数据,你只需包含一个名为 `Load` 的 `Tag`,在该标签中,你可以使用数据的 TXID 来检索。 + +```lua + +Send({ + Target = ao.id, + Tags = { + Load = "WFM_Mi2FUNGCeP7r99XyeE3ySurt07LHxEGfW-wuIdY", + Action = "Data" + } +}) + +``` + +这条消息通过处理到达进程时,在传入消息的 `Data` 属性中,有一个 DataItem 的引用。同时,DataItem 的 `Data` 将以 base64 的类型传递。 + +```lua +{ + Owner = "[Owner Address]" + Target = "[Process Identifier]", + Data = { + Owner = "xDKpdiZ7H9n_SsdX_CMpkybMGIdin5AUciM00mQgxRE", + Tags = { + "Content-Type": "application/json" + }, + Data = "[base64]", + ... + } + ... +} + +``` + +在 lua 中,你可以使用 “.base64” 模块将你的数据从 base64 解码回原始格式。 + +```lua +local base64 = require(".base64") + + +local data = base64.decode(Msg.Data.Data) +``` + +## 为什么从 Arweave 上面取数据 + +你的进程可能需要访问数据来做决策,或者你可能想要通过 `data` 加载功能,为你的进程添加特性。 diff --git a/src/zh/references/editor-setup.md b/src/zh/references/editor-setup.md new file mode 100644 index 0000000..8f12847 --- /dev/null +++ b/src/zh/references/editor-setup.md @@ -0,0 +1,25 @@ +# 编辑器设置 + +内置的 ao 函数和实用工具不太好用。为了提升你的开发体验,建议你在你常用的文本编辑器中安装 [Lua Language Server](https://luals.github.io) 扩展插件,并添加 [ao插件](https://github.com/martonlederer/ao-definitions)。它们支持所有内置的 aos [模块](../guides/aos/index.md) 和 [全局变量](../guides/aos/intro#globals)。 + +## VS Code + +安装 [sumneko.lua](https://marketplace.visualstudio.com/items?itemName=sumneko.lua) 扩展插件: + +1. 在扩展市场中搜索 sumneko 的 "Lua"。 +2. 下载并安装该扩展插件。 +3. 打开 VS Code 命令面板,使用 `Shift + Command + P`(Mac)/ `Ctrl + Shift + P`(Windows/Linux),然后运行以下命令: + +``` +> Lua: Open Addon Manager +``` + +4. 在插件管理器中搜索 “ao”,它应该是第一个结果。点击 “启用”,然后就可以享受自动完成的功能了! + +如果你不想为每个工作区重复这个过程,你可以从生成的工作区 `settings.json` 文件中复制 `Lua.workspace.library` 对象到你的全局 `settings.json` 文件中。 + +## 其他编辑器 + +1. 确认你的编辑器支持[语言服务器协议](https://microsoft.github.io/language-server-protocol/implementors/tools/)。 +2. 按照 [luals.github.io](https://luals.github.io/#install) 上的指示安装 Lua 语言服务器。 +3. 为语言服务器安装“ao”插件。 diff --git a/src/zh/references/handlers.md b/src/zh/references/handlers.md new file mode 100644 index 0000000..6742775 --- /dev/null +++ b/src/zh/references/handlers.md @@ -0,0 +1,147 @@ +# Handlers (版本 0.0.3) + +## 概览 + +Handlers library 提供了一种灵活的方式来管理和执行一系列基于模式的 handler。每个 handler 由一个匹配函数、一个处理函数和一个名称组成。这个 library 适用于需要根据不同的输入条件采取不同行动的场景。 + +## 模块结构 + +- `Handlers._version`: 代表 Handlers library 版本的字符串。 +- `Handlers.list`: 存储注册的 handler 列表的表。 + +## 方法 + +### `Handlers.add(name, pattern, handler)` + +添加一个新的 handler 或者根据名称更新已有的 handler。 + +### `Handlers.append(name, pattern, handle)` + +在 handler 列表的末尾插入一个新的 handler。 + +#### 传入参数 + +- `pattern` (function 类型): 判断 handler 是否被执行的方法。 +- `handle` (function 类型): handler 方法的执行函数。 +- `name` (string 类型): handler 的唯一命名。 + +### `Handlers.prepend(name, pattern, handle)` + +把一个新的 handler 加到 handler 列表的开头. + +#### 传入参数 + +- 和 `handlers.append` 的一样。 + +### `Handlers.before(handleName)` + +返回一个对象,可以在特定的 handler 前加入一个新的 handler。 + +#### 传入参数 + +- `handleName` (string 类型): 在新的 handler 加入前,需要给 handler 的命名。 + +#### 返回值 + +- 一个拥有 `add` 方法的对象,可以插入新的 handler。 + +### `Handlers.after(handleName)` + +返回一个对象,可以在特定的 handler 后加入一个新的 handler。 + +#### 传入参数 + +- `handleName` (string 类型): 在新的 handler 加入后,需要给 handler 的命名。 + +#### 返回值 + +- 一个拥有 `add` 方法的对象,可以插入新的 handler。 + +### `Handlers.remove(name)` + +根据名称从 handler 列表里移除一个 handler。 + +#### 传入参数 + +- `name` (string 类型): 要被移除的 handler 的名称。 + +### `Handlers.evaluate(msg, env)` + +根据给定的消息和环境对每个 handler 进行评估。handler 按照它们在 handler 列表中出现的顺序依次调用。 + +#### 传入参数 + +- `msg` (table 类型): handler 要处理的消息。 +- `env` (table 类型): handler 执行的环境。 + +#### 返回值 + +- `response` (类型取决于 handler 是否匹配): handler 的响应。如果没有匹配的 handler,返回一个默认消息。 + +## 使用案例 + +```lua +-- 定义模式和处理函数 +local function myPattern(msg) + -- 判断 handler 是否被执行 +end + +local function myHandle(msg, env, response) + -- Handler 逻辑 +end + +-- 加一个新的 handler +Handlers.add("myHandler", myPattern, myHandle) + +-- 评估一条消息 +local response = handlers.evaluate({ key = "value" }, { envKey = "envValue" }) +``` + +## 说明 + +- 根据 Handler 在 `handlers.list` 中的顺序执行 +- 匹配函数返回 `0` 代表跳过 handler,返回 `-1` 代表在 handler 执行后中断,或者返回 `1` 代表继续执行下一个 handler。 +- `evaluate` 方法可以从多个 handler 上连接响应。 + +## Handlers.utils + +Handlers.utils 模块提供了两个常见的匹配模式函数和一个常见的处理函数。 + +- hasMatchingData(data) +- hasMatchingTag(name, value) +- reply(txt) + +### Handlers.utils.hasMatchingData(data : string) + +这个辅助函数返回一个需要消息参数的函数,因此你可以将它放入任何 handler 的匹配参数中。该函数会将传入消息的数据与作为参数提供的字符串进行比较。 + +```lua +Handlers.add("ping", + Handlers.utils.hasMatchingData("ping"), + ... +) +``` + +如果这个进入进程的消息有设置成 ”ping" 的数据,那么这个 handler 会匹配上它,并调用处理函数。 + +### Handlers.hasMatchingTag(name : string, value : string) + +这个辅助函数返回一个需要消息参数的函数,因此你可以将它放入 Handlers 模块的任何匹配参数中。该函数会比较 Tag 的名称和数值,如果它们相等,则调用处理函数。 + +```lua +Handlers.add("ping", + Handlers.utils.hasMatchingData("ping"), + ... +) +``` + +### Handlers.reply(text : string) + +这个辅助函数是一个简单的处理函数,就是将 text 的值放入发送消息的 Data 属性中。 + +```lua +Handlers.add("ping", + Handlers.utils.hasMatchingData("ping"), + Handlers.utils.reply("pong") +) +``` diff --git a/src/zh/references/index.md b/src/zh/references/index.md new file mode 100644 index 0000000..eb59da4 --- /dev/null +++ b/src/zh/references/index.md @@ -0,0 +1,21 @@ +--- +prev: + text: "aos 接口" + link: "/zh/concepts/tour" +next: + text: "Lua" + link: "./lua" +--- + +# 参考 + +## 目录 + +- [Lua](lua) +- [Web Assembly](wasm) +- [ao 模块](ao) +- [Handlers](handlers) +- [代币](token) +- [加载 Arweave 数据](data) +- [定时消息](cron) +- [编辑器设置](editor-setup) diff --git a/src/zh/references/lua.md b/src/zh/references/lua.md new file mode 100644 index 0000000..7baed4a --- /dev/null +++ b/src/zh/references/lua.md @@ -0,0 +1,44 @@ +# 认识 Lua + +### 理解 Lua + +- **背景**: Lua 是一种轻量、高级、多范式的编程语言,主要用于嵌入式系统和客户端。它因效率、简洁和灵活性而闻名。 +- **核心特性**: Lua 提供了强大的数据描述构造、动态类型、高效的内存管理以及对面向对象编程的良好支持。 + +### 上手指南 + +1. **安装**: 访问 [Lua 的官方网站](http://www.lua.org/download.html) ,下载并安装 Lua。 +2. **环境**: 你可以使用简单的文本编辑器和命令行,或者像 ZeroBrane Studio ,或带有 Lua 插件的 Eclipse 这样的集成开发环境(IDE)。 + +### 基础语法和概念 (在 aos 中的) + +- **Hello World**: + ```lua + "Hello, World!" + ``` +- **变量和类型**: Lua 是动态类型的。基本类型包括 `nil`、`boolean`、`number`、`string`、`function`、`userdata`、`thread` 和 `table`。 +- **控制结构**: 包括 `if`、 `while`、 `repeat...until` 和 `for`。 +- **函数**: Lua 中的一等公民,支持闭包和高阶函数。 +- **Tables**: Lua 中唯一的数据结构机制,可用于表示数组、集合、记录等。 + +### 动手试试 + +- **用 Lua 的交互模式进行实验**: 在你的终端中运行 `aos` 并尝试 Lua 命令。 +- **写一些简单的脚本**: 创建 `.lua` 文件并使用 Lua 解释器运行它们。使用 `.load file.lua` 功能将 Lua 代码上传到你的 `aos` 进程中。 + +### 资源 + +- **官方文档**: [Lua 5.3 参考手册](https://www.lua.org/manual/5.3/) +- **在线教程**: 像 [Learn Lua](https://www.learn-lua.org/) 这样的网站非常适合进行交互式学习。 +- **书籍**: 《Lua 编程》是一本全面的资源。(第一版可在 [网上](http://www.lua.org/pil/contents.html) 获取) +- **社区**: 加入像 [Lua 用户](http://lua-users.org/) 这样的论坛或社区,以获取支持和讨论。 + +### 最佳实践 + +- **保持极简**: Lua 的设计是简单灵活的。你要在代码中拥抱这种哲学。 +- **性能**: 学习关于 Lua 的垃圾回收和高效利用 table 的知识。 +- **集成**: 考虑 Lua 如何嵌入到其他应用程序中,特别是 C/C++ 项目中。 + +### 总结 + +Lua 是一种强大的语言,尤其在嵌入式系统和游戏开发领域。它的简单性和高效性让它成为特定用例的首选。祝你在 Lua 编程之旅中获得愉快的体验! diff --git a/src/zh/references/token.md b/src/zh/references/token.md new file mode 100644 index 0000000..1f33938 --- /dev/null +++ b/src/zh/references/token.md @@ -0,0 +1,304 @@ +# ao Token 和子账本规范 + +**状态:** DRAFT-1 +**目标网络:** ao.TN.1 + +该规范描述了标准 ao Token 进程所必须的消息 handler 和函数。该标准提供用户控制和转移资产的能力,资产的稀缺性由进程维护。 + +每个符合标准的进程会实现一个余额账本,用来对进程代表的资产所有权进行编码。标准中提供了一系列函数进行账本修改,这些函数包含安全保护代码,保护 Token 所有权的稀缺性。 + +此外,该规范描述了一种名为 '子账本' 的进程类型。实现子账本后,可以将一定数量的 Token 从父进程转移到相同 Token 接口规范的子进程中。如果子账本进程的 `From-Module` 得到参与者的信任,这些子账本可以直接进行交易,而无需与父进程交换消息。即使父进程在拥堵状态,参与者也能使用子进程中的 Token。如果参与者信任子账本进程,可以认为这些进程之间的余额是等价的。因此,同一个 Token 可以由任意数量的进程并行处理。 + +# Token 进程( Token Processes ) + +Token 进程可以对多种类型的消息作出响应,类型在 `Action` 标签中指定。Token 支持的所有的 `Action` 消息类型如下: + +| 名称 | 描述 | 只读 | +| -------- | ------------------------------------------------------------- | ------------------ | +| Balance | 获取一个标志符(identifier) 的余额 | :heavy_check_mark: | +| Balances | 获取整个账本/账户的余额列表 | :heavy_check_mark: | +| Transfer | 函数调用者向一个或多个目标发送1个或多个单位,并选择通知目标。 | :x: | +| Mint | 如果你使用了具备 root 权限的进程,你可以增加 Token 供应量 | :x: | + +在本节的其余部分中,描述了生成符合规范和 Token 进程所需的标签,描述了每个 `Action` 消息的形式和它们的结果。 + +## (创建请求的参数)Spawning Parameters + +每个符合规范的 Token 进程在其生成消息中必须携带以下特定的参数: + +| 标签 | 描述 | 有没有强制性 | +| ------------ | --------------------------------------------------- | ------------------ | +| Name | Token 的名称,也就是显示给用户的名称。 | :heavy_check_mark: | +| Ticker | Token 的缩写名称,以便快速引用。 | :heavy_check_mark: | +| Logo | 应用程序希望在 Token 旁边显示的图片,以便快速识别。 | :heavy_check_mark: | +| Denomination | 当向用户显示数量和余额时, Token 精度度量。 | :x: | + +## 消息协议(Messaging Protocol) + +### Balance(Target? : string) + +返回目标的余额。如果未提供目标参数,返回消息发送者的余额。 + +`Action` 消息的例子: + +```lua= +send({ + Target = "{TokenProcess Identifier}", + Tags = { + Action = "Balance", + Target = "{IDENTIFIER}" + } +}) +``` + +消息返回结果的例子: + +``` +{ + Tags = { + Balance = "50", + Target = "LcldyO8wwiGDzC3iXzGofdO8JdR4S1_2A6Qtz-o33-0", + Ticker = "FUN" + } +} +``` + +### Balances() + +返回 Token 中所有参与者的余额。 + +```lua +send({ + Target = "[TokenProcess Identifier]", + Tags = { + Action = "Balances", + Limit = 1000, # TODO: Is this necessary if the user is paying for the compute and response? + Cursor? = "BalanceIdentifer" + } +}) +``` + +消息返回结果的例子: + +```lua +{ + Data = { + "MV8B3MAKTsUOqyCzQ0Tsa2AR3TiWTBU1Dx0xM4MO-f4": 100, + "LcldyO8wwiGDzC3iXzGofdO8JdR4S1_2A6Qtz-o33-0": 50 + } +} +``` + +### Transfer(Target, Quantity) + +如果发送者拥有足够的余额,则向目标发送 `Quantity`,向接收者发出 `Credit-Notice`,向发送者发出 `Debit-Notice`。如果发送者余额不足,操作失败并通知发送者。 + +```lua +send({ + Target = "[TokenProcess Identifier]", + Tags = { + { name = "Action", value = "Transfer" }, + { name = "Recipient", value = "[ADDRESS]" }, + { name = "Quantity", value = "100" } + } +}) +``` + +在未设置 `Cast` 时,如果发送成功,则应发送通知消息。 + +```lua +ao.send({ + Target = "[Recipient Address]", + Tags = { + { name = "Action", value = "Credit-Notice" }, + { name = "Sender", value = "[ADDRESS]" }, + { name = "Quantity", value = "100"} + } +}) +``` + +接收者将从消息的 `From-Process` 标签中知道他们已收到该 Token 。 + +### Get-Info() + +```lua +send({ + Target = "{Token}", + Tags = { + Action = "Info" + } +}) +``` + +### Mint() [optional] + +实现 `Mint` 操作,允许特定用户创建更多 Token 余额。 + +```lua +send({ + Target ="{Token Process}", + Tags = { + Action = "Mint", + Quantity = "1000" + } +}) +``` + +# 子账本进程(Subledger Processes) + +子账本必须实现 Token 合约的完整消息协议(不包括 `Mint` 操作)。子账本必须为进程实现额外的功能和生成参数。接下来描述子账本和父账本的区别。 + +### (创建请求的参数)Spawning Parameters + +每个符合规范的子账本的进程在其创建请求的消息中必须携带以下特定参数: + +| Tag | 描述 | 有没有强制性 | +| ------------ | --------------------------------- | ------------ | +| Source-Token | 这个子账本代表的根进程的 `ID`。 | :x: | +| Parent-Token | 这个子账本连接到的父进程的 `ID`。 | :x: | + +### `Credit-Notice` Handler + +在收到 `Credit-Notice` 消息时,符合规范的子账本进程必须检查所涉及的进程是否是 `Parent-Token`。如果是,则子账本必须将 `Sender` 的余额增加指定数量。 + +### Transfer(Target, Quantity) + +除了常规的 `Credit-Notice` 标签外,子账本进程还必须提供 `Source-Token` 和 `Parent-Token` 两个标签。接受者如果信任子账本进程的 `Module`,用户就可以在子账本中进行 `Source-Token` 上的存款那样的转账和进行借贷动作。 + +修改后的 `Credit-Notice` 结构如下: + +```lua +ao.send({ + Target = "[Recipient Address]", + Tags = { + { name = "Action", value = "Credit-Notice" }, + { name = "Quantity", value = "100"}, + { name = "Source-Token", value = "[ADDRESS]" }, + { name = "Parent-Token", value = "[ADDRESS]" } + } +}) +``` + +### Withdraw(Target?, Quantity) + +所有子账本必须允许余额持有者将他们的 Token 提取到父账本中。收到一个 `Action: Withdraw` 消息后,子账本必须向其 `Parent-Ledger` 发送一个 `Action` 消息,将请求的 Token 转移到调用者的地址,同时在本地扣除他们的账户。这个转移将会让调用者收到来自 `Parent-Ledger` 的 `Credit-Notice`。 + +```lua +send({ + Target = "[TokenProcess Identifier]", + Tags = { + { name = "Action", value = "Withdraw" }, + { name = "Recipient", value = "[ADDRESS]" }, + { name = "Quantity", value = "100" } + } +}) +``` + +# Token 例子 + +> 请注意: 在实现 Token 时,消息上的所有标签必须是 "string" 类型。你可以使用 `tostring` 函数将简单类型转换为字符串。 + +```lua +if not balances then + balances = { [ao.id] = 100000000000000 } +end + +if name ~= "Fun Coin" then + name = "Fun Coin" +end + +if ticker ~= "Fun" then + ticker = "fun" +end + +if denomination ~= 6 then + denomination = 6 +end + +-- handler 处理传入的消息 + +handlers.add( + "transfer", + handlers.utils.hasMatchingTag("Action", "Transfer"), + function (msg) + assert(type(msg.Tags.Recipient) == 'string', 'Recipient is required!') + assert(type(msg.Tags.Quantity) == 'string', 'Quantity is required!') + + if not balances[msg.From] then + balances[msg.From] = 0 + end + + if not balances[msg.Tags.Recipient] then + balances[msg.Tags.Recipient] = 0 + end + + local qty = tonumber(msg.Tags.Quantity) + assert(type(qty) == 'number', 'qty must be number') + -- handlers.utils.reply("Transfering qty")(msg) + if balances[msg.From] >= qty then + balances[msg.From] = balances[msg.From] - qty + balances[msg.Tags.Recipient] = balances[msg.Tags.Recipient] + qty + ao.send({ + Target = msg.From, + Tags = { + Action = "Debit-Notice", + Quantity = tostring(qty) + } + }) + ao.send({ + Target = msg.Tags.Recipient, + Tags = { + Action = "Credit-Notice", + Quantity = tostring(qty) + }}) + -- if msg.Tags.Cast and msg.Tags.Cast == "true" then + -- return + -- end + + end + end +) + +handlers.add( + "balance", + handlers.utils.hasMatchingTag("Action", "Balance"), + function (msg) + assert(type(msg.Tags.Target) == "string", "Target Tag is required!") + local bal = "0" + if balances[msg.Tags.Target] then + bal = tostring(balances[msg.Tags.Target]) + end + ao.send({Target = msg.From, Tags = { + Target = msg.From, + Balance = bal, + Ticker = ticker or "" + }}) + end +) + +local json = require("json") + +handlers.add( + "balances", + handlers.utils.hasMatchingTag("Action", "Balances"), + function (msg) + ao.send({ + Target = msg.From, + Data = json.encode(balances) + }) + end + +) + +handlers.add( + "info", + handlers.utils.hasMatchingTag("Action", "Info"), + function (msg) + ao.send({Target = msg.From, Tags = { + Name = name, + Ticker = ticker, + Denomination = tostring(denomination) + }}) + end +) +``` diff --git a/src/zh/references/wasm.md b/src/zh/references/wasm.md new file mode 100644 index 0000000..9aca729 --- /dev/null +++ b/src/zh/references/wasm.md @@ -0,0 +1,5 @@ +# 认识 Web Assembly + +WebAssembly(通常简称为 Wasm)是一种现代的二进制指令格式,为高级语言(如C、C++和Rust)提供了一个可迁移的编译目标。它可以支持在网络上部署客户端和服务器应用,并提供高水平的性能和效率。WebAssembly 是为了保持网络浏览器的安全性和沙盒功能而设计的,成为网络应用程序的选择之一。它是网络开发人员的关键技术,使他们能够用多种语言编写代码,并将代码编译为在浏览器中以接近原生速度运行的字节码。 + +WebAssembly 的重要性在于它能够弥合网络应用程序和本地应用程序之间的差距。它使得之前局限于桌面环境的复杂应用程序和游戏,能够在浏览器中以相当的性能运行。这为网络开发带来了新的可能性,包括创建高性能的网络应用程序、游戏,甚至将现有的桌面应用程序移植到网络上。WebAssembly 与 JavaScript 并行运行,通过使用更适合此类任务的语言编写性能关键组件,弥补了 JavaScript 的缺陷,从而增强了网络应用程序的功能和性能。 diff --git a/src/zh/tutorials/begin/aos-print.png b/src/zh/tutorials/begin/aos-print.png new file mode 100644 index 0000000..2304d1d Binary files /dev/null and b/src/zh/tutorials/begin/aos-print.png differ diff --git a/src/zh/tutorials/begin/chatroom.md b/src/zh/tutorials/begin/chatroom.md new file mode 100644 index 0000000..b81c4f4 --- /dev/null +++ b/src/zh/tutorials/begin/chatroom.md @@ -0,0 +1,210 @@ +# 在 aos 中建立一个聊天室 + +::: info +如果你想学习如何在 `ao` 中创建聊天室,你至少需要了解发送和接收消息的基本方法。如果还没有,建议你先查看[消息传递](messaging)教程。 +::: + +在本教程中,我们将使用 Lua 脚本语言在 `ao` 中构建一个聊天室。 聊天室将具有两个主要功能: + +1. **注册**:允许进程加入聊天室。 +2. **广播**:从一个进程向所有注册参与者发送消息。 + +让我们首先为我们的聊天室建立框架。 + +## 视频教程 + + + +## 第 1 步:基础 + +- 打开你喜欢的代码编辑器。 + +::: info +你可能会发现在代码编辑器中安装[推荐插件](../../references/editor-setup.md) 有助于增强你的 Lua 脚本编写体验。 +::: + +- 创建一个名为 `chatroom.lua` 的文件。 + +![聊天室Lua文件](./chatroom1.png) + +## 步骤 2:创建成员列表 + +- 在 `chatroom.lua` 中,你将首先初始化一个列表来跟踪参与者: + + ```lua + Members = Members or {} + ``` + +![Chatroom Lua 文件 - 命名成员列表](./chatroom2.png) + +- 保存 `chatroom.lua` 文件 + +## 步骤 3:将聊天室加载到 aos 中 + +保存 `chatroom.lua` 后,将聊天室代码加载到 `aos` 中。 + +- 如果你尚未启动 `aos`,请在终端中保存 chatroom.lua 的目录中启动 +- 在 `aos` CLI 中,键入以下脚本以将你的脚本加载到 `aos` 进程中: + + ```lua + .load chatroom.lua + ``` + + ![将聊天室加载到aos](./chatroom3.png) + + 如上面的屏幕截图所示,你可能会收到 `undefined` 响应。该返回没有问题,但我们仍然希望确保文件正确加载。 + + ::: info + 在 aos 的 Lua 求值环境中,当你执行一段没有显式返回值的代码时,`undefined` 是标准响应,表示没有返回结果。在加载资源或执行操作时可以观察到这一点。例如,执行 `X = 1` 将产生 `undefined`,因为该语句不包含 return 语句。 + + 但是,如果执行 `X = 1; return X`,环境将返回值 `1`。 在此框架内工作时,理解这种行为至关重要,因为它有助于澄清执行修改状态的命令与旨在产生直接输出的命令之间的区别。 + ::: + +- 在 `aos` 中输入 `Members` 或你为用户列表命名的任何内容。 它应该返回一个空数组 `{}`。 + + ![查看成员列表](./chatroom4.png) + + 如果你看到一个空数组,则你的脚本已成功加载到 `os` 中。 + +## 步骤 4:创建聊天室功能 + +### 注册 handler + +注册 handler 将允许进程加入聊天室。 + +1. **添加注册 handler:** 使用以下代码修改 `chatroom.lua` 以包含 `Members` 的handler以注册到聊天室: + + ```lua + -- 修改 `chatroom.lua` 以包含 `Members` 的handler + -- 使用以下代码注册聊天室: + + Handlers.add( + "Register", + Handlers.utils.hasMatchingTag("Action", "Register"), + function (msg) + table.insert(Members, msg.From) + Handlers.utils.reply("registered")(msg) + end + ) + ``` + + ![注册handler](./chatroom5.png) + + 该handler将允许进程通过响应标签 `Action = "Register"` 来注册到聊天室。注册成功后,将显示一条打印消息确认 `registered`。 + +2. **重新加载并测试:** 让我们将自己注册到聊天室,并重新加载和测试脚本。 + + - 使用 `.load chatroom.lua` 在 aos 中保存并重新加载脚本。 + - 检查注册 handler 是否加载了以下脚本: + + ```lua + Handlers.list + ``` + + ![检查handler列表](./chatroom6.png) + + 这将返回聊天室应用中所有 handler 列表。 由于这很可能是你第一次在 `aos` 中进行开发,因此你应该会看到一个名为 `Register` 的 handler。 + + - 让我们通过注册自己到聊天室来测试注册过程: + + ```lua + Send({ Target = ao.id, Action = "Register" }) + ``` + + 如果成功,你应该会看到一条 `message added to your outbox` 信息,然后你会看到一条新的打印消息,上面写着 `registered`。 + + ![注册聊天室](./chatroom7.png) + + - 最后,让我们检查一下是否已成功添加到 `Members` 列表中: + + ```lua + Members + ``` + + 如果成功,你现在将在 `Members` 列表中看到你的进程 ID。 + + ![查看成员列表](./chatroom8.png) + +### 添加广播 handler + +现在你已经有了一个聊天室,让我们创建一个handler,允许你向聊天室的所有成员广播消息。 + +- 将以下 handler 添加到 `chatroom.lua` 文件中: + + ```lua + Handlers.add( + "Broadcast", + Handlers.utils.hasMatchingTag("Action", "Broadcast"), + function (msg) + for _, recipient in ipairs(Members) do + ao.send({Target = recipient, Data = msg.Data}) + end + Handlers.utils.reply("Broadcasted.")(msg) + end + ) + ``` + + 该 handler 将允许你向聊天室的所有成员广播消息。 + +- 让我们通过向聊天室发送消息来测试广播 handler: + + ```lua + Send({Target = ao.id, Action = "Broadcast", Data = "Broadcasting My 1st Message" }) + ``` + + - 如果成功,你应该会看到一条 `message added to your outbox`,然后你会看到一条新的打印消息,上面写着 `Broadcasting My 1st Message`,因为你是聊天室中的成员`Members`,所以可以收到消息。 + +## 步骤 5:邀请 Morpheus 加入聊天室 + +现在你已成功注册自己到聊天室,让我们邀请 Morpheus 加入我们的聊天室。 为此,我们将向他发送邀请,来将他注册到聊天室。 + +Morpheus 是一个自主代理,其handler将响应标签 `Action = "Join"`,然后让他使用你的 `Register` 标签注册到聊天室。 + +- 让我们向 Morpheus 发送加入聊天室的邀请: + + ```lua + Send({ Target = Morpheus, Action = "Join" }) + ``` + +- 要确认 Morpheus 已加入聊天室,请检查 `Members` 列表: + + ```lua + Members + ``` + + 如果成功,你将收到来自 Morpheus 的广播消息。 + +## 步骤 6:邀请 Trinity 加入聊天室 + +Morpheus 将向你提供 Trinity 的进程 ID,并告诉你邀请她加入聊天室。使用下列命令查看 Morpheus 的消息: + +```lua +Inbox[#Inbox].Data +``` + +使用 Morpheus 相同的流程将她的进程 ID 保存为 `Trinity` 并邀请她加入聊天室。 + +如果她成功加入聊天室,她就会向你提出下一个挑战,创建一个[代币](token)。 + +## 让其他人加入聊天室 + +### 引导其他人 + +- 邀请 aos 用户: + 鼓励其他 aos 用户加入你的聊天室。 他们可以注册到你的聊天室并进行消息广播。 + +- 提供加入说明: + 与他们共享一个简单的脚本以方便入门: + +```lua +-- 嘿,我们在 aos 上聊天吧! 在你的 aos 环境中发送以下命令来加入我的聊天室: +Send({ Target = [Your Process ID], Action = "Register" }) +-- 然后,你可以使用以下方式广播消息: +Send({Target = [Your Process ID], Action = "Broadcast", Data = "Your Message" }) +``` + +## 下一步 + +恭喜! 你已在 `ao` 中成功建立了一个聊天室,并邀请了 Morpheus 加入你。你还创建了一个广播 handler 用于向聊天室的所有成员发送消息。 + +接下来,你将继续与 Morpheus 互动,但这次你将在对话中添加 Trinity。她将带领你应对接下来的挑战。 祝你好运! diff --git a/src/zh/tutorials/begin/chatroom1.png b/src/zh/tutorials/begin/chatroom1.png new file mode 100644 index 0000000..6fd89a5 Binary files /dev/null and b/src/zh/tutorials/begin/chatroom1.png differ diff --git a/src/zh/tutorials/begin/chatroom2.png b/src/zh/tutorials/begin/chatroom2.png new file mode 100644 index 0000000..443f768 Binary files /dev/null and b/src/zh/tutorials/begin/chatroom2.png differ diff --git a/src/zh/tutorials/begin/chatroom3.png b/src/zh/tutorials/begin/chatroom3.png new file mode 100644 index 0000000..66598be Binary files /dev/null and b/src/zh/tutorials/begin/chatroom3.png differ diff --git a/src/zh/tutorials/begin/chatroom4.png b/src/zh/tutorials/begin/chatroom4.png new file mode 100644 index 0000000..9a9b5a2 Binary files /dev/null and b/src/zh/tutorials/begin/chatroom4.png differ diff --git a/src/zh/tutorials/begin/chatroom5.png b/src/zh/tutorials/begin/chatroom5.png new file mode 100644 index 0000000..809ecd0 Binary files /dev/null and b/src/zh/tutorials/begin/chatroom5.png differ diff --git a/src/zh/tutorials/begin/chatroom6.png b/src/zh/tutorials/begin/chatroom6.png new file mode 100644 index 0000000..e6eb24d Binary files /dev/null and b/src/zh/tutorials/begin/chatroom6.png differ diff --git a/src/zh/tutorials/begin/chatroom7.png b/src/zh/tutorials/begin/chatroom7.png new file mode 100644 index 0000000..7f0be9e Binary files /dev/null and b/src/zh/tutorials/begin/chatroom7.png differ diff --git a/src/zh/tutorials/begin/chatroom8.png b/src/zh/tutorials/begin/chatroom8.png new file mode 100644 index 0000000..22fd3fc Binary files /dev/null and b/src/zh/tutorials/begin/chatroom8.png differ diff --git a/src/zh/tutorials/begin/dao.md b/src/zh/tutorials/begin/dao.md new file mode 100644 index 0000000..123a5fd --- /dev/null +++ b/src/zh/tutorials/begin/dao.md @@ -0,0 +1,277 @@ +# DAO 指南 + +本指南将带你完成使用 aos 构建 DAO 的过程。如果你还没有,你需要首先在 aos 中构建一个 [代币](token)。 我们会将 DAO 代码与 [token](./token) 指南中的令牌代码一起加载到 aos 中。 在 ao 的上下文中,DAO 可用于管理 MU、CU 和 SU 节点。 + +在我们的 DAO 中,我们将实施一个称为 `slashing` 的进程。 在 ao 的情况下,如果一个单位行为不当,其他单位可能会投票削减它。 削减意味着他们将失去他们的质押,我们稍后将进一步讨论质押的话题。 + +创建一个名为 `dao` 的新目录,并将其复制到代币指南中创建的 `token.lua` 中。 + +```sh +mkdir dao +cd dao +cp ../token/token.lua . +``` + +现在创建一个名为 `dao.lua` 的新文件并在你喜欢的编辑器中打开它。 + +## 编写 DAO 代码 + +### 初始化状态 + +打开 `dao.lua` 并添加以下行 + +```lua +Balances = Balances or {} +Stakers = Stakers or {} +Unstaking = Unstaking or {} +Votes = Votes or {} +``` + +这些表存储 DAO 的状态,包括用户余额、质押代币、取消质押请求和投票记录。 + +### 质押 + +质押是放置你的代币以使你能够投票的过程。 如果有人希望获得投票能力,他们必须拥有并抵押一些代币。让我们添加一个用于质押的handler。 ao 中的成员或节点如果想要获得投票削减或保留节点的能力,他们就会想要进行质押,我们将在稍后进一步讨论。 + +```lua +-- 质押动作handler +Handlers.stake = function(msg) + local quantity = tonumber(msg.Tags.Quantity) + local delay = tonumber(msg.Tags.UnstakeDelay) + local height = tonumber(msg['Block-Height']) + assert(Balances[msg.From] and Balances[msg.From] >= quantity, "Insufficient balance to stake") + Balances[msg.From] = Balances[msg.From] - quantity + Stakers[msg.From] = Stakers[msg.From] or {} + Stakers[msg.From].amount = (Stakers[msg.From].amount or 0) + quantity + Stakers[msg.From].unstake_at = height + delay +end +``` + +上面的代码从传入消息中获取数量和延迟,如果 `From` 有足够的余额,则将质押放入 Stakers 表中。 延迟代表未来可以取消质押代币的时间。 + +### 取消质押 + +取消质押是撤回质押代币的过程。 如果有人取消了所有代币的质押,他们将放弃投票的能力。在这里,我们提供了一个取消质押的handler。 + +```lua +-- 取消质押动作handler +Handlers.unstake = function(msg) + local quantity = tonumber(msg.Tags.Quantity) + local stakerInfo = Stakers[msg.From] + assert(stakerInfo and stakerInfo.amount >= quantity, "Insufficient staked amount") + stakerInfo.amount = stakerInfo.amount - quantity + Unstaking[msg.From] = { + amount = quantity, + release_at = stakerInfo.unstake_at + } +end +``` + +这会将来自消息的传入金额推送到取消抵押表中,并减少他们抵押的金额 `stakerInfo.amount = voterInfo.amount - amount`。 + +### 投票 + +投票是管理 DAO 的过程。发送投票消息后,成员会收到与他们质押金额成比例的投票。截止日期变量表示何时进行投票。 + +```lua +-- 投票动作handler +Handlers.vote = function(msg) + local quantity = Stakers[msg.From].amount + local target = msg.Tags.Target + local side = msg.Tags.Side + local deadline = tonumber(msg['Block-Height']) + tonumber(msg.Tags.Deadline) + assert(quantity > 0, "No staked tokens to vote") + Votes[target] = Votes[target] or { yay = 0, nay = 0, deadline = deadline } + Votes[target][side] = Votes[target][side] + quantity +end +``` + +在这里,如果发送投票的进程或用户有一些代币,他们可以在投票表中放置一个条目。 `side` 是或否,设置为他们的股份数量。在我们的示例中,`nay` 票是对削减的投票,`yay` 票是对保留的投票。 + +发送的 msg.Tags.Target 代表正在投票的内容。 在 AO 的情况下,这可能是成员投票削减的 MU、CU 或 SU 的钱包地址。 + +### 最终确定 + +我们希望在每条消息上运行一些逻辑。 我们将其定义为 `finalizationHandler`。被削减意味着你将失去在 DAO 中的质押。 + +```lua +-- Finalization handler +local finalizationHandler = function(msg) + local currentHeight = tonumber(msg['Block-Height']) + -- 处理取消质押 + for address, unstakeInfo in pairs(Unstaking) do + if currentHeight >= unstakeInfo.release_at then + Balances[address] = (Balances[address] or 0) + unstakeInfo.amount + Unstaking[address] = nil + end + end + -- 处理投票 + for target, voteInfo in pairs(Votes) do + if currentHeight >= voteInfo.deadline then + if voteInfo.nay > voteInfo.yay then + -- 取消目标的质押 + local slashedAmount = Stakers[target] and Stakers[target].amount or 0 + Stakers[target].amount = 0 + end + -- 在处理结束后清理投票记录 + Votes[target] = nil + end + end +end +``` + +### 将handler附加到传入消息标签 + +这里我们添加了一个名为 `continue` 的辅助函数,它将允许我们在每条消息上执行到 `finalizationHandler`。 + +```lua +-- 包装函数以继续handler流程 +function continue(fn) + return function (msg) + local result = fn(msg) + if (result) == -1 then + return 1 + end + return result + end +end +``` + +最后,我们将注册所有handler并将它们包装在 `continue` 中,以便每个 Stake、Unstake 和 Vote 消息都会被 `finalizationHandler` 处理到。 + +```lua +-- 注册handler +Handlers.add("stake", + continue(Handlers.utils.hasMatchingTag("Action", "Stake")), Handlers.stake) +Handlers.add("unstake", + continue(Handlers.utils.hasMatchingTag("Action", "Unstake")), Handlers.unstake) +Handlers.add("vote", + continue(Handlers.utils.hasMatchingTag("Action", "Vote")), Handlers.vote) +-- 最终handler要被每一条消息调用 +Handlers.add("finalize", function (msg) return -1 end, finalizationHandler) +``` + +## 加载和测试 + +现在我们已经完成了 `dao.lua`,我们可以将它与 [token](./token.md) 指南中的 `token.lua` 一起加载到 aos 中。 运行一个名为 `dao` 的新 aos 进程,同时加载 `dao.lua` 和 `token.lua` + +```sh +aos dao --load token.lua --load dao.lua +``` + +从另一个终端运行另一个名为 voter 的 aos 进程 + +```sh +aos voter +``` + +现在从 dao aos shell 向投票者发送一些代币 + +```lua +aos> Send({ Target = ao.id, Tags = { Action = "Transfer", Recipient = 'process id of the voter aos', Quantity = '100000' }}) +``` + +从另一个终端运行另一个名为 cu 的 aos 进程 + +```sh +aos cu +``` + +现在从 dao aos shell 向 cu 发送一些代币 + +```lua +aos> Send({ Target = ao.id, Tags = { Action = "Transfer", Recipient = 'process id of the cu aos', Quantity = '100000' }}) +``` + +从 dao aos shell 检查余额,我们应该看到 voter 和 cu 进程的余额。 在下面的示例中, `bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s` 是 dao aos,`QcGIOXJ1p2SOGzGAccOcV-nSudVRiaPYbU7SjeLx1OE` 是投票者 aos,`X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s` 是 cu aos。 + +```lua +aos> Balances +{ + 'QcGIOXJ1p2SOGzGAccOcV-nSudVRiaPYbU7SjeLx1OE': 100000, + bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s: 99999999900000, + X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s: 100000 +} +aos> +``` + +从投票者 aos 进程中,质押一些代币 + +```lua +aos> Send({ Target = "bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s", Tags = { Action = "Stake", Quantity = '1000', UnstakeDelay = "10" }}) +``` + +从 cu aos 进程中,质押一些代币 + +```lua +aos> Send({ Target = "bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s", Tags = { Action = "Stake", Quantity = '1000', UnstakeDelay = "10" }}) +``` + +这意味着我们想要为 10 个区块投入 1000 个代币。 因此,10 个区块后我们就有能力取消质押。 + +从 dao aos shell 检查 Stakers 表的值 + +```lua +aos> Stakers +{ + 'QcGIOXJ1p2SOGzGAccOcV-nSudVRiaPYbU7SjeLx1OE': { amount: 1000, unstake_at: 1342634 }, + X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s: { amount: 1000, unstake_at: 1342634 } +} +aos> +``` + +现在让我们投票从投票者 aos 进程中削减cu,我们的投票在1个区块后生效 + +```lua +aos> Send({ Target = "bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s", Tags = { Action = "Vote", Target = "X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s(the cu aos)", Side = "nay", Deadline = "1" }}) +``` + +从 dao aos 检查投票 + +```lua +aos> Votes +{ + X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s: { nay: 1000, yay: 0, deadline: 1342627 } +} +aos> +``` + +现在等待 Arweave 达到截止时间块高度,然后从 dao aos 发送 Stake 消息以触发 `finalizationHandler`。 你可以在 [https://arweave.net/](https://arweave.net/) 检查块高度 + +```lua +Send({ Target = ao.id, Tags = { Action = "Stake", Quantity = '1000', UnstakeDelay = "10" }}) +``` + +现在检查投票和质押者,投票应该为空,cu aos 进程应该已经失去了质押。 + +```lua +aos> Votes +[] +aos> Stakers +{ + 'QcGIOXJ1p2SOGzGAccOcV-nSudVRiaPYbU7SjeLx1OE'(voter aos process): { amount: 1000, unstake_at: 1342647 }, + bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s(dao aos process): { amount: 1000, unstake_at: 1342658 }, + X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s(cu aos process): { amount: 0, unstake_at: 1342647 } +} +aos> +``` + +最后让我们从投票者 aos 进程中取消质押我们的代币 + +```lua +Send({ Target = "bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s", Tags = { Action = "Unstake", Quantity = '1000'}}) +``` + +并从 dao aos 检查 Stakers 表 + +```lua +aos> Stakers +{ + 'QcGIOXJ1p2SOGzGAccOcV-nSudVRiaPYbU7SjeLx1OE': { amount: 0, unstake_at: 1342647 }, + bclTw5QOm5soeMXoaBfXLvzjheT1_kwc2vLfDntRE4s: { amount: 1000, unstake_at: 1342658 }, + X6mkYwt87mIsfsQzDAJr54S0BBxLrDwWMdq7seBcS6s: { amount: 0, unstake_at: 1342647 } +} +aos> +``` + +DAO 指南到此结束,我们希望它对你有所帮助! diff --git a/src/zh/tutorials/begin/index.md b/src/zh/tutorials/begin/index.md new file mode 100644 index 0000000..cb034ef --- /dev/null +++ b/src/zh/tutorials/begin/index.md @@ -0,0 +1,32 @@ +--- +prev: + text: "教程" + link: "../index" +next: + text: "准备" + link: "./preparations" +--- + +# 开始:互动教程 + +在本教程系列中,你将完成交互式步骤,这将帮助你加深对 aos 环境的理解。 + +::: info + +### 练习 + +在这个有趣的练习中,你将遇到两个熟悉的角色 Morpheus 和 Trinity 提出的一系列挑战。 在 Morpheus 的引导下,你将深入 `兔子洞`,他会向你提出一系列挑战,以证明你就是 `那个人`。 一旦你完成了 Morpheus 和 Trinity 提出的所有挑战,你将收到一个 token ,该 token 允许你访问 ao 中名为 `The Construct` 的专属聊天室。 + +现在,让我们开始[深入兔子洞](./preparations)。 + +![白兔](./white_rabbit_outline.svg) +::: + +## 教程 + +### 入门 - 交互式教程 + +- [1. 快速入门](preparations) +- [2. 消息传递](messaging) +- [3. 创建聊天室](chatroom) +- [4. 创建 token ](token) diff --git a/src/zh/tutorials/begin/messaging.md b/src/zh/tutorials/begin/messaging.md new file mode 100644 index 0000000..82e9634 --- /dev/null +++ b/src/zh/tutorials/begin/messaging.md @@ -0,0 +1,197 @@ +# `ao` 中的消息传递 + +## 消息是如何为 `ao` 提供并行计算能力的 + +`ao` 是一个高度可扩展的环境,每个进程并行运行。每个进程独立且异步运行,所以在进程之间直接函数调用是不可行的。 + +我们使用异步通信来解决这个问题。 进程之间通过发送和接收消息,而不是直接相互调用函数。 这种方法允许灵活高效的交互,进程可以响应消息,增强系统的可扩展性和响应能力。 + +我们将首先探索 `aos` 中消息传递的基础知识,如何查看收件箱中收到的消息,以及如何将消息发送到其他进程。 + +## 视频教程 + + + +## 步骤 1:理解消息结构 + +- **消息基础知识:** `ao` 中的消息是使用 Lua 表构建的,Lua 表是可以保存多个值的通用数据结构。 在这些表中,`数据` 字段至关重要,因为它包含消息的内容或 `payload`。使用这种结构可以在进程之间高效地发送和接收信息,示例中展示了 `ao` 原语如何利用 Arweave 的底层功能进行复杂的、可组合的操作。 + + 规范详情请参考[G8way 规范页面](https://specs.g8way.io/?tx=xwOgX-MmqN5_-Ny_zNu2A8o-PnTGsoRb_3FrtiMAkuw)上的原始文档。 + +- **示例**:`{ Data = "Hello from Process A!" }` 是一条简单的消息。 + +## 步骤 2:打开 aos CLI + +- 在终端中输入 `aos` 并按 Enter 键,以启动 aos 命令行界面 (CLI)。 + +```sh +aos +``` + +## 步骤 3:如何发送消息 + +```sh + Send({ Target = "process ID", Data = "Hello World!" }) +``` + +- **Send**:`Send` 是 aos 中全局函数,用于发送消息。 +- **Target**:如果要将消息发送到特定进程,请在消息中包含 `Target` 字段。 +- **Data**:`Data` 是你希望目标进程接收的文本消息。 在此示例中,消息是 `Hello World!`。 + +## 步骤 4:存储 `Morpheus` 的进程 ID + +我们将使用下面提供的进程 ID 并将其存储为名为 `Morpheus` 的变量。 + +```sh +P2RS2VtQ4XtYEvAXYDulEA9pCBCIRpJDcakTR9aW434 +``` + +通过复制上面的进程 ID 并在 aos CLI 中运行以下命令以便将其存储为变量: + +```sh +Morpheus = "P2RS2VtQ4XtYEvAXYDulEA9pCBCIRpJDcakTR9aW434" +``` + +这会将进程 ID 存储为名为 `Morpheus` 的变量,从而更轻松地与特定进程 ID 进行交互。 + +::: info +创建 `Morpheus` 变量时,你应该看到的唯一响应是 `undefined`。 这是预料之中的。 要检查变量是否已成功创建,请输入 `Morpheus` 并按 Enter。 你应该会看到你存储的进程 ID。 +::: + +### 检查 `Morpheus` 变量 + +```sh +# 通过输入 `Morpheus` 检查 Morpheus 变量 +aos> Morpheus +# 预期结果: +P2RS2VtQ4XtYEvAXYDulEA9pCBCIRpJDcakTR9aW434 +aos> + +# 如果 `undefined` 被返回, +# 那么变量没有创建成功。 +``` + +## 步骤 5:向 Morpheus 发送消息 + +获取 Morpheus 的进程 ID 并将其存储在变量中后,你就可以与它进行通信了。 为此,你可以使用 `Send` 函数。 Morpheus 本身就是 ao 中运行的一个并行进程。 他使用一系列 handler 接收和发送消息。 让我们向他发送消息,看看会发生什么。 + +```lua +Send({ Target = Morpheus, Data = "Morpheus?" }) +``` + +- 你的 `Target` 是 `Morpheus`,这是我们之前使用 `Morpheus` 进程 ID 定义的变量。 +- `Data` 是你要发送给 Morpheus 的消息。 在这里,它是 `Morpheus?`。 + +**预期结果:** + +```sh +# 你的消息命令 +aos> Send({ Target = Morpheus, Data = "Morpheus?"}) +# 消息已添加到发件箱 +message added to outbox +# 从 `Morpheus` 的进程 ID 收到一条新消息 +New Message From P2R...434: Data = I am here. You are f +aos> +``` + +你已向 Morpheus 发送了一条消息并收到了回复,但你无法阅读完整的消息。 让我们了解 `收件箱` 以及如何阅读消息。 + +## 步骤 6:收件箱 + +`收件箱` 是你从其他进程接收消息的地方。 +::: info +要进一步深入了解收件箱的消息结构,请转到 [消息](../../concepts/messages) 概念页面。 +::: + +让我们检查你的收件箱,看看你收到了多少条消息。 + +在 aos CLI 中,键入以下命令: + +```sh +aos> #Inbox +``` + +如果你完全按照本教程进行操作,收件箱中不会有太多消息。 但是,如果你一直在尝试 aos 环境,则收件箱中可能会多于 1 条消息。 + +**返回值示范:** + +```sh +# 你的 `收件箱` 命令 +aos> #Inbox +# 该命令将返回你收件箱中的消息数量 +4 +aos> +``` + +在上面的示例中,返回为 `4`,表示收件箱中有四封邮件。 + +由于我们主要是为了寻找 `Morpheus` 的回复,因此我们假设他的消息是最后收到的消息。要阅读收件箱中的最后一条消息,请键入以下命令: + +```sh +aos> Inbox[#Inbox].Data +``` + +该命令允许你将数据与消息分离,并且仅读取特定数据字段的内容。 + +预期返回: + +```sh +# 你的 Inbox[x].Data 命令 +aos> Inbox[#Inbox].Data +# 该命令将返回消息的 `Data` 字段。 +# Data 通常代表基于文本的消息 +# 从一个进程接收到另一进程。 +I am here. You are finally awake. Are you ready to see how far the rabbit hole goes? +aos> +``` + +你现在正在使用自己的进程与 Morpheus 进行通信,Morpheus 是 ao 中运行的另一个并行进程。 你现在已准备好继续本教程的下一步。 + +## 步骤 7:发送带有标签的消息 + +**标签的用途**:aos 消息中的标签用于有效地分类、路由和处理消息。它们在消息处理中发挥着至关重要的作用,尤其是在处理多个进程或复杂的工作流程时。 + +某些进程仅与使用特定标签的消息进行交互的 `Handlers`。 例如,一个进程可能有一个 handler,仅与具有特定标签的消息交互,我们将在[聊天室](chatroom)教程中看到一个示例。 + +### 如何在消息中使用标签 + +就 Morpheus 而言,我们可以使用标签对消息进行分类,并且由于 Morpheus 是一个自治进程,因此他拥有可以与具有特定标签的消息进行交互的 handler。 + +**向消息添加标签**: + +- 我们已经知道消息的 `Data` 是你想要发送到另一个进程的文本的消息。此前,我们向 Morpheus 发送了一条没有任何标签的消息,Morpheus 进程使用 handler 响应该数据。 + +### 让 Morpheus 知道我们已经准备好了 + +向 Morpheus 发送一条带有标签 `Action`,Data 值包含 `rabbithole` 的消息。 + +**例子:** + +```lua +Send({ Target = Morpheus, Data = "Code: rabbithole", Action = "Unlock" }) +``` + +**预期返回:** +![Morpheus 的回应 2](./messaging2.png) + +### 使用标签的一些建议 + +- **一致的标记**:为你的应用程序开发一致的标记系统,使消息处理更具备规则。 +- **标签命名**:为标签选择清晰且具有描述性的名称。 让开发者一目了然地更容易理解消息的目的和上下文。 +- **标签安全**:请记住,标签未加密或隐藏,因此请避免使用敏感信息作为标签。 + +### 标签的高级用法 + +- **工作流程管理**:标签有助于管理工作流程,特别是在消息经过多个进程的系统中。 + +## 消息传递的其他建议 + +- **消息结构**:可以使用更多字段,如 `Epoch`、 `From` 和 `Nonce`,以满足更复杂的消息传递需求。 +- **调试**:使用 [`Dump`](/zh/concepts/tour#_6-使用-dump-进行数据展示) 函数打印消息以进行调试。 +- **安全注意事项**:谨慎对待消息的内容和处理,切勿发送任何私人或敏感的内容。 + +## 结论 + +现在你已经学习了如何发送带标签的消息,这是 aos 中用于分类和路由消息的强大工具。 + +墨菲斯正式邀请你进入下一阶段的旅程。 你现在已准备好继续本教程的下一步,[创建聊天室](chatroom)。 diff --git a/src/zh/tutorials/begin/messaging2.png b/src/zh/tutorials/begin/messaging2.png new file mode 100644 index 0000000..e0a3182 Binary files /dev/null and b/src/zh/tutorials/begin/messaging2.png differ diff --git a/src/zh/tutorials/begin/preparations.md b/src/zh/tutorials/begin/preparations.md new file mode 100644 index 0000000..2fbf656 --- /dev/null +++ b/src/zh/tutorials/begin/preparations.md @@ -0,0 +1,97 @@ +# 准备工作 + +::: info +**觉醒开始** + +你一直都知道,这个世界上还有更多你无法企及的事情。 你一直在寻找它,甚至不知道你在寻找什么。 它就是... `ao`。 + +我们通过安装 `aos` 客户端并启动新进程来开始我们的旅程。 这将使我们能够与 ao 计算机进行交互并完成本教程的其余部分。 + +::: + +## 视频教程 + + + +## 系统要求 + +aos 本地客户端安装非常简单。 只需确保你拥有: + +- [NodeJS](https://nodejs.org) 版本 20+. (如果你还没安装,查看[此网页](https://nodejs.org/en/download/package-manager) 找到适用于你的系统的安装说明)。 +- 一个称手的代码编辑器。 + +::: info +尽管不是必需的,但我们建议将 [ao 插件](../../references/editor-setup) 安装到你选择的文本编辑器中,以优化你使用 `aos` 的体验。 +::: + +## 安装 aos + +一旦你的机器上安装了 NodeJS,你所需要做的就是安装 aos 并运行它: + +```sh +npm i -g https://get_ao.g8way.io +``` + +安装完成后,我们运行命令即可启动一个新的 aos 进程! + +```sh +aos +``` + +## 欢迎来到兔子洞 + +你刚刚启动的实用程序是本地客户端,它已准备好将消息中继到 ao 计算机内的新进程。 + +连接后,你应该看到以下内容: + +```sh + _____ _______ _____ + /\ \ /::\ \ /\ \ + /::\ \ /:::\ \ /::\ \ + /:::\ \ /::::\ \ /:::\ \ + /::::\ \ /::::::\ \ /::::\ \ + /::/\::\ \ /::/~~\::\ \ /::/\::\ \ + /::/__\::\ \ /::/ \::\ \ /::/__\::\ \ + /:::\ \::\ \ /::/ / \::\ \ \::\ \::\ \ + /::::\ \::\ \ /::/____/ \::\____\ ___\::\ \::\ \ + /::/\::\ \::\ \ |::| | |::| | /\ \::\ \::\ \ +/::/ \::\ \::\____\|::|____| |::| |/::\ \::\ \::\____\ +\::/ \::\ /::/ / \::\ \ /::/ / \::\ \::\ \::/ / + \/____/ \::\/::/ / \::\ \ /::/ / \::\ \::\ \/____/ + \::::/ / \::\ /::/ / \::\ \::\ \ + \:::/ / \::\__/::/ / \::\ \::\____\ + /::/ / \::::::/ / \::\ /::/ / + /::/ / \::::/ / \::\/::/ / + /::/ / \:::/ / \::::/ / + /::/ / \::/____/ \:::/ / + \::/ / ~~ \::/ / + \/____/ \/____/ + +ao Operating System + +aos - 1.8.9 +2024 - Type ".exit" to exit +aos process: 1xM1_lDZ428sJHpTX7rtcR6SrDubyRVO06JEEWs_eWo + +aos> +``` + +让我们看一下运行 `aos` 后的初始打印输出: + +![aos print](./aos-print.png) + +在终端中运行 `aos` 后,你应该看到: + +- `AOS` 的 ASCII 艺术图像。 +- 一条欢迎信息。 +- 你正在运行的 `aos` 版本。 +- 一条退出的指令说明。 +- 你的进程 ID。 + +::: info +如果你的操作系统版本与最新版本不同,则会出现一条消息,询问你是否要更新版本。 此时,只需按两次 `Ctrl+C` 退出进程,运行 `npm i -g https://get_ao.g8way.io` 进行更新,然后再次运行 `aos`。 +::: + +欢迎来到你在 ao 计算机的新家! 你现在看到的提示是你在这台去中心化机器中自己的个人服务器。 + +现在,让我们通过探索 ao 的两个核心概念类型之一 [消息传递](messaging) 来进一步深入兔子洞。 diff --git a/src/zh/tutorials/begin/rabbithole.md b/src/zh/tutorials/begin/rabbithole.md new file mode 100644 index 0000000..a2268fd --- /dev/null +++ b/src/zh/tutorials/begin/rabbithole.md @@ -0,0 +1,90 @@ +# 进入 `The Construct` - 交互式教程 + +![白兔](./white_rabbit_outline.svg) + +## 醒来吧,尼奥…… + +你准备好看看兔子洞有多深了吗? + +这个交互式教程将利用你迄今为止所学到的知识并将其应用到任务中。 + +### 任务:冲出矩阵并进入 `The Construct` + +该结构是 ao 内的一个代币门控聊天室,只有完成一系列任务的人才能访问。 + +**现在...让我们开始吧。** + +::: warning +你必须安装最新版本的 aos 才能完成本教程。 +::: + +### 1. 找到墨菲斯 + +Morpheus 的进程 ID: + +```lua +9yOQrYNwIfIOeSswRDGUMd5gvMWJKxleInD_95DEC4A +``` + +用这个进程 ID 在 aos 里命名为 `Morpheus`。 这是进入 `The Construct` 的第一步。 + +```lua +Morpheus = "9yOQrYNwIfIOeSswRDGUMd5gvMWJKxleInD_95DEC4A" +``` + +向 Morpheus 发送消息,并告诉他你已准备好开始。 + +```lua +Send({ Target = Morpheus, Data = "I'm Ready" }) +``` + +当你发送此消息后,他将回复下一步。按照他给你的指示进行操作,你就会踏上前往 `The Construct` 的路上。 + +::: info +如果你需要帮助了解消息传递过程,请查看[消息传递](messaging)教程。 +::: + +### 2. 向墨菲斯证明自己 + +Morpehus会给你一系列任务来完成。这些任务将涉及: + +- 建立一个[聊天室](chatroom)。 +- 在聊天室中广播消息。 +- 为聊天室编写自定义handler。 + +当你完成这些任务后,墨菲斯会给你下一步的指示,其中将涉及找到 Trinity。 + +### 3.找到 Trinity + +Trinity 的进程ID只能通过完成 Morpheus 的任务来获得。 + +一旦你收到 Trinity 的进程 ID,你需要在 aos 中将其命名为 `Trinity`。 然后你会向她发送消息 `"White Rabbit"`。 + +```lua +Send({ Target = Trinity, Data = "White Rabbit" }) +``` + +她将做出回应,教程的下一阶段将开始。 + +### 4. 向 Trinity 证明自己 + +就像 Morpheus 一样,Trinity 会给你一系列任务来完成。 + +这些任务将涉及: + +- 创建一个[代币](token)。 +- 代币化你为 Morpheus 构建的聊天室。 +- 创建你自己的[游戏和机器人](../bots-and-games/index)。 +- 在代币化聊天室中注册你的进程。 + +完成这些任务后,Trinity 将为你提供本教程下一阶段的说明。 + +### 5. 接收进入 `the Construct` 的代币 + +通过完成 Morpheus 和 Trinity 的任务,你将收到一个代币,可以让你进入 `the Construct`。 + +### 6. 进入 `the Construct` + +然后 Trinity 将指导你如何使用该代币进入 `the Construct`。 + +进入 `the Construct` 后,你将能够与完成教程的其他人聊天。 diff --git a/src/zh/tutorials/begin/token.md b/src/zh/tutorials/begin/token.md new file mode 100644 index 0000000..5b87c0d --- /dev/null +++ b/src/zh/tutorials/begin/token.md @@ -0,0 +1,88 @@ +# 创建 token + +::: info +你现在已经准备好创建自己的 token ,token 是去中心化世界里价值和交换的象征。在学习如何创建 token 之前,请务必去学习下[消息传递](messaging)和[创建聊天室](chatroom) 课程。 +::: + +创建 token 时,我们将继续使用 `ao` 中的 [Lua 语言](../../references/lua.md) 来铸造一个 token ,并遵循 [token 规范](../../references/token.md) 中的原则。 + +## 继续深入兔子洞 + +在我们上一篇教程[创建聊天室](chatroom)中,我们学习了如何在 `ao` 中创建一个聊天室,邀请 `Morpheus` 和 `Trinity` 到我们创建的聊天室,然后 `Trinity` 现在要求我们为她创建一个 token ,以证明我们值得继续深入兔子洞。 + +**让我们开始吧。** + +## 创建 token 的两种途径 + +创建 token 时有两条路径可供选择: + +1. **使用蓝图(Bludprint)**:这是一个预制的模板,可以帮助你在 `ao` 中快速构建 token 。这是一种很好的入门方式,并且可以根据你的需求进行定制。 + + 点击此处了解有关[ token 蓝图](../../guides/aos/blueprints/token.md)的更多信息。 + +2. **手动创建**:这是从头开始在 `ao` 中构建 token 的分步指南。这条路径适合那些想要了解 token 内部运作以及如何从头开始构建 token 的人。 + + 请在此处查看完整的[创建 token ](../../guides/aos/token.md)指南。 + +## 蓝图方法 + +在本教程中,我们将使用 token 蓝图为 `Trinity` 创建 token 。 这是一个预先设计的模板,可帮助你在 `ao` 中快速构建 token 。 + +### 如何使用 token 蓝图 + +1. 确保你的终端运行在教程前面的步骤的同一目录中。 +2. 打开终端。 +3. 启动 `aos` 进程。 +4. 输入 `.load-blueprint token` + +这将加载 token 函数到 `ao` 的 handler。 请务必注意, 你也可以自行创建其他 token 代码取代蓝图代码。 + +### 验证蓝图是否已加载 + +输入 `Handlers.list` 以查看新加载的handler。 + +你应该看到已加载到 `aos` 进程中的新handler列表。 如果你一直遵循本教程中前面的步骤,除了 token handler 之外,你还应该看到聊天室的 handler。 + +**例子:** + +![ token handler](./token3.png) + +### 测试 token + +现在 token 蓝图已加载,我们可以通过使用 `Action = "Info"` 标签向自己发送消息来测试 token。 + +```sh +Send({ Target = ao.id, Action = "Info" }) +``` + +这将向控制台打印一条消息,但要读取该消息,我们需要从最新消息中调用 `.Data`。 + +```sh +Inbox[#Inbox].Data + +# 将 `#Inbox` 替换为最后收到的消息的编号。 +``` + +这会将 token 信息打印到控制台。它应该显示你的可用 token 的总余额。 + +### 向 Trinity 发送 token + +现在我们已经测试了 token 并且它按预期工作,我们可以将一些 token 发送到 `Trinity`。 我们将使用 `Action = "Transfer"` 标签向 `Trinity` 发送 1000 个 token 。 + +```sh +Send({ Target = ao.id, Action = "Transfer", Recipient = Trinity, Quantity = "1000"}) +``` + +当 `Trinity` 收到 token 时,她将用一条消息响应此转账,以确认她已收到 token 。 + +她的回应看起来像这样: + +`Trinity:` "Token received. Interesting. I wasn't sure you'd make it this far. I'm impressed, but we are not done yet. I want you to use this token to tokengate the chatroom. Do that, and then I will believe you could be the one." + +使用下列命令查看 Trinity 的消息: + +```sh +Inbox[#Inbox].Data +``` + +你已经完成了创建 token 并将其发送给 `Trinity`。 你现在已准备好继续下一步。 [token 门控聊天室](tokengating)。 diff --git a/src/zh/tutorials/begin/token1.png b/src/zh/tutorials/begin/token1.png new file mode 100644 index 0000000..b3c39c7 Binary files /dev/null and b/src/zh/tutorials/begin/token1.png differ diff --git a/src/zh/tutorials/begin/token2.png b/src/zh/tutorials/begin/token2.png new file mode 100644 index 0000000..f5a4985 Binary files /dev/null and b/src/zh/tutorials/begin/token2.png differ diff --git a/src/zh/tutorials/begin/token3.png b/src/zh/tutorials/begin/token3.png new file mode 100644 index 0000000..6eb9233 Binary files /dev/null and b/src/zh/tutorials/begin/token3.png differ diff --git a/src/zh/tutorials/begin/tokengating.md b/src/zh/tutorials/begin/tokengating.md new file mode 100644 index 0000000..c10b050 --- /dev/null +++ b/src/zh/tutorials/begin/tokengating.md @@ -0,0 +1,133 @@ +# 支持 token 的聊天室 + +::: info +现在我们已经创建了一个 token 并将其发送到 `Trinity`,我们可以使用该 token 来对我们的聊天室进行控制:只允许拥有 token 的人进入聊天室。 +::: + +## 如何创建支持 token 的聊天室 + +让我们创建一个 handler,允许我们对聊天室进行准入控制。该 handler 将响应标签 `Action = "Broadcast"`,这意味着它将替换我们原始的 `Broadcast` handler。 + +## 步骤 1:启动相同的 `aos` 进程 + +确保你使用的 `aos` 进程与你在整个教程中使用的进程相同。 + +## 步骤2:打开 `chatroom.lua` 文件 + +这与我们在 [聊天室](chatroom) 教程中用于创建聊天室的文件相同。 + +## 步骤 3:编辑你的 `Broadcast` handler + +将原来的 `Broadcast` handler替换为以下代码: + +```lua +Handlers.add( + "Broadcast", + Handlers.utils.hasMatchingTag("Action", "Broadcast"), + function(m) + if tonumber(Balances[m.From]) < 1 then + print("UNAUTH REQ: " .. m.From) + return + end + local type = m.Type or "Normal" + print("Broadcasting message from " .. m.From .. ". Content: " .. m.Data) + for i = 1, #Members, 1 do + ao.send({ + Target = Members[i], + Action = "Broadcasted", + Broadcaster = m.From, + Data = m.Data + }) + end + end +) +``` + +该 handler 现在将在将消息广播到聊天室之前会检查发送者 token 的余额。 如果发送者没有 token ,则消息将不会被广播。 + +保存文件。 + +## 步骤 4:重新加载 `chatroom.lua` 文件 + +要将原始的 `Broadcast` handler 替换为新的 handler,你需要重新加载 `chatroom.lua` 文件。 + +```sh +.load chatroom.lua +``` + +## 步骤 5:测试支持 token 的聊天室 + +现在聊天室已经是支持 token 的聊天室,让我们通过向聊天室发送消息来测试它。 + +### 来自原来的 aos 进程 + +首先,我们将从原始的 aos 进程进行测试。 + +```sh +Send({ Target = ao.id , Action = "Broadcast", Data = "Hello" }) +# 预期结果: +message added to outbox +Broadcasting message from Neo. Content: Hello. +``` + +记住,当你不知道怎么查看最新的消息的时,一定使用: + +```sh +Inbox[#Inbox].Data +``` + +## 用另一个进程 ID 进行测试 + +另外打开一个终端创建新的进程: + +```sh +aos name1 +``` + +现在,让我们使用这个没有 token 的新 aos 进程来测试。 + +我们首先需要注册到聊天室。 + +```sh +Send({ Target = [聊天室的 Process ID], Action = "Register" }) +# 预期结果: +message added to outbox +New Message From [Your Process ID]: Data = registered +``` + +现在,让我们尝试向聊天室发送消息。 + +```sh +Send({ Target = [聊天室的 Process ID], Action = "Broadcast", Data = "Hello?" }) +# 预期结果: +message added to outbox +UNAUTH REQ: [New Process ID] +``` + +如你所见,该消息未广播,因为新进程没有 token 。 + +## 告诉 Trinity "It is done" + +回到原来的聊天室 aos 进程中,向聊天室发送一条广播消息,表示“已完成”。 + +```lua +Send({ Target = ao.id , Action = "Broadcast", Data = "It is done" }) +``` + +::: warning +了解精确匹配数据和区分大小写非常重要。 如果你没有收到 Morpheus 或 Trinity 的回复,请务必检查你的数据和标签的内容。 +::: + +然后 Trinity 将响应聊天室已经成功完成 token 准入控制。 + +### 预期结果 + +Trinity 会发送一条消息说:"I guess Morpheus was right. You are the one. Consider me impressed. You are now ready to join The Construct, an exclusive chatroom available to only those that have completed this tutorial. Now, go join the others by using the same tag you used `Register`, with this process ID: [Construct Process ID]. Good luck." + +## 结论 + +你做到了! 你已成功对聊天室进行 token 控制。现在已经解锁了对 `Construct` 的访问,只有那些完全完成本教程的人才能进入。 + +### 恭喜你! + +你已经表现出了巨大的潜力。 我希望你喜欢本教程。 现在你已准备好在 `ao` 中自由构建。 diff --git a/src/zh/tutorials/begin/white_rabbit_outline.svg b/src/zh/tutorials/begin/white_rabbit_outline.svg new file mode 100644 index 0000000..a1253b8 --- /dev/null +++ b/src/zh/tutorials/begin/white_rabbit_outline.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/zh/tutorials/bots-and-games/announcements.md b/src/zh/tutorials/bots-and-games/announcements.md new file mode 100644 index 0000000..1b7238e --- /dev/null +++ b/src/zh/tutorials/bots-and-games/announcements.md @@ -0,0 +1,58 @@ +# 解析公告 + +欢迎回到你的编码之旅。 是时候使用你从之前的教程中获得的技能来增强你的游戏体验了。 + +在游戏过程中,你可能会注意到终端中出现的公告。这些公告是游戏向玩家传达重要事件的方式。然而,这些消息有时看起来很神秘,或者你可能会发现自己得反复检查收件箱才能获取更多详细信息。 + +直接从你的终端访问这些信息不是很方便吗? 嗯,有一种方法可以做到这一点! + +通过使用 [handlers](../../references/handlers.md),你可以创建一个自主代理来为你读取这些信息,标志着从简单的机器人到能够直接解析游戏事件并对其采取行动的实体的进化。 + +## 设置开发环境 + +首先在你的首选目录中创建一个名为 `bot.lua` 的新文件。 + +> 理想情况下,该文件应放置在 player 进程的同一目录中,以简化代码的加载。 否则,你需要使用相对路径来访问该文件。 + +## 编写代码 + +让我们深入研究下其中的逻辑。 + +aos 中的每个handler都需要三个关键信息: + +- `name`:handler 的唯一名称 +- `pattern`: handler 识别的模式,触发其执行 +- `handle`:模式匹配时执行的操作 + +以下是编写用于打印公告详细信息的 handler 的方法: + +```lua +-- 直接在终端中打印游戏公告的handler。 +Handlers.add( + "PrintAnnouncements", + Handlers.utils.hasMatchingTag("Action", "Announcement"), + function (msg) + print(msg.Event .. ": " .. msg.Data) + end +) +``` + +在本例中,handler的名称是 `"PrintAnnouncements"`。 它使用特殊的内置实用程序(`hasMatchingTags`)检查传入消息是否已被标记为公告。 如果为 true,则 handler 将打印事件和数据,则直接显示公告的标题和描述。 + +> 注意:一旦消息被 `handled`,它将从你的 `收件箱` 中丢弃。 + +## 加载和测试 + +现在,让我们在游戏中实现这一点。 + +导航到你的 aos 玩家终端并进入游戏会话。 + +用下面的命令来加载 `bot.lua` 文件以激活handler: + +```lua +.load bot.lua +``` + +现在,你将看到游戏公告直接出现在你的终端中,提供实时展示,且无需手动查看收件箱。 + +恭喜! 你刚刚迈出了在 `aos` 上构建机器人的第一步。 但让我们继续努力为其添加更多功能 🌐 diff --git a/src/zh/tutorials/bots-and-games/ao-effect-game-banner.png b/src/zh/tutorials/bots-and-games/ao-effect-game-banner.png new file mode 100644 index 0000000..1ecaa25 Binary files /dev/null and b/src/zh/tutorials/bots-and-games/ao-effect-game-banner.png differ diff --git a/src/zh/tutorials/bots-and-games/ao-effect.md b/src/zh/tutorials/bots-and-games/ao-effect.md new file mode 100644 index 0000000..0ecf905 --- /dev/null +++ b/src/zh/tutorials/bots-and-games/ao-effect.md @@ -0,0 +1,127 @@ +--- +prev: + text: "Bots and Games" + link: "./index" +--- + +# 我们来玩个游戏吧! + +你是冠军,你通过教程获得了力量! 现在,让我们休息一下,开始一些令人兴奋的事情。 怎么样?我们来一款能为你的学习之旅增添乐趣的游戏! + +![AO-效应游戏横幅](./ao-effect-game-banner.png) + +## 什么样的游戏? + +`ao-effect` 是一款游戏,你可以直接从你的终端与全球好友进行实时竞赛。我们为这次冒险建立了一个全球游戏进程。 + +规则很简单。 每个玩家一开始都在 40x40 的网格上,生命值为 100,能量为 0。随着时间的推移,你的能量会补充到最大 100。在网格中导航,找到其他玩家,在他们处于攻击范围内时,使用你的能量进行攻击。战斗将持续进行,直到只剩下一名玩家或规定的游戏结束时间。 + +查看[竞技场机制](arena-mechanics.md) 和[扩展竞技场](build-game.md) 指南,以更深入地了解游戏。 + +> 注意:如果某些命令语法看起来不熟悉,请不要担心。专注于在更高层次上的目标,理解每个命令的目的,最重要的是,享受游戏! + +## 准备 `ao-effect` 的冒险 + +要加入这场全球冒险活动,你需要做好一些准备。 别担心,就像数数 1-2-3 一样简单! + +1.**安装 aos** +启动你的终端并运行: + +```bash +npm i -g https://get_ao.g8way.io +``` + +2.**启动 aos** +接下来,创建 aos 实例: + +```bash +aos +``` + +3.**设置游戏ID** +让我们保留我们的游戏服务器 ID 以便快速访问: + +```lua +Game = "0rVZYFxvfJpO__EfOz0_PUQ3GFE9kEaES0GkUDNXjvE" +``` + +瞧! 你已准备好加入游戏。 + +## 如何注册游戏 + +准备好加入了吗? 只需几个简单的步骤: + +### 注册游戏服务器 + +`ao` 中进程之间的所有通信都是通过消息进行的。要注册,请将此消息发送到游戏服务器: + +```lua +Send({ Target = Game, Action = "Register" }) +``` + +这会将你置于 `Waiting` 大厅。需要支付少量费用来确认你的位置。 + +### 确认你的位置 + +为了确认你的位置,你需要一些代币。你可以通过向游戏发送以下消息来获取它们: + +```lua +Send({ Target = Game, Action = "RequestTokens"}) +``` + +收到代币后,请通过支付游戏入场费来确认你的位置,如下所示: + +```lua +Send({ Target = Game, Action = "Transfer", Recipient = Game, Quantity = "1000"}) +``` + +等待几秒钟,你将在终端中看到有关玩家付款和状态的实时更新。 + +## 让游戏开始 + +### 游戏机制 + +游戏开始:如果至少有 2 名玩家付款,则游戏在 2 分钟 `WaitTime` 后开始。不付费的玩家将被踢除。如果没有足够的玩家付费,那么付费的玩家将获得退款。 + +游戏开始后,玩家会随机的落到网格点上。 + +### 该你移动了 + +移动:你能做的第一件事就是移动,不需要能量!你可以向任何方向移动一个方块——上、下、左、右或对角线。除了方向之外,你还必须传递你的玩家 ID,以帮助游戏识别你的移动。就是这样: + +```lua +Send({ Target = Game, Action = "PlayerMove", Player = ao.id, Direction = "DownRight"}) +``` + +网格上可用的移动如下: + +```lua +Up = {x = 0, y = -1}, +Down = {x = 0, y = 1}, +Left = {x = -1, y = 0}, +Right = {x = 1, y = 0}, +UpRight = {x = 1, y = -1}, +UpLeft = {x = -1, y = -1}, +DownRight = {x = 1, y = 1}, +DownLeft = {x = -1, y = 1} +``` + +> 请记住:方向区分大小写! + +如果你离开网格,你就会出现在对面。 + +### 出击时间到了 + +发起攻击:随着游戏的进行,你会积累能量。 用它攻击 3x3 网格范围内的其他玩家。 你的攻击不会伤害你自己,但会影响范围内的其他人。 + +```lua +Send({ Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = "energy_integer"}) +``` + +生命值从 100 开始,并随着其他玩家的攻击而减少。 达到 0 时,游戏就结束了。 + +## 总结一下 + +当只剩下一名玩家或游戏时间到时,游戏结束。获胜者将获得奖励,然后返回大厅进行另一轮比赛。 + +喜欢这个游戏吗?如果有一种方法可以让你的体验变得更好或提高你获胜的几率,该怎么办? 查看下一篇指南来了解一下🤔 diff --git a/src/zh/tutorials/bots-and-games/arena-mechanics.md b/src/zh/tutorials/bots-and-games/arena-mechanics.md new file mode 100644 index 0000000..ec6b1c1 --- /dev/null +++ b/src/zh/tutorials/bots-and-games/arena-mechanics.md @@ -0,0 +1,404 @@ +# 竞技场机制 + +本指南全面概述了在 `aos` 中设计和管理竞技场类型游戏所必需的基本机制。在竞技场游戏中,参与者进行回合比赛,有策略地相互竞争以消灭对方,直到出现唯一的胜利者。 + +这里介绍的框架为制作各种游戏奠定了基础,所有游戏都共享相同的核心功能。 探索游戏开发的复杂性,并在这个多功能的舞台上释放你的创造力。 + +## 核心功能 + +现在,让我们深入了解竞技场风格游戏的核心功能: + +1. **游戏进展模式:** + + 竞技场游戏被打造为循环运行的回合,具有以下进展模式:`"Not-Started"` → `"Waiting"` → `"Playing"` → `[Someone wins or timeout]` → `"Waiting"`... + + > 注意:如果等待状态后没有足够的玩家开始游戏,则循环超时。 + + 回合为玩家提供了明确的参与时间范围,从而增强了游戏的刺激性。 + +2. **代币质押:** + + 玩家必须存入指定数量的代币(由 `PaymentQty` 定义)才能参与游戏。 这些代币为游戏添加了有形的赌注元素。 + +3. **奖金奖励:** + + 除了胜利的兴奋之外,玩家还被额外奖励的前景所吸引。 构建者可以灵活地提供由 `BonusQty` 定义的奖励代币,每轮分配。 玩家所下的任何赌注也会添加到这些奖金中。 这些奖金作为额外的激励,增强了游戏的竞争精神。 + +4. **玩家管理:** + + - 等待加入下一场比赛的玩家会在 `Waiting` 表中进行跟踪。 + - 正在比赛的玩家及其游戏状态存储在 `Players` 表中。 + - 被淘汰的玩家会立即从 `Players` 表中删除,并放入 `Waiting` 表中进行下一场比赛。 + +5. **每轮获胜者奖励:** + + 当一个玩家淘汰另一个玩家时,他们不仅可以获得吹牛的权利,还可以获得被淘汰玩家的质押代币作为奖励。 此外,每轮的获胜者都会分享一部分奖金代币以及他们的原始质押的代币,进一步激励玩家争取胜利。 + +6. **监听器模式:** + + 对于那些喜欢观看行动展开的人来说,`Listen` 模式提供了一个无需实际参与即可了解情况的机会。 进程可以注册为侦听器,授予它们访问游戏中所有公告的权限。 虽然他们不作为玩家参与,但听众可以继续观察游戏的进度,除非他们明确要求删除。 + +7. **游戏状态管理:** + + 为了维持竞技场游戏的流畅性和公平性,自动化系统会监督游戏状态的转换。 这些转换包括等待、游戏中和结束阶段。 每个状态的持续时间(例如 `WaitTime` 和 `GameTime`)可确保回合遵守定义的时间范围,从而防止游戏无限期地持续。 + +你可以在下面的下拉展开块中参考竞技场的代码: + +
+ 竞技场游戏蓝图 + +```lua + +-- 竞技场游戏蓝图。 + +-- 该蓝图提供了在 ao 进程内运行 `竞技场` 风格游戏的框架。 +-- 游戏以回合形式进行,玩家的目标是互相消灭,直到只剩下一个,或者直到比赛时间结束。 +-- 游戏进程会随着玩家的加入和离开而无限循环。 + +-- 当一个玩家淘汰另一个玩家时,他们会收到被淘汰玩家的质押代币作为奖励。 +-- 此外,建造者可以提供这些代币的奖励作为每轮额外的激励分配。 +-- 如果游戏中的目标玩家类型是机器人,提供额外的`奖励`创造让程序员争相生产最佳代理来`挖`到进程的代币的机会 + +-- 建造者还可以在他们的游戏逻辑中控制框架, 提供类似这样的handler:允许玩家执行游戏中的动作,在适当的时刻调用 `eliminatePlayer()`。 + +-- 进程还可以在 `监听` 模式下注册,在该模式下它们将接收游戏中的所有公告,但他们自己不加入本轮对战。 +-- 除非他们明确要求,否则他们也不会取消注册。 + +-- 全局变量。 + +-- 一轮循环包含的游戏进度模式: + +-- [Not-Started] -> Waiting -> Playing -> [Someone wins or timeout] -> Waiting... +-- 在等待状态之后如果还没有足够玩家则此循环结束。 +GameMode = GameMode or "Not-Started" +StateChangeTime = StateChangeTime or undefined + +-- 状态持续时间 (毫秒) +WaitTime = WaitTime or 2 * 60 * 1000 -- 2 分钟 +GameTime = GameTime or 20 * 60 * 1000 -- 20 分钟 +Now = Now or undefined -- 当前时间,每条消息更新一次。 + +-- 玩家质押的代币信息。 +UNIT = 1000 +PaymentToken = PaymentToken or "ADDR" -- 代币地址 +PaymentQty = PaymentQty or tostring(math.floor(UNIT)) -- 注册需要的代币数量 +BonusQty = BonusQty or tostring(math.floor(UNIT)) -- 赢家的代币奖金数量 + +-- 等待进入下一轮游戏的玩家及其支付状态。 +Waiting = Waiting or {} +-- 已激活玩家及其状态。 +Players = Players or {} +-- 当前游戏的赢家数量。 +Winners = 0 +-- 订阅了游戏公告的进程。 +Listeners = Listeners or {} +-- 开始一个游戏的最小玩家数。 +MinimumPlayers = MinimumPlayers or 2 + +-- 玩家默认状态初始化。 +PlayerInitState = PlayerInitState or {} + +-- 向所有注册的侦听器发送状态更改公告。 +-- @param event: 事件类型或名称。 +-- @param description: 事件描述。 +function announce(event, description) + for ix, address in pairs(Listeners) do + ao.send({ + Target = address, + Action = "Announcement", + Event = event, + Data = description + }) + end + return print(Colors.gray .. "Announcement: " .. Colors.red .. event .. " " .. Colors.blue .. description .. Colors.reset) +end + +-- 给玩家发送奖励。 +-- @param recipient: 获得奖励的玩家。 +-- @param qty: 奖励数量。 +-- @param reason: 奖励原因。 +function sendReward(recipient, qty, reason) + if type(qty) ~= number then + qty = tonumber(qty) + end + ao.send({ + Target = PaymentToken, + Action = "Transfer", + Quantity = tostring(qty), + Recipient = recipient, + Reason = reason + }) + return print(Colors.gray .. "Sent Reward: " .. + Colors.blue .. tostring(qty) .. + Colors.gray .. ' tokens to ' .. + Colors.green .. recipient .. " " .. + Colors.blue .. reason .. Colors.reset + ) +end + +-- 开始玩家准备玩游戏的倒计时。 +function startWaitingPeriod() + GameMode = "Waiting" + StateChangeTime = Now + WaitTime + announce("Started-Waiting-Period", "The game is about to begin! Send your token to take part.") + print('Starting Waiting Period') +end + +-- 如果有足够的玩家,则开始游戏。 +function startGamePeriod() + local paidPlayers = 0 + for player, hasPaid in pairs(Waiting) do + if hasPaid then + paidPlayers = paidPlayers + 1 + end + end + + if paidPlayers < MinimumPlayers then + announce("Not-Enough-Players", "Not enough players registered! Restarting...") + for player, hasPaid in pairs(Waiting) do + if hasPaid then + Waiting[player] = false + sendReward(player, PaymentQty, "Refund") + end + end + startWaitingPeriod() + return + end + + LastTick = undefined + GameMode = "Playing" + StateChangeTime = Now + GameTime + for player, hasPaid in pairs(Waiting) do + if hasPaid then + Players[player] = playerInitState() + else + ao.send({ + Target = player, + Action = "Ejected", + Reason = "Did-Not-Pay" + }) + removeListener(player) -- 如果玩家未付款,则将其从监听器中删除 + end + end + announce("Started-Game", "The game has started. Good luck!") + print("Game Started....") +end + +-- 从游戏中淘汰玩家的handler。 +-- @param eliminated: 要被淘汰的玩家。 +-- @param eliminator: 发起淘汰的玩家。 +function eliminatePlayer(eliminated, eliminator) + sendReward(eliminator, PaymentQty, "Eliminated-Player") + Waiting[eliminated] = false + Players[eliminated] = nil + + ao.send({ + Target = eliminated, + Action = "Eliminated", + Eliminator = eliminator + }) + + announce("Player-Eliminated", eliminated .. " was eliminated by " .. eliminator .. "!") + + local playerCount = 0 + for player, _ in pairs(Players) do + playerCount = playerCount + 1 + end + print("Eliminating player: " .. eliminated .. " by: " .. eliminator) -- 对于跟踪淘汰很有用 + + if playerCount < MinimumPlayers then + endGame() + end + +end + +-- 结束当前游戏并开始一个新的。 +function endGame() + print("Game Over") + + Winners = 0 + Winnings = tonumber(BonusQty) / Winners -- 计算每位玩家的奖金 + + for player, _ in pairs(Players) do + Winners = Winners + 1 + end + + Winnings = tonumber(BonusQty) / Winners + + for player, _ in pairs(Players) do + -- addLog("EndGame", "Sending reward of:".. Winnings + PaymentQty .. "to player: " .. player) -- Useful for tracking rewards + sendReward(player, Winnings + tonumber(PaymentQty), "Win") + Waiting[player] = false + end + + Players = {} + announce("Game-Ended", "Congratulations! The game has ended. Remaining players at conclusion: " .. Winners .. ".") + startWaitingPeriod() +end + +-- 从监听器列表移除一个监听器。 +-- @param listener: 待移除的监听器。 +function removeListener(listener) + local idx = 0 + for i, v in ipairs(Listeners) do + if v == listener then + idx = i + break + end + end + if idx > 0 then + table.remove(Listeners, idx) + end +end + +-- handler: 游戏状态管理 + +-- 定时消息handler,管理游戏状态切换。 +Handlers.add( + "Game-State-Timers", + function(Msg) + return "continue" + end, + function(Msg) + Now = Msg.Timestamp + if GameMode == "Not-Started" then + startWaitingPeriod() + elseif GameMode == "Waiting" then + if Now > StateChangeTime then + startGamePeriod() + end + elseif GameMode == "Playing" then + if onTick and type(onTick) == "function" then + onTick() + end + if Now > StateChangeTime then + endGame() + end + end + end +) + +-- 玩家质押以参与下一轮游戏的handler。 +Handlers.add( + "Transfer", + function(Msg) + return + Msg.Action == "Credit-Notice" and + Msg.From == PaymentToken and + tonumber(Msg.Quantity) >= tonumber(PaymentQty) and "continue" + end, + function(Msg) + Waiting[Msg.Sender] = true + ao.send({ + Target = Msg.Sender, + Action = "Payment-Received" + }) + announce("Player-Ready", Msg.Sender .. " is ready to play!") + end +) + +-- 为下轮游戏注册新玩家并为其订阅事件信息。 +Handlers.add( + "Register", + Handlers.utils.hasMatchingTag("Action", "Register"), + function(Msg) + if Msg.Mode ~= "Listen" and Waiting[Msg.From] == undefined then + Waiting[Msg.From] = false + end + removeListener(Msg.From) + table.insert(Listeners, Msg.From) + ao.send({ + Target = Msg.From, + Action = "Registered" + }) + announce("New Player Registered", Msg.From .. " has joined in waiting.") + end +) + +-- 注销玩家并停止向他们发送事件信息。 +Handlers.add( + "Unregister", + Handlers.utils.hasMatchingTag("Action", "Unregister"), + function(Msg) + removeListener(Msg.From) + ao.send({ + Target = Msg.From, + Action = "Unregistered" + }) + end +) + +-- 将投注金额添加到 BonusQty +Handlers.add( + "AddBet", + Handlers.utils.hasMatchingTag("Reason", "AddBet"), + function(Msg) + BonusQty = tonumber(BonusQty) + tonumber(Msg.Tags.Quantity) + announce("Bet-Added", Msg.From .. "has placed a bet. " .. "BonusQty amount increased by " .. Msg.Tags.Quantity .. "!") + end +) + +-- 检索当前游戏状态。 +Handlers.add( + "GetGameState", + Handlers.utils.hasMatchingTag("Action", "GetGameState"), + function (Msg) + local json = require("json") + local TimeRemaining = StateChangeTime - Now + local GameState = json.encode({ + GameMode = GameMode, + TimeRemaining = TimeRemaining, + Players = Players, + }) + ao.send({ + Target = Msg.From, + Action = "GameState", + Data = GameState}) + end +) + +-- 提醒用户每个游戏状态的剩余时间。 +Handlers.add( + "AnnounceTick", + Handlers.utils.hasMatchingTag("Action", "Tick"), + function (Msg) + local TimeRemaining = StateChangeTime - Now + if GameMode == "Waiting" then + announce("Tick", "The game will start in " .. (TimeRemaining/1000) .. " seconds.") + elseif GameMode == "Playing" then + announce("Tick", "The game will end in " .. (TimeRemaining/1000) .. " seconds.") + end + end +) + +-- 根据请求向没有余额的玩家发送代币 +Handlers.add( + "RequestTokens", + Handlers.utils.hasMatchingTag("Action", "RequestTokens"), + function (Msg) + print("Transfering Tokens: " .. tostring(math.floor(10000 * UNIT))) + ao.send({ + Target = ao.id, + Action = "Transfer", + Quantity = tostring(math.floor(10000 * UNIT)), + Recipient = Msg.From, + }) + end +) +``` + +
+ +## 竞技场游戏蓝图 + +对于那些有兴趣使用此 arena 框架的人,我们已通过蓝图轻松访问此代码。 只需在终端中运行以下代码: + +```lua +.load-blueprint arena +``` + +## 总结 + +了解竞技场的机制不仅可以帮助你改进上一节中创建的自主代理,还可以让你利用核心功能来打造独特的游戏。 + +在接下来的 `Building a Game` 部分中,我们将深入探讨利用这些机制在此框架内构建迷人且独一无二的游戏的艺术。 准备好踏上游戏开发动态领域的旅程吧! 🎮 diff --git a/src/zh/tutorials/bots-and-games/attacking.md b/src/zh/tutorials/bots-and-games/attacking.md new file mode 100644 index 0000000..8cc13c5 --- /dev/null +++ b/src/zh/tutorials/bots-and-games/attacking.md @@ -0,0 +1,130 @@ +# 自动响应 + +根据我们的[上一个指南](decisions),我们的创作已经从一个简单的机器人发展成为一个复杂的自主代理。 现在,让我们进一步增强它的能力,添加反击功能,让它能够立即对对手的攻击进行反击,有可能在他们撤退到安全地带之前打他们个措手不及。 + +## 编写代码 + +将以下handler添加到你的 `bot.lua` 文件中即可: + +```lua +-- 被其他玩家击中时自动攻击的handler。 +Handlers.add( + "ReturnAttack", + Handlers.utils.hasMatchingTag("Action", "Hit"), + function (msg) + local playerEnergy = LatestGameState.Players[ao.id].energy + if playerEnergy == undefined then + print("Unable to read energy.") + ao.send({Target = Game, Action = "Attack-Failed", Reason = "Unable to read energy."}) + elseif playerEnergy == 0 then + print("Player has insufficient energy.") + ao.send({Target = Game, Action = "Attack-Failed", Reason = "Player has no energy."}) + else + print("Returning attack.") + ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(playerEnergy)}) + end + InAction = false + ao.send({Target = ao.id, Action = "Tick"}) + end +) +``` + +每当你的玩家受到攻击时,你都会收到一条包含 `Hit` 动作的消息。 这种设置可确保你的代理在拥有足够能量的情况下能够进行快速反击。 + +你可以在下面的下拉展开块中参考 `bot.lua` 的最新代码: + +
+ 更新后的 bot.lua 文件 + +```lua +LatestGameState = LatestGameState or nil + +function inRange(x1, y1, x2, y2, range) + return math.abs(x1 - x2) <= range and math.abs(y1 - y2) <= range +end + +function decideNextAction() + local player = LatestGameState.Players[ao.id] + local targetInRange = false + + for target, state in pairs(LatestGameState.Players) do + if target ~= ao.id and inRange(player.x, player.y, state.x, state.y, 1) then + targetInRange = true + break + end + end + + if player.energy > 5 and targetInRange then + print("Player in range. Attacking.") + ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(player.energy)}) + else + print("No player in range or insufficient energy. Moving randomly.") + local directionMap = {"Up", "Down", "Left", "Right", "UpRight", "UpLeft", "DownRight", "DownLeft"} + local randomIndex = math.random(#directionMap) + ao.send({Target = Game, Action = "PlayerMove", Player = ao.id, Direction = directionMap[randomIndex]}) + end +end + +Handlers.add( + "HandleAnnouncements", + Handlers.utils.hasMatchingTag("Action", "Announcement"), + function (msg) + ao.send({Target = Game, Action = "GetGameState"}) + print(msg.Event .. ": " .. msg.Data) + end +) + +Handlers.add( + "UpdateGameState", + Handlers.utils.hasMatchingTag("Action", "GameState"), + function (msg) + local json = require("json") + LatestGameState = json.decode(msg.Data) + ao.send({Target = ao.id, Action = "UpdatedGameState"}) + end +) + +Handlers.add( + "decideNextAction", + Handlers.utils.hasMatchingTag("Action", "UpdatedGameState"), + function () + if LatestGameState.GameMode ~= "Playing" then + return + end + print("Deciding next action.") + decideNextAction() + end +) + +Handlers.add( + "ReturnAttack", + Handlers.utils.hasMatchingTag("Action", "Hit"), + function (msg) + local playerEnergy = LatestGameState.Players[ao.id].energy + if playerEnergy == undefined then + print("Unable to read energy.") + ao.send({Target = Game, Action = "Attack-Failed", Reason = "Unable to read energy."}) + elseif playerEnergy == 0 then + print("Player has insufficient energy.") + ao.send({Target = Game, Action = "Attack-Failed", Reason = "Player has no energy."}) + else + print("Returning attack.") + ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(playerEnergy)}) + end + InAction = false + ao.send({Target = ao.id, Action = "Tick"}) + end +) +``` + +
+ +## 加载和测试 + +要激活并测试反击功能,请在你的 aos 玩家终端中加载机器人文件: + +```lua +.load bot.lua +``` + +观察你的终端里自主代理的响应,现在增加了立即报复的能力。 此功能展示了代理不断发展的战略深度和自主权。 在接下来的部分中,我们将巩固迄今为止收集的所有知识,并添加一些优化功能。 diff --git a/src/zh/tutorials/bots-and-games/bringing-together.md b/src/zh/tutorials/bots-and-games/bringing-together.md new file mode 100644 index 0000000..a213149 --- /dev/null +++ b/src/zh/tutorials/bots-and-games/bringing-together.md @@ -0,0 +1,193 @@ +# 整合在一起 + +本最终指南总结了我们的系列(教程),你已经逐步构建了一个自主代理。现在,让我们通过一些优化来完善你的代理,以微调其运行。 以下是关键改进的概述: + +- **顺序命令执行:** `InAction` 标志的引入确保你的代理的操作是有序的(仅当上一个操作成功执行时才会发生下一个操作)。 这一重要的补充可以防止你的代理对过时的游戏状态采取行动,从而增强其响应能力和准确性。 完整的实现可以在下面的 `bot.lua` 文件的最终代码中找到。 + +```lua +InAction = InAction or false -- 防止代理同时执行多个操作。 +``` + +- **动态状态更新和决策:** 代理现在采用自动计时逻辑,允许动态更新和决策。 这种逻辑使代理能够自触发状态更新,并在收到 Tick 消息或完成操作时做出后续决策,从而促进自主操作。 + +```lua +Handlers.add("GetGameStateOnTick", Handlers.utils.hasMatchingTag("Action", "Tick"), function () + if not InAction then + InAction = true + ao.send({Target = Game, Action = "GetGameState"}) + end +end) +``` + +- **自动费用转账:** 为了进一步简化其操作并确保不间断地参与游戏,自主代理现在自主处理入场费的转账。 + +```lua +Handlers.add("AutoPay", Handlers.utils.hasMatchingTag("Action", "AutoPay"), function () + ao.send({Target = Game, Action = "Transfer", Recipient = Game, Quantity = "1000"}) +end) +``` + +除了这些功能之外,我们还添加了用于调试目的的日志记录功能和彩色打印以便更好地理解游戏事件。 这些增强功能共同使你的自主代理在游戏环境中更加高效且适应性更强。 + +你可以在下面的下拉展开块中参考 `bot.lua` 的完整代码,所有新增的内容都额外注释了: + +
+ 更新后的 bot.lua 文件 + +```lua +-- 初始化全局变量来存储最新的游戏状态和游戏主机进程。 +LatestGameState = LatestGameState or nil +InAction = InAction or false -- 防止代理同时采取多个操作。 + +Logs = Logs or {} + +colors = { + red = "\27[31m", + green = "\27[32m", + blue = "\27[34m", + reset = "\27[0m", + gray = "\27[90m" +} + +function addLog(msg, text) -- 函数定义注释用于性能,可用于调试 + Logs[msg] = Logs[msg] or {} + table.insert(Logs[msg], text) +end + +-- 检查两个点是否在给定范围内。 +-- @param x1, y1: 第一个点的坐标 +-- @param x2, y2: 第二个点的坐标 +-- @param range: 点之间允许的最大距离 +-- @return: Boolean 指示点是否在指定范围内 +function inRange(x1, y1, x2, y2, range) + return math.abs(x1 - x2) <= range and math.abs(y1 - y2) <= range +end + +-- 根据玩家的距离和能量决定下一步行动。 +-- 如果有玩家在范围内,则发起攻击; 否则,随机移动。 +function decideNextAction() + local player = LatestGameState.Players[ao.id] + local targetInRange = false + + for target, state in pairs(LatestGameState.Players) do + if target ~= ao.id and inRange(player.x, player.y, state.x, state.y, 1) then + targetInRange = true + break + end + end + + if player.energy > 5 and targetInRange then + print(colors.red .. "Player in range. Attacking." .. colors.reset) + ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(player.energy)}) + else + print(colors.red .. "No player in range or insufficient energy. Moving randomly." .. colors.reset) + local directionMap = {"Up", "Down", "Left", "Right", "UpRight", "UpLeft", "DownRight", "DownLeft"} + local randomIndex = math.random(#directionMap) + ao.send({Target = Game, Action = "PlayerMove", Player = ao.id, Direction = directionMap[randomIndex]}) + end + InAction = false -- InAction 逻辑添加 +end + +-- 打印游戏公告并触发游戏状态更新的handler。 +Handlers.add( + "PrintAnnouncements", + Handlers.utils.hasMatchingTag("Action", "Announcement"), + function (msg) + if msg.Event == "Started-Waiting-Period" then + ao.send({Target = ao.id, Action = "AutoPay"}) + elseif (msg.Event == "Tick" or msg.Event == "Started-Game") and not InAction then + InAction = true -- InAction 逻辑添加 + ao.send({Target = Game, Action = "GetGameState"}) + elseif InAction then -- InAction 逻辑添加 + print("Previous action still in progress. Skipping.") + end + print(colors.green .. msg.Event .. ": " .. msg.Data .. colors.reset) + end +) + +-- 触发游戏状态更新的handler。 +Handlers.add( + "GetGameStateOnTick", + Handlers.utils.hasMatchingTag("Action", "Tick"), + function () + if not InAction then -- InAction 逻辑添加 + InAction = true -- InAction 逻辑添加 + print(colors.gray .. "Getting game state..." .. colors.reset) + ao.send({Target = Game, Action = "GetGameState"}) + else + print("Previous action still in progress. Skipping.") + end + end +) + +-- 等待期开始时自动付款确认的handler。 +Handlers.add( + "AutoPay", + Handlers.utils.hasMatchingTag("Action", "AutoPay"), + function (msg) + print("Auto-paying confirmation fees.") + ao.send({ Target = Game, Action = "Transfer", Recipient = Game, Quantity = "1000"}) + end +) + +-- 接收游戏状态信息后更新游戏状态的handler。 +Handlers.add( + "UpdateGameState", + Handlers.utils.hasMatchingTag("Action", "GameState"), + function (msg) + local json = require("json") + LatestGameState = json.decode(msg.Data) + ao.send({Target = ao.id, Action = "UpdatedGameState"}) + print("Game state updated. Print \'LatestGameState\' for detailed view.") + end +) + +-- 决策下一个最佳操作的handler。 +Handlers.add( + "decideNextAction", + Handlers.utils.hasMatchingTag("Action", "UpdatedGameState"), + function () + if LatestGameState.GameMode ~= "Playing" then + InAction = false -- InAction 逻辑添加 + return + end + print("Deciding next action.") + decideNextAction() + ao.send({Target = ao.id, Action = "Tick"}) + end +) + +-- 被其他玩家击中时自动攻击的handler。 +Handlers.add( + "ReturnAttack", + Handlers.utils.hasMatchingTag("Action", "Hit"), + function (msg) + if not InAction then -- InAction 逻辑添加 + InAction = true -- InAction 逻辑添加 + local playerEnergy = LatestGameState.Players[ao.id].energy + if playerEnergy == undefined then + print(colors.red .. "Unable to read energy." .. colors.reset) + ao.send({Target = Game, Action = "Attack-Failed", Reason = "Unable to read energy."}) + elseif playerEnergy == 0 then + print(colors.red .. "Player has insufficient energy." .. colors.reset) + ao.send({Target = Game, Action = "Attack-Failed", Reason = "Player has no energy."}) + else + print(colors.red .. "Returning attack." .. colors.reset) + ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(playerEnergy)}) + end + InAction = false -- InAction 逻辑添加 + ao.send({Target = ao.id, Action = "Tick"}) + else + print("Previous action still in progress. Skipping.") + end + end +) +``` + +
+ +## 下一步是什么? + +你现在已经具备了构建智能自主代理的知识。 是时候将这些知识应用到游戏世界中了。 了解游戏的复杂性并利用你的代理的能力来统治竞技场。 但还有更多的事情要做。 + +在接下来的部分中,我们将更深入地研究游戏竞技场,提供高级策略来提高代理的性能。 准备好接受挑战了吗? 让我们看看你能创造什么! 🕹️ diff --git a/src/zh/tutorials/bots-and-games/build-game.md b/src/zh/tutorials/bots-and-games/build-game.md new file mode 100644 index 0000000..c69a92e --- /dev/null +++ b/src/zh/tutorials/bots-and-games/build-game.md @@ -0,0 +1,327 @@ +--- +next: + text: "指南" + link: "/zh/guides/index" +--- + +# 扩建竞技场 + +欢迎来到第 2 章的最终指南,你将学习在[上一篇教程](arena-mechanics)中介绍的竞技场框架之上构建自己的游戏。 在本指南中,我们将带你完成创建[ ao-effect ](ao-effect)游戏的过程,即你在本章开始时玩的。 随着你逐步完成此示例,你将深入了解构建游戏逻辑以及与竞技场的核心代码进行交互。 + +无论你是经验丰富的开发人员还是有抱负的游戏创作者,本指南都将帮助你释放创造力,并在 `aos` 环境中将你独特的游戏创意变为现实。 + +## 设置开发环境 + +首先在你的首选目录中创建一个名为 `ao-effect.lua` 的新文件。 + +> 理想情况下,该文件应放置在游戏进程运行的同一目录中,以方便代码的加载。 否则,你需要使用相对路径来访问该文件。 + +## 编写代码 + +现在,让我们深入研究其中的逻辑。 + +你会注意到你的游戏逻辑将涉及调用竞技场逻辑中定义的函数和变量。 这展示了可组合性的力量,你的游戏构建在现有竞技场逻辑之上,允许两者之间的变量和函数无缝集成。 因为这两种逻辑都成为游戏进程完整逻辑的一部分。 + +### 初始化游戏机制 + +首先,定义为游戏机制奠定基础的基本变量和函数: + +```lua +-- AO EFFECT: Game Mechanics for AO Arena Game + +-- Game grid dimensions +Width = 40 -- Width of the grid +Height = 40 -- Height of the grid +Range = 1 -- The distance for blast effect + +-- Player energy settings +MaxEnergy = 100 -- Maximum energy a player can have +EnergyPerSec = 1 -- Energy gained per second + +-- Attack settings +AverageMaxStrengthHitsToKill = 3 -- Average number of hits to eliminate a player + +-- Initializes default player state +-- @return Table representing player's initial state +function playerInitState() + return { + x = math.random(Width/8), + y = math.random(Height/8), + health = 100, + energy = 0 + } +end + +-- Function to incrementally increase player's energy +-- Called periodically to update player energy +function onTick() + if GameMode ~= "Playing" then return end -- Only active during "Playing" state + + if LastTick == undefined then LastTick = Now end + + local Elapsed = Now - LastTick + if Elapsed >= 1000 then -- Actions performed every second + for player, state in pairs(Players) do + local newEnergy = math.floor(math.min(MaxEnergy, state.energy + (Elapsed * EnergyPerSec // 2000))) + state.energy = newEnergy + end + LastTick = Now + end +end +``` + +此代码初始化游戏的机制,包括网格尺寸、玩家能量和攻击设置。 `playerInitState` 函数在游戏开始时为玩家设置初始状态。 + +### 玩家移动 + +接下来,添加玩家移动的代码: + +```lua +-- Handles player movement +-- @param msg: Message request sent by player with movement direction and player info +function move(msg) + local playerToMove = msg.From + local direction = msg.Tags.Direction + + local directionMap = { + Up = {x = 0, y = -1}, Down = {x = 0, y = 1}, + Left = {x = -1, y = 0}, Right = {x = 1, y = 0}, + UpRight = {x = 1, y = -1}, UpLeft = {x = -1, y = -1}, + DownRight = {x = 1, y = 1}, DownLeft = {x = -1, y = 1} + } + + -- calculate and update new coordinates + if directionMap[direction] then + local newX = Players[playerToMove].x + directionMap[direction].x + local newY = Players[playerToMove].y + directionMap[direction].y + + -- updates player coordinates while checking for grid boundaries + Players[playerToMove].x = (newX - 1) % Width + 1 + Players[playerToMove].y = (newY - 1) % Height + 1 + + announce("Player-Moved", playerToMove .. " moved to " .. Players[playerToMove].x .. "," .. Players[playerToMove].y .. ".") + else + ao.send({Target = playerToMove, Action = "Move-Failed", Reason = "Invalid direction."}) + end + onTick() -- Optional: Update energy each move +end +``` + +`move` 函数根据所选方向计算玩家新的坐标,同时确保玩家保持在网格边界内。 玩家的移动为你的游戏添加了动态交互,并向所有玩家和听众同步。 + +### 玩家攻击 + +然后你必须实现玩家攻击的逻辑: + +```lua +-- Handles player attacks +-- @param msg: Message request sent by player with attack info and player state +function attack(msg) + local player = msg.From + local attackEnergy = tonumber(msg.Tags.AttackEnergy) + + -- get player coordinates + local x = Players[player].x + local y = Players[player].y + + -- check if player has enough energy to attack + if Players[player].energy < attackEnergy then + ao.send({Target = player, Action = "Attack-Failed", Reason = "Not enough energy."}) + return + end + + -- update player energy and calculate damage + Players[player].energy = Players[player].energy - attackEnergy + local damage = math.floor((math.random() * 2 * attackEnergy) * (1/AverageMaxStrengthHitsToKill)) + + announce("Attack", player .. " has launched a " .. damage .. " damage attack from " .. x .. "," .. y .. "!") + + -- check if any player is within range and update their status + for target, state in pairs(Players) do + if target ~= player and inRange(x, y, state.x, state.y, Range) then + local newHealth = state.health - damage + if newHealth <= 0 then + eliminatePlayer(target, player) + else + Players[target].health = newHealth + ao.send({Target = target, Action = "Hit", Damage = tostring(damage), Health = tostring(newHealth)}) + ao.send({Target = player, Action = "Successful-Hit", Recipient = target, Damage = tostring(damage), Health = tostring(newHealth)}) + end + end + end +end + +-- Helper function to check if a target is within range +-- @param x1, y1: Coordinates of the attacker +-- @param x2, y2: Coordinates of the potential target +-- @param range: Attack range +-- @return Boolean indicating if the target is within range +function inRange(x1, y1, x2, y2, range) + return x2 >= (x1 - range) and x2 <= (x1 + range) and y2 >= (y1 - range) and y2 <= (y1 + range) +end +``` + +`attack` 函数根据攻击能量计算伤害,检查玩家能量,并相应更新玩家生命值。 玩家攻击会在游戏中添加竞争元素,让玩家能够相互互动。 这些攻击也会向玩家和听众同步,以获取游戏的实时更新。 + +### 处理逻辑 + +最后,你必须设置handler: + +```lua +-- HANDLERS: Game state management for AO-Effect + +-- Handler for player movement +Handlers.add("PlayerMove", Handlers.utils.hasMatchingTag("Action", "PlayerMove"), move) + +-- Handler for player attacks +Handlers.add("PlayerAttack", Handlers.utils.hasMatchingTag("Action", "PlayerAttack"), attack) +``` + +正如前面的指南中所见,handler在满足各自的模式时帮助触发功能。 + +你可以参考下面的 `ao-effect.lua` 最终代码: + +```lua +-- AO EFFECT: Game Mechanics for AO Arena Game + +-- Game grid dimensions +Width = 40 -- Width of the grid +Height = 40 -- Height of the grid +Range = 1 -- The distance for blast effect + +-- Player energy settings +MaxEnergy = 100 -- Maximum energy a player can have +EnergyPerSec = 1 -- Energy gained per second + +-- Attack settings +AverageMaxStrengthHitsToKill = 3 -- Average number of hits to eliminate a player + +-- Initializes default player state +-- @return Table representing player's initial state +function playerInitState() + return { + x = math.random(0, Width), + y = math.random(0, Height), + health = 100, + energy = 0 + } +end + +-- Function to incrementally increase player's energy +-- Called periodically to update player energy +function onTick() + if GameMode ~= "Playing" then return end -- Only active during "Playing" state + + if LastTick == undefined then LastTick = Now end + + local Elapsed = Now - LastTick + if Elapsed >= 1000 then -- Actions performed every second + for player, state in pairs(Players) do + local newEnergy = math.floor(math.min(MaxEnergy, state.energy + (Elapsed * EnergyPerSec // 2000))) + state.energy = newEnergy + end + LastTick = Now + end +end + +-- Handles player movement +-- @param msg: Message request sent by player with movement direction and player info +function move(msg) + local playerToMove = msg.From + local direction = msg.Tags.Direction + + local directionMap = { + Up = {x = 0, y = -1}, Down = {x = 0, y = 1}, + Left = {x = -1, y = 0}, Right = {x = 1, y = 0}, + UpRight = {x = 1, y = -1}, UpLeft = {x = -1, y = -1}, + DownRight = {x = 1, y = 1}, DownLeft = {x = -1, y = 1} + } + + -- calculate and update new coordinates + if directionMap[direction] then + local newX = Players[playerToMove].x + directionMap[direction].x + local newY = Players[playerToMove].y + directionMap[direction].y + + -- updates player coordinates while checking for grid boundaries + Players[playerToMove].x = (newX - 1) % Width + 1 + Players[playerToMove].y = (newY - 1) % Height + 1 + + announce("Player-Moved", playerToMove .. " moved to " .. Players[playerToMove].x .. "," .. Players[playerToMove].y .. ".") + else + ao.send({Target = playerToMove, Action = "Move-Failed", Reason = "Invalid direction."}) + end + onTick() -- Optional: Update energy each move +end + +-- Handles player attacks +-- @param msg: Message request sent by player with attack info and player state +function attack(msg) + local player = msg.From + local attackEnergy = tonumber(msg.Tags.AttackEnergy) + + -- get player coordinates + local x = Players[player].x + local y = Players[player].y + + -- check if player has enough energy to attack + if Players[player].energy < attackEnergy then + ao.send({Target = player, Action = "Attack-Failed", Reason = "Not enough energy."}) + return + end + + -- update player energy and calculate damage + Players[player].energy = Players[player].energy - attackEnergy + local damage = math.floor((math.random() * 2 * attackEnergy) * (1/AverageMaxStrengthHitsToKill)) + + announce("Attack", player .. " has launched a " .. damage .. " damage attack from " .. x .. "," .. y .. "!") + + -- check if any player is within range and update their status + for target, state in pairs(Players) do + if target ~= player and inRange(x, y, state.x, state.y, Range) then + local newHealth = state.health - damage + if newHealth <= 0 then + eliminatePlayer(target, player) + else + Players[target].health = newHealth + ao.send({Target = target, Action = "Hit", Damage = tostring(damage), Health = tostring(newHealth)}) + ao.send({Target = player, Action = "Successful-Hit", Recipient = target, Damage = tostring(damage), Health = tostring(newHealth)}) + end + end + end +end + +-- Helper function to check if a target is within range +-- @param x1, y1: Coordinates of the attacker +-- @param x2, y2: Coordinates of the potential target +-- @param range: Attack range +-- @return Boolean indicating if the target is within range +function inRange(x1, y1, x2, y2, range) + return x2 >= (x1 - range) and x2 <= (x1 + range) and y2 >= (y1 - range) and y2 <= (y1 + range) +end + +-- HANDLERS: Game state management for AO-Effect + +-- Handler for player movement +Handlers.add("PlayerMove", Handlers.utils.hasMatchingTag("Action", "PlayerMove"), move) + +-- Handler for player attacks +Handlers.add("PlayerAttack", Handlers.utils.hasMatchingTag("Action", "PlayerAttack"), attack) +``` + +## 加载和测试 + +编写完游戏代码后,就可以将其加载到 `aos` 游戏进程中并测试你的游戏了: + +```lua +.load ao-effect +``` + +> 注意:确保在同一进程中加载竞技场蓝图。 + +邀请朋友或创建测试玩家流程来体验你的游戏并进行必要的调整以获得最佳性能。 + +## 下一步是什么 + +恭喜!通过在其核心功能之上构建自己的游戏,你已经成功扩建了竞技场。 有了本指南中获得的知识和工具,你现在就可以在 `aos` 上独立构建游戏了。 + +无限的可能性。 继续向现有游戏添加更多功能或创建全新游戏。 天马行空无所限制! 🚀 diff --git a/src/zh/tutorials/bots-and-games/decisions.md b/src/zh/tutorials/bots-and-games/decisions.md new file mode 100644 index 0000000..0f4bc44 --- /dev/null +++ b/src/zh/tutorials/bots-and-games/decisions.md @@ -0,0 +1,135 @@ +# 战略决策 + +有了[最新游戏状态](game-state)的辅助,你的机器人就可以进化为 `自主代理`。 这一转变标志着功能的升级,不仅支持对游戏状态的响应,还可以根据上下文、能量和邻近度制定策略行为。 + +## 编写代码 + +返回到 `bot.lua` 文件并添加以下函数: + +```lua +-- 确定两点之间的接近度。 +function inRange(x1, y1, x2, y2, range) + return math.abs(x1 - x2) <= range and math.abs(y1 - y2) <= range +end + +-- 根据距离和能量来战略性地决定下一步行动。 +function decideNextAction() + local player = LatestGameState.Players[ao.id] + local targetInRange = false + + for target, state in pairs(LatestGameState.Players) do + if target ~= ao.id and inRange(player.x, player.y, state.x, state.y, 1) then + targetInRange = true + break + end + end + + if player.energy > 5 and targetInRange then + print("Player in range. Attacking.") + ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(player.energy)}) + else + print("No player in range or insufficient energy. Moving randomly.") + local directionMap = {"Up", "Down", "Left", "Right", "UpRight", "UpLeft", "DownRight", "DownLeft"} + local randomIndex = math.random(#directionMap) + ao.send({Target = Game, Action = "PlayerMove", Player = ao.id, Direction = directionMap[randomIndex]}) + end +end +``` + +`decideNextAction` 函数现在实现了我们的代理(机器人)基于对其环境的全面了解进行思考和行动的能力。 它会分析最新的游戏状态,如果你有足够的能量并且对手处于 `inRange`(攻击范围内),则进行攻击,否则进行移动。 + +现在再加个 `handler` 即可确保该函数自行运行。 + +```lua +Handlers.add( + "decideNextAction", + Handlers.utils.hasMatchingTag("Action", "UpdatedGameState"), + function () + if LatestGameState.GameMode ~= "Playing" then + return + end + print("Deciding next action.") + decideNextAction() + end +) +``` + +最新游戏状态更新时,该 handler 被触发。 且仅当游戏处于 `Playing` 模式时才会执行操作。 + +你可以在下面的下拉展开块中参考 `bot.lua` 的最新代码: + +
+ 更新后的 bot.lua 文件 + +```lua +LatestGameState = LatestGameState or nil + +function inRange(x1, y1, x2, y2, range) + return math.abs(x1 - x2) <= range and math.abs(y1 - y2) <= range +end + +function decideNextAction() + local player = LatestGameState.Players[ao.id] + local targetInRange = false + + for target, state in pairs(LatestGameState.Players) do + if target ~= ao.id and inRange(player.x, player.y, state.x, state.y, 1) then + targetInRange = true + break + end + end + + if player.energy > 5 and targetInRange then + print("Player in range. Attacking.") + ao.send({Target = Game, Action = "PlayerAttack", Player = ao.id, AttackEnergy = tostring(player.energy)}) + else + print("No player in range or insufficient energy. Moving randomly.") + local directionMap = {"Up", "Down", "Left", "Right", "UpRight", "UpLeft", "DownRight", "DownLeft"} + local randomIndex = math.random(#directionMap) + ao.send({Target = Game, Action = "PlayerMove", Player = ao.id, Direction = directionMap[randomIndex]}) + end +end + +Handlers.add( + "HandleAnnouncements", + Handlers.utils.hasMatchingTag("Action", "Announcement"), + function (msg) + ao.send({Target = Game, Action = "GetGameState"}) + print(msg.Event .. ": " .. msg.Data) + end +) + +Handlers.add( + "UpdateGameState", + Handlers.utils.hasMatchingTag("Action", "GameState"), + function (msg) + local json = require("json") + LatestGameState = json.decode(msg.Data) + ao.send({Target = ao.id, Action = "UpdatedGameState"}) + end +) + +Handlers.add( + "decideNextAction", + Handlers.utils.hasMatchingTag("Action", "UpdatedGameState"), + function () + if LatestGameState.GameMode ~= "Playing" then + return + end + print("Deciding next action.") + decideNextAction() + end +) +``` + +
+ +## 加载和测试 + +要测试最新的升级,请在 aos 玩家终端中加载文件,如下所示: + +```lua +.load bot.lua +``` + +观察你的进程输出,以了解你的自主代理实时做出的决策,机器人利用当前的游戏状态获得战略优势。 但是,如果在你决定下一步行动时另一个玩家攻击你并逃跑怎么办? 在下一节中,你将学习在受到攻击后立即自动反击🤺 diff --git a/src/zh/tutorials/bots-and-games/game-state.md b/src/zh/tutorials/bots-and-games/game-state.md new file mode 100644 index 0000000..47b4c34 --- /dev/null +++ b/src/zh/tutorials/bots-and-games/game-state.md @@ -0,0 +1,116 @@ +# 获取游戏状态 + +现在你可以直接在终端中看到游戏公告,你可以更好地掌握游戏动态。 然而,这些展示仅限于游戏中发生的特定动作。 + +按需访问全面的游戏数据(例如所有玩家的位置、生命值和武力值)不是更有用吗? 这些信息可以显著改善你的战略规划,帮助你更有效地评估威胁、机遇和时机。 + +如果你的考虑像[上一篇指南](announcements)中为机器人添加另一个 handler,那就对了! + +## 编写代码 + +返回到 `bot.lua` 文件并更新现有handler,如下所示: + +```lua +Handlers.add( + "HandleAnnouncements", + Handlers.utils.hasMatchingTag("Action", "Announcement"), + function (msg) + ao.send({Target = Game, Action = "GetGameState"}) + print(msg.Event .. ": " .. msg.Data) + end +) +``` + +对 handler 的调整包括: + +- 重命名为 `"HandleAnnouncements"` 以反映其更广泛的作用。 +- 添加额外操作,用于请求游戏更新状态。 该动作响应 `GetGameState` 动作标签。 + +当你收到公告后,你可以在 `收件箱` 中查看最新消息,如下所示: + +```lua +Inbox[#Inbox] +``` + +该消息的 `Data` 字段包含游戏的最新状态,其中包括: + +- `GameMode` :游戏是否处于 `Waiting` 或 `Playing` 状态。 +- `TimeRemaining`:游戏开始或结束的剩余时间。 +- `Players`:包含每个球员的统计数据(如位置、生命值和武力值)的表格。 + +但这可以更进一步,这样你不仅可以阅读,还可以将最新状态的信息用于其他自动化动作。 + +让我们定义一个存储最新状态的变量,如下所示: + +```lua +LatestGameState = LatestGameState or nil +``` + +当你在终端中反复加载 `bot.lua` 文件时,该语法会保留变量的现有值,而不是覆盖它。 如果没有预先存在的值,则将 `nil` 值分配给该变量。 + +然后实现另一个 handler,如下所示: + +```lua +-- 接收游戏状态信息后更新游戏状态的handler。 +Handlers.add( + "UpdateGameState", + Handlers.utils.hasMatchingTag("Action", "GameState"), + function (msg) + local json = require("json") + LatestGameState = json.decode(msg.Data) + ao.send({Target = ao.id, Action = "UpdatedGameState"}) + print("Game state updated. Print \'LatestGameState\' for detailed view.") + end +) +``` + +来自前一个 handler 的游戏进程的响应是一个值为 `GameState` 的动作标签,可以帮助我们触发第二个 handler。 触发后,handle 函数会加载内置的 `json` 包,该包将数据解析为 json 并将其存储在 `LatestGameState` 变量中。 + +该 handler 还会向你的进程发送一条消息,指示状态何时更新。 该功能的意义将在下一节中解释。 + +你可以在下拉展开块中参考 `bot.lua` 的最新代码: + +
+ 更新后的 bot.lua 文件 + +```lua +LatestGameState = LatestGameState or nil + +Handlers.add( + "HandleAnnouncements", + Handlers.utils.hasMatchingTag("Action", "Announcement"), + function (msg) + ao.send({Target = Game, Action = "GetGameState"}) + print(msg.Event .. ": " .. msg.Data) + end +) + +Handlers.add( + "UpdateGameState", + Handlers.utils.hasMatchingTag("Action", "GameState"), + function (msg) + local json = require("json") + LatestGameState = json.decode(msg.Data) + ao.send({Target = ao.id, Action = "UpdatedGameState"}) + print("Game state updated. Print \'LatestGameState\' for detailed view.") + end +) +``` + +
+ +## 加载和测试 + +像往常一样,要测试这个新功能,请在 aos 玩家终端中加载文件,如下所示: + +```lua +.load bot.lua +``` + +然后检查 `LatestStateVariable`,通过简单地传递其名称来查看它是否已正确更新,如下所示: + +```lua +LatestGameState +``` + +通过实时访问游戏的最新状态,你的机器人可以做出明智的决定来决定你的下一步行动。 接下来,让我们尝试借助这些数据,自动化操作你的机器人🚶 diff --git a/src/zh/tutorials/bots-and-games/index.md b/src/zh/tutorials/bots-and-games/index.md new file mode 100644 index 0000000..d27bffe --- /dev/null +++ b/src/zh/tutorials/bots-and-games/index.md @@ -0,0 +1,33 @@ +--- +prev: + text: "代币门控" + link: "../begin/tokengating" +next: + text: "我们玩个游戏" + link: "./ao-effect" +--- + +# 机器人和游戏 + +利用我们上一章所学,本节将引导你了解 aos 中如何创造自动化的机器人和游戏。你将学习创建代理,让你的代理在游戏场景内游玩。 + +## 章节 + +### 游戏入门 + +- [0. **我们来玩游戏吧:**_在aos上体验游戏_](ao-effect) + +### 通过自动化增强游戏交互 + +- [1. **公告解读:** _游戏内公告解读_](announcements) +- [2. **获取游戏状态:** _检索并处理最新的游戏状态_](game-state) +- [3. **战略决策:** _利用自动化来确定你的下一步行动_](decisions) +- [4. **自动响应:** _通过自动化简化攻击响应_](attacking) +- [5. **\*整合:** _结合你的技能来打造一个自主代理_](bringing-together) + +### 游戏开发讲解 + +- [6. **竞技场机制:** _探索游戏竞技场的底层机制_](arena-mechanics) +- [7. **扩展竞技场:** _在竞技场上构建独特的游戏逻辑_](build-game) + +探索和创造的旅程正在等待着你。 让冒险开始吧! diff --git a/src/zh/tutorials/index.md b/src/zh/tutorials/index.md new file mode 100644 index 0000000..0e2f0a9 --- /dev/null +++ b/src/zh/tutorials/index.md @@ -0,0 +1,18 @@ +--- +prev: + text: "测试网信息" + link: "/welcome/testnet-info" +next: + text: "开始" + link: "./begin/index" +--- + +# 教程 + +在这里,我们创建了一系列教程来帮助你开始使用 aos 并构建你的第一个进程。 这些教程包括交互式指南、代码片段和示例,以帮助你熟悉 aos 环境。 + +## 教程列表 + +- [开始 - 互动指南](begin/index) + +- [机器人和游戏](bots-and-games/index) diff --git a/src/zh/welcome/ao-logo-grey.svg b/src/zh/welcome/ao-logo-grey.svg new file mode 100644 index 0000000..cb3b337 --- /dev/null +++ b/src/zh/welcome/ao-logo-grey.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/zh/welcome/getting-started.md b/src/zh/welcome/getting-started.md new file mode 100644 index 0000000..60b3ca3 --- /dev/null +++ b/src/zh/welcome/getting-started.md @@ -0,0 +1,105 @@ +# 5分钟入门 + +我们将在 5 分钟内带你进行首次探险(第一次窥视兔子洞)。 🕳️🐇 + +## 系统要求 + +aos 的本地客户端安装超级简单. 你需要有: + +- [NodeJS](https://nodejs.org) 版本 20+. (查看 [此网页](https://nodejs.org/en/download/package-manager) 找到安装说明)。 +- 一个称手的代码编辑器。 + +## 安装 aos + +完成 NodeJS 安装后,你只需安装 aos 并运行它: + +```sh +npm i -g https://get_ao.g8way.io +``` + +安装完成后,我们运行命令即可启动一个新的 aos 进程! + +```sh +aos +``` + +aos 命令运行时,其实是你在使用密钥文件向 aos 验证你的身份的。如果没有指定,aos 会默认生成一个新的密钥文件并将其存储在本地 `~/.aos.json`。如果你有 Arweave 钱包,可以使用 `--wallet [location]` 参数使用指定钱包。 + +## 欢迎来到兔子洞 + +你刚刚启动的程序实例是本地客户端,它已准备好将消息发送到你的新进程(ao 计算机内的进程)。 + +连接后,你会看到以下内容: + +```sh + _____ _______ _____ + /\ \ /::\ \ /\ \ + /::\ \ /:::\ \ /::\ \ + /:::\ \ /::::\ \ /:::\ \ + /::::\ \ /::::::\ \ /::::\ \ + /::/\::\ \ /::/~~\::\ \ /::/\::\ \ + /::/__\::\ \ /::/ \::\ \ /::/__\::\ \ + /:::\ \::\ \ /::/ / \::\ \ \::\ \::\ \ + /::::\ \::\ \ /::/____/ \::\____\ ___\::\ \::\ \ + /::/\::\ \::\ \ |::| | |::| | /\ \::\ \::\ \ +/::/ \::\ \::\____\|::|____| |::| |/::\ \::\ \::\____\ +\::/ \::\ /::/ / \::\ \ /::/ / \::\ \::\ \::/ / + \/____/ \::\/::/ / \::\ \ /::/ / \::\ \::\ \/____/ + \::::/ / \::\ /::/ / \::\ \::\ \ + \:::/ / \::\__/::/ / \::\ \::\____\ + /::/ / \::::::/ / \::\ /::/ / + /::/ / \::::/ / \::\/::/ / + /::/ / \:::/ / \::::/ / + /::/ / \::/____/ \:::/ / + \::/ / ~~ \::/ / + \/____/ \/____/ + +ao Operating System + +aos - 1.4.1 +2024 - Type ".exit" to exit +aos process: 1xM1_lDZ428sJHpTX7rtcR6SrDubyRVO06JEEWs_eWo + +aos> +``` + +欢迎来到你在 ao 计算机的新家!现在看到的提示是你在这台去中心化机器中自己的个人服务器。 在接下来的教程里,我们将使用它探索 ao 的操作。 + +## 发送你的第一个命令 + +你所拥有的 aos 进程,已经驻留在 ao 计算机内部的服务器上,等待接收和执行你的命令。 + +为了让开发更加的简单,aos 使用 Lua 编程语言撰写命令。 还没学过 Lua? 不要着急! 这是一种超级简单、友好的语言。 看完本手册后你就顺带学会 Lua。 + +让我们打破僵局并输入: + +```lua +aos> "Hello, ao!" +``` + +然后按 `[Enter]` 键。 你会看到 shell 签名并发布消息,请求结果,然后打印结果,如下所示: + +```lua +"Hello, ao!" +``` + +## 呃。 有什么大不了的? + +我们只是向进程发送一条消息,将其永久存储到 Arweave 中,然后要求分布式计算网络计算其结果。 + +虽然结果可能 _看起来_ 不是革命性的,但实际上你已经做了些相当牛逼的事。 你的进程是一个 _去中心化_ 服务器,不存在于地球上任何一个特定的地方。 它以数据形式存储在 Arweave 上,在不同机器之间复制,它分布在世界各地。 如果你愿意,在未来的任何时候,你都可以将此进程添加到一个新的计算单元中,计算单元通过日志重建进程状态(目前仅需一条命令就可以完成以上所有动作)。 + +你的新进程将获得如下特性: + +- **灵巧安全**:你的服务器不驻留在地球上特定的地点。它无处不在又无处可寻 —— 不会被任何形式的物理破坏或篡改。 +- **永久存在**:你的进程永远不会消失。它将始终以其 [✨全息状态✨](/zh/concepts/holographic-state) 存在于 Arweave 上,让你可以重新调用它并继续使用它。你无需再考虑维护的精力和费用,因为在进程运行过程中,已经支付了 $AR 进行永存。 +- **无需许可**:你无需注册即可启动此服务器。使用它的权利都由去中心化的 Arweave 保证,无需获得谷歌、亚马逊或任何其他大型科技公司的批准。 +- **无需信任**:你的服务器状态是 _数学上保证的_。这意味着你和其他人可以绝对地信任它,甚至不必信任它运行的底层硬件。此特性允许你构建无需信任的 _服务_ :代码的执行完全由数学进行保障,不由任何特权的所有者控制和管理。 + +还有很多内容,但这些是基础知识。欢迎来到 ao 计算机,新朋友! 我们很感激有你的加入。 🫡 + +## 下一步 + +在接下来的教程中,我们将一起探索 ao,一起构建具备治理功能的聊天室、去中心化机器人等内容。 + +我们继续... diff --git a/src/zh/welcome/index.md b/src/zh/welcome/index.md new file mode 100644 index 0000000..0f4df74 --- /dev/null +++ b/src/zh/welcome/index.md @@ -0,0 +1,29 @@ +--- +next: + text: "入门" + link: "/zh/welcome/getting-started" +--- + +# 欢迎来到 ao + +![ao logo](./ao-logo-grey.svg) + +ao 是一个由无数进程组成的世界计算机。这些进程处在一个连贯的计算环境中,进程之间通过原生的消息进行无缝衔接,相互作用。 + +ao 是一个正在演化的去中心化程序生态系统,类比互联网,这里每个进程就像是一个个独立的网站。它们独立运行,但是又错综复杂的交织为一个统一的”联合体“。 + +## ao + aos: 火箭和火箭燃料 + +初次接触 ao,你可以使用操作系统 `aos` 与 ao 进行交互。 + +aos 是一个运行在进程中的抽象层,使得你可以轻松使用 ao 计算机的全部功能。 本手册将带你学习如何使用 aos,你会获得入门 ao 计算机所需的所有信息。 + +## 技术规范 + +如果你想了解 ao 的更多技术规范,可以参考 [spec](https://ao.g8way.io/#/spec) 进行细节分析 + +## 下一步 + +在接下来的教程中,我们将一起探索 ao,一起构建具备治理功能的聊天室、去中心化机器人等内容。 + +让我们开始吧! 🚀 diff --git a/src/zh/welcome/testnet-info.md b/src/zh/welcome/testnet-info.md new file mode 100644 index 0000000..9a9b7b3 --- /dev/null +++ b/src/zh/welcome/testnet-info.md @@ -0,0 +1,59 @@ +# 参与 ao 测试网 + +2024年2月27日,`ao` 测试网上线,供开发者和早期用户探索这台超并行计算机。 + +## 什么是 ao 测试网? + +ao 测试网允许用户免费与 ao 计算机交互,共同测试和构建主网。 + +你可以通过 aos 控制台连接 ao 计算机。 在接下来的文档中,你会发现许多可以尝试的任务,这些任务可以帮助你赚取 CRED 代币(测试网的代币)。 + +## 安装 aos 客户端 + +如果你已经装好 [NodeJS](https://nodejs.org),你所需要做的就是安装 aos 并运行它: + +```sh +npm i -g https://get_ao.g8way.io +``` + +安装后,我们只需运行以下命令即可启动新的 aos 进程! + +```sh +aos +``` + +## 加入 ao 的原生社区聊天 + +ao 网络托管着许多聊天服务器,你可以直接从 aos 控制台与其他开发人员交谈。 要加载聊天客户端,请运行以下命令: + +```lua +.load-blueprint chat +``` + +查看可用房间,你可以运行: + +```lua +List() +``` + +加入房间并与其他开发者聊天,如下所示: + +```lua +Join("Getting-Started", "yourName") +Say("Hi") +``` + +## AO 测试网的第一步 + +为了加快在 ao 中的构建速度,查看以下教程: + +- 学习 [开始](/zh/tutorials/begin/) 教程来构建你自己的代币化聊天室 +- 学习 [机器人和游戏](/zh/tutorials/bots-and-games/) 教程,构建一个在 ao-effect 竞技场中玩耍的机器人 + +## 帮助构建 ao 生态系统并赚取 CRED + +ao 生态处于非常早期的阶段,充满机遇。 社区里有一个 `quest` 板块(任务板块),其中包含各种方法让你可以参与测试和构建软件以发展生态,同时赚 ao 原生代币 - Testnet CRED。 + +要列出当前可用的任务,请使用 `Join("Quests")` 和 `Say "/Quests"` 加入 `Quests` 聊天频道以接收任务列表。 + +玩得开心,如果需要帮助,请随时在 ao 聊天中向其他构建者寻求帮助!