EOS开发

Oasis(绿洲)

2018-05-20  本文已影响11人  糖果果老师

在我们开始使用代码之前,我们首先需要一个想法。你看过Steven Spielberg的“Ready Player One”电影吗?如果你确实已经知道了绿洲是什么!精彩,对吧?让我们想象一下,我们是将要建立下一个Oasis的团队的一部分,我们的任务是将区块链技术集成到其中,或者至少表明可以完成。我们要做的是一个小型的dApp,它会有玩家。每个玩家都可以玩游戏,并且可以从中获得硬币。随着硬币,他可以去市场购买物品,这会给体力,能力,生命 ,升级。当然,Oasis合同是我们的主要起点。

开发前准备

为了能够开发EOS dApps,您需要使用C / C ++的背景,因为这是用于EOS Smart Contracts的编程语言

一个IDE,您可以在其中编写智能合约。我们使用VS Code和C / C ++扩展,但是你可以选择最适合你的。CLion也是一个非常不错的选择

当然,你需要一个本地testnet节点,但你知道如何构建一个。

配置开发环境

在编写我们的第一份合同之前,我们需要在开发过程中设置一些我们需要的东西。

步骤1 - 启动节点

nodeos -e -p eosio --plugin eosio :: wallet_api_plugin --plugin eosio :: chain_api_plugin --plugin eosio :: account_history_api_plugin --resync 

步骤2 - 创建一个电子钱包

它将存储我们在签署交易时使用的密钥

# 1. Create new wallet with the name "oasis"
cleos wallet create -n oasis

# 2. Generate two pair of keys (use the command twice)
cleos create key

# 3. Import the generated private keys in the wallet(you need to specify the wallet at the end)
# {private_key_1} - This will be the OwnerKey
# {private_key_2} - This will be the ActiveKey
cleos wallet import {private_key_1} -n oasis 
cleos wallet import {private_key_2} -n oasis 

# 4. Add the private key of the "eosio" account into your wallet
# Note: With the latest version of EOSIO the private key is automatically added to your wallet
cleos wallet import 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3 -n oasis

注意:不要忘记保存您的密钥和钱包密码
EOS.IO智能合约在帐户上运行。因此,需要账户将交易转移或以其他方式推送至区块链。让我们将我们的帐户命名为“ anorak ”。听起来很熟悉?

# Create the account using the public keys
cleos create account eosio anorak {public_OwnerKey} {public_ActiveKey}

# "eosio" is the name of the account who will create the new one
# "anorak" is the name of the new account

项目'绿洲'

现在是开始开发我们的dApp的时候了!创建一个名为Oasis的项目文件夹。里面添加两个主要的子文件夹 - contracts 和 tests。是的,我们也要写tests。将会有四个子文件夹:Players, Games, Marketplace , Oasis。

Player智能合约

玩家将成为我们的第一个EOS智能合约。每个玩家将拥有用户名,等级,生命值,能量值,金币,背包和天赋。他将能够用金币从商店购买物品,并将其添加到他的背包,生命值,能量值或天赋中。为了获得金币,他需要与绿洲的其他玩家一起玩游戏

在Players文件夹中创建Players.hpp和Players.cpp文件

  1. Player.hpp是包含由.cpp文件引用的变量,常量和函数的头文件。

  2. Player.cpp文件是包含合同功能的源文件。

我们来深入看一下EOS合约的基本结构

Players.hpp
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
#include <string>
Players.cpp

在合约开始时,我们设置了我们要使用的所有包含。在我们的例子中,Players.hpp已经拥有了它们,所以在我们的合同实现中只包含头文件就足够了。大多数情况下,当你使用头文件时你会这样做。

将所有内容都包装在名称空间中是一种很好的做法,因为它与Oasis一起显示在这里。

#include "Players.hpp"

namespace Oasis {
    using namespace eosio;
    using std::string;

    class Players : public contract {
        using contract::contract;
        
        public:
            Players(account_name self):contract(self) {}

            //@abi action
            void add(const account_name account, string& username, uint64_t level) {
                
            }

            //@abi action
            void update(const account_name account) {

            }

            //@abi action
            void getplayer(const account_name account) {
                
            }

        private:

            //@abi table player i64
            struct player {
                uint64_t account_name;
                string username;
                uint64_t level;
                uint64_t health_points = 1000;
                uint64_t energy_points = 1000;

                uint64_t primary_key() const { return account_name; }

                EOSLIB_SERIALIZE(player, (account_name)(username)(level)(health_points)(energy_points))
            };

            typedef multi_index<N(player), player> playerIndex;
    };

    EOSIO_ABI(Players, (add)(update)(getplayer))
}

该Players 继承contract ,并使用它的构造函数(using contract::contract)。

在开发EOS dApp时,您应该了解的一件重要事情是,智能合约以操作和共享内存数据库访问的形式相互通信。一个合同可以读取另一个合同数据库的状态,只要它包含在具有异步可达的事务的读取范围内。这可以通过使用两种通信模式之一来实现 - inline 和deferred。您可以将它们视为同步和异步

从EOS.IO文档:

inline - 内联保证与当前交易函数一起执行或展开; 无论成功或失败,都不会收到任何通知。内联与原始交易函数具有相同的范围和权限
deferred - 延期将在稍后由生产者自行决定; 可以传达通信结果或者可以简单地超时。延期可以延伸到不同的范围,并承担发送它们的合同的权力。
在我们合同的类体中,有两种类型的访问修饰符 - public和private。在公共部分是构造函数和所有动作。一个动作表示智能合约中的单个操作。在我们的合同中,我们add,update和getplayer。我们稍后会看看他们。与此同时,您应该已经注意到,在每个函数之前,我们都有“ // @ abi action ”。它是eosiocpp脚本的指示标志,它将为我们的智能合约生成.abi文件。

在private中,我们保存我们不希望从Player合约外部访问的所有内容。这里我们正在初始化multi_index。这是什么,为什么我们需要它?
//@abi table player i64
struct player {
  uint64_t account_name;
  string username;
  uint64_t level;
  uint64_t health_points = 1000;
  uint64_t energy_points = 1000;

  uint64_t primary_key() const { return username; }

  EOSLIB_SERIALIZE(player, (account_name)(username)(level)(health_points)(energy_points))
};

typedef multi_index<N(player), player> playerIndex;

Actions 实现

add 负责创建一个玩家

/@abi action
void add(const account_name account, string& username) {
    /**
     * We require that only the owner of an account can use this action
     * or somebody with the account authorization
    */
    require_auth(account);

    /**
     * We access the "player" table as creating an object of type "playerIndex"
     * As parameters we pass code & scope - _self from the parent contract
    */
    playerIndex players(_self, _self);

    /**
     * We must verify that the account doesn't exist yet
     * If the account is not found the iterator variable should be players.end()
    */
    auto iterator = players.find(account);
    eosio_assert(iterator == players.end(), "Address for account already exists");

    /**
     * We add the new player in the table
     * The first argument is the payer of the storage which will store the data
    */
    players.emplace(account, [&](auto& player) {
        player.account_name = account;
        player.username = username;
        player.level = 1;
        player.health_points = 1000;
        player.energy_points = 1000;
    });
}

update 负责更新玩家level, health and energy points . 实现和add相似,只是在更新玩家状态时第一个参数时迭代器

//@abi action
void update(account_name account, uint64_t level, int64_t healthPoints, int64_t energyPoints) {
    require_auth(account);

    playerIndex players(_self, _self);

    auto iterator = players.find(account);
    eosio_assert(iterator != players.end(), "Address for account not found");

    /**
     * We add the new player in the table
     * The first argument is the payer of the storage which will store the data
    */
    players.modify(iterator, account, [&](auto& player) {
        player.level = level;

        if ((player.health_points - healthPoints) < 0) {
            player.health_points = 0;
        } else {
            player.health_points -= healthPoints;
        }

        if ((player.energy_points - energyPoints) < 0) {
            player.energy_points = 0;
        } else {
            player.energy_points -= energyPoints;
        }
    });
}

getplayer 是一个查询操作,任何人都可以访问 ,不需要认证

//@abi action
void getplayer(const account_name account) {
    playerIndex players(_self, _self);

    auto iterator = players.find(account);
    eosio_assert(iterator != players.end(), "Address for account not found");

    /**
     * The "get" function returns a constant reference to the object
     * containing the specified secondary key
    */
    auto currentPlayer = players.get(account);
    print("Username: ", currentPlayer.username.c_str(), " Level: ", currentPlayer.level, " Health: ", currentPlayer.health_points, " Energy: ", currentPlayer.energy_points);
}

Player智能合约部署

生成wast和abi

//生成wast
eosiocpp -o Players.wast Players.cpp
//生成abi
eosiocpp -g Players.abi Players.cpp

部署

# cleos set contract {account} {path_to_contract_folder} {path_to_.wast_file} {path_to_.abi_file}
cleos set contract anorak ./contracts/Players ./contracts/Players/Players.wast ./contracts/Players/Players.abi
Reading WAST...
Assembling WASM...
Publishing contract...
executed transaction: b3fd7968d08f3e1cece14fed9632c8c678e89f5832b356a3c4c651aa4d3fe159  5668 bytes  10000 cycles
#         eosio <= eosio::setcode               "000000004073e9340000cf590061736d0100000001cf011d60037f7e7f0060057f7e7e7e7e0060027f7e0060000060057e7...
#         eosio <= eosio::setabi                "000000004073e934000406706c6179657200050c6163636f756e745f6e616d650675696e74363408757365726e616d65067...

测试

创建一个玩家
# cleos push action {account} {action_name} '{data}' -p {account}@active
cleos push action anorak add '["anorak","art3mis"]' -p anorak@active

executed transaction: 9b099bd072d090424bff9cff3e0473a2ccde98d3554cbffee2392172e4221055  252 bytes  1000 cycles
#        anorak <= anorak::add                  {"account":"anorak","username":"art3mis"}
查询玩家
# cleos push action {account} {action} {data}
# account - The account providing the contract to execute
# action - The action to execute on the contract
# data - The arguments to the contract
cleos push action anorak getplayer '["anorak"]'

executed transaction: 5ecdf264952837e61671b31e935eec6365af8c6430f00f3eafd274e7ca81910d  156 bytes  1000 cycles
#        anorak <= anorak::getplayer            {"account":"anorak"}
>> Username: art3mis Level: 1 Health: 1000 Energy: 1000
更新玩家状态
cleos push action anorak update '["anorak",4,75,110]' -p anorak@active

executed transaction: 0da802b784e9bc2d56d84582e269eae213f6255ca2bbe83c32b6baf9fafc1f54  268 bytes  1000 cycles
#        anorak <= anorak::update               {"account":"anorak","level":4,"healthPoints":75,"energyPoints":110}
验证更新
cleos push action anorak getplayer '["anorak"]' -p anorak@active

executed transaction: a82c8356aaaea39ddb544d0fc5486f567c4bbdbb887b752aaf21ff0dd9ee8772  156 bytes  1000 cycles
#        anorak <= anorak::getplayer            {"account":"anorak"}
>> Username: art3mis Level: 4 Health: 925 Energy: 890

总结

from:https://infinitexlabs.com/eos-development-tutorial-part-1/

上一篇 下一篇

猜你喜欢

热点阅读