15158846557 在线咨询 在线咨询
15158846557 在线咨询
所在位置: 首页 > 营销资讯 > 网站运营 > 基于EOS生态的一元夺宝游戏开发

基于EOS生态的一元夺宝游戏开发

时间:2023-06-21 17:30:02 | 来源:网站运营

时间:2023-06-21 17:30:02 来源:网站运营

基于EOS生态的一元夺宝游戏开发:

距离EOS主网上线已经有一段时间了。EOS一直被期待可以成为下一代区块链技术,因为他解决了ETH 吞吐量低的问题,自从主网上线,也出现了蛮多的基于EOS DAPP(分布式应用)。

我对这个技术也有点兴趣,所以决定用设计开发一个EOS夺宝游戏来验证下。简单而言可以理解成一元夺宝的EOS版,若干用户每个人投一个EOS,当奖池到达N个EOS的时候,就开奖,把N个EOS返回给中奖用户。

相比于传统技术的一元夺宝,EOS夺宝最大的优势在于公平公正,传统的一元夺宝代码都运行在某公司的服务器上,代码是否有吃用户钱的逻辑谁也不知道。但是智能合约代码是部署在区块链上,运行的代码,代码运行的结果都可以被所有用户查询到的,公开透明从而保证了游戏(赌博)的公平性。同时相比于ETH,EOS网络拥有高吞吐量,用户不会感受到网络拥堵,满足高并发下对性能的要求。

备注:一元夺宝已经被定义为违法,该项目只供学习参考,另外EOS安全相关知识需要认真理解,实践过程中发生二次丢币事件。

整体架构

从产品形态上,它需要一个网页入口,这个入口会展示当前有多少人参与游戏,距离开奖还有多少个EOS,历史参与游戏的用户名单,历史获得奖品的用户名单等信息。它是一个 web application,它需要和 EOS 区块链数据交互,可以直接从链上获取数据以保证游戏的公开公正。

为了方便理解,简单线框图 demo 如下:

要提供一个方式让用户参与游戏,因为这个 web application 不具备钱包的属性,所以不能直接转账,转账还是依赖用户从钱包发起。

当前创建EOS账户还是比较麻烦,不过网络上还是能找到蛮多途径。假设你已经有EOS账户,并将它倒入到 imtoken 等支持EOS的钱包。

参与的用户(player)通过 imtoken 发起一笔转账到合约发布者账户(owner)。合约发布者发布的智能合约会监听转账请求,记录下转账人,如果奖池满足条件,则触发开奖逻辑。

整体架构如下图:

开发环境准备

开发环境的准备可以参考官方文档,

这里会列举一些重点说明一下。开发环境整体分两个部分,运行EOS节点和准备智能合约编译环境。

运行 EOS 节点

一般有两种方式运行EOS节点,本地编译和docker运行,这里推荐使用docker运行,因为真的很省心,编译可能遇到各种问题,并且时间很长。docker运行只要 pull下来 image,运行就好了。具体命令如下:

docker pull eosio/eosdocker run --name eosio / --publish 7777:7777 / --publish 127.0.0.1:5555:5555 / --volume /root/contracts:/root/contracts / --detach / eosio/eos / /bin/bash -c / "keosd --http-server-address=0.0.0.0:5555 & exec nodeos -e -p eosio --plugin eosio::producer_plugin --plugin eosio::history_plugin --plugin eosio::chain_api_plugin --plugin eosio::history_plugin --plugin eosio::history_api_plugin --plugin eosio::http_plugin -d /mnt/dev/data --config-dir /mnt/dev/config --http-server-address=0.0.0.0:7777 --access-control-allow-origin=* --contracts-console --http-validate-host=false --filter-on='*'"智能合约编译环境(EOSIO-CPP)

不同EOS智能合约使用 C++ 编写,当我们设计好完成智能合约代码后,需要编译成abi文件,然后发布到网络上。具体流程可以参考如下文档

但是,也是有一些坑的,主要是 C++ 版本的问题,默认情况下 centos 系统自带的版本 gcc版本太低了,我升级到 7.3.0 后,编译出现的问题消失。

智能合约编写

智能合约主要解决几个问题,首先是要监听到转账,然后记录参与用户,在满足条件的时候给用户开奖。核心代码如下,可以一一讲解:

class duobao: public eosio::contract{public: duobao(account_name self) :contract(self), bets(_self, _self), winners(_self, _self) { } using contract::contract; static constexpr int64_t unit_count = 10; static constexpr int64_t min_count = 1 * unit_count; static constexpr int64_t max_count = 20 * unit_count; static constexpr float win_ratio = 0.9; uint64_t random_generate(uint64_t range) { checksum256 result; auto mixedBlock = tapos_block_prefix() * tapos_block_num(); const char *mixedChar = reinterpret_cast<const char *>(&mixedBlock); sha256( (char *)mixedChar, sizeof(mixedChar), &result); const char *p64 = reinterpret_cast<const char *>(&result); int64_t sum = 0; for(int i = 0; i<6; i++) { sum += (int64_t)p64[i]; } return (abs(sum) % (range)); } void buy(account_name from, account_name to, asset quantity, string memo) { if ((from == _self) || (to != _self)) { return; } eosio_assert(quantity.symbol == CORE_SYMBOL, "Must by paid by EOS"); eosio_assert(quantity.amount % min_count == 0, "Bet Must Be divided by 1 EOS"); eosio_assert(quantity.amount >= min_count, "Bet Must be greater than 1 EOS"); auto new_offer_itr = bets.emplace(_self, [&](auto& betting){ betting.id = bets.available_primary_key(); betting.bet = quantity; betting.owner = from; betting.memo = memo; }); uint64_t total = query_total(); if( total >= max_count ){ doOpen(total); } } uint64_t query_total(){ uint64_t sum = 0; for( const auto& item : bets ) { sum += item.bet.amount; } return sum; } void doOpen(uint64_t total) { uint64_t towin = random_generate(total / unit_count) * unit_count, inc = 0; for( const auto& item : bets ) { // the winner if( (towin >= inc) && (towin < (inc + item.bet.amount)) ) { auto new_offer_itr = winners.emplace(_self, [&](auto& winner){ winner.id = winners.available_primary_key(); winner.owner = item.owner; winner.bet = item.bet; winner.total = total; winner.res = towin; }); action( permission_level{_self, N(active)}, N(eosio.token), N(transfer), std::make_tuple(_self, item.owner, asset(total * win_ratio), item.memo) ).send(); break; } inc += item.bet.amount; } for(auto itr = bets.begin(); itr != bets.end(); ){ itr = bets.erase(itr); } } // @abi action [[eosio::action]] void open( account_name user ) { require_auth( user ); doOpen(query_total() ); }private: //@abi table offer i64 struct [[eosio::table]] betting { uint64_t id; account_name owner; asset bet; string memo; uint64_t primary_key()const { return id; } uint64_t by_bet()const { return (uint64_t)bet.amount; } EOSLIB_SERIALIZE( betting, (id)(owner)(bet)(memo) ) }; //@abi table offer i64 struct [[eosio::table]] winner { uint64_t id; account_name owner; asset bet; uint64_t total; uint64_t res; uint64_t primary_key()const { return id; } EOSLIB_SERIALIZE( winner, (id)(owner)(bet)(total)(res) ) }; typedef eosio::multi_index< N(betting), betting > bet_index; typedef eosio::multi_index< N(winner), winner> account_index; bet_index bets; account_index winners;};extern "C" { void apply( uint64_t receiver, uint64_t code, uint64_t action ) { duobao thiscontract(receiver); if((code == N(eosio.token)) && (action == N(transfer))) { execute_action(&thiscontract, &duobao::buy); return; } if (code != receiver) return; switch (action) { EOSIO_API(duobao, (open)) }; eosio_exit(0); }}重点说几个地方吧:

  1. 监听转账:
extern "C" { void apply( uint64_t receiver, uint64_t code, uint64_t action ) { duobao thiscontract(receiver); if((code == N(eosio.token)) && (action == N(transfer))) { execute_action(&thiscontract, &duobao::buy); return; } if (code != receiver) return; switch (action) { EOSIO_API(duobao, (open)) }; eosio_exit(0); }}从代码可以看出,当执行的合约是 eosio.token ,行为是 transfer 的时候会触发 duobao 这个智能合约的 buy 函数。eosio.token 是系统提供的智能合约,每个账户都会默认运行一些系统级别智能合约,eosio.token 定义了 EOS 生态中和token相关的一些行为,包括转 EOS,发行新 token等。

2. 记录参与者

参与者数据记录在 multi_index 中,采用一个 betting 的数据结构,来存储。数据包括参与者账户,投注额度,备注等信息。

3. 随机数生成(开奖)

我们会觉得开奖是一个简单的过程,因为只要rand 函数调用一下,就可以生成一个随机数作为开奖结果。但是EOS规定,智能合约的代码执行结果是明确的,所以 rand 函数在编译过程中是不可识别的。智能用一些其他的方式模拟随机数字的生成,官方提供的例子中,对这个问题有一个方案,就是用户在提交的时候,随意给一个备注(memo),然后通过memo的hash来决定谁是赢家。但是这个方法对用户体验有影响,用户在转账的时候,还要加 memo 麻烦,所以这里 使用了 knight 的方案:

4. 发奖

发奖其实就是转账给中奖用户~,就是这段代码:

action(permission_level{_self, N(active)}, N(eosio.token), N(transfer), std::make_tuple(_self, item.owner, asset(total * win_ratio), item.memo) ).send();可以理解为从智能合约A调用智能合约B,本案是 duobao 智能合约调用 eosio.token 的 transfer 方法。而这个操作需要对合约授权:

cleos set account permission duobaoeos223 active '{"threshold": 1,"keys": [{"key":"EOS5XsJ5uf4TAL5FsHnsbb6cSSXNZtMQ3dymt2MhaYy1d4xdMX5Bp", "weight":1}],"accounts": [{"permission":{"actor":"duobaoeos223","permission":"eosio.code"},"weight":1}]}' owner -p duobaoeos223@owner


合约完整代码在:

交互

既然我们已经把游戏参与者数据写到链上,后续肯定希望某种方式把数据从链中读出来,前端需要展示,后端开发也需要查看,主要有 cleos 控制台和 rest api 方式获取数据。

cleos get table $accountname $accountname $tablenamecurl -XPOST "http://mainnet.eoscalgary.io/v1/chain/get_table_rows" --data '{"table":"$tablename","scope":"$accountname","code":"$accountname","limit":10,"json":true}'其中第二种方式可以用来获取数据给前端展示使用。官方还提供了另外一套API来监控EOS网络,在如下地址:

发布

合约编写、编译都完成后,就是发布了。发布过程中,可能遇到 RAM 不足,CPU不足的情况,这个时候需要使用EOS购买RAM,CPU或者NET。可以使用 imtoken 钱包购买,感觉EOS还是略坑的,因为购买这些东西略贵,买了好几百块钱的资源才能发布一个合约,略微有些坑。

到这里一款基于EOS智能合约的夺宝游戏就完成了,感兴趣的小伙伴可以试着玩玩。

关键词:游戏,生态

74
73
25
news

版权所有© 亿企邦 1997-2025 保留一切法律许可权利。

为了最佳展示效果,本站不支持IE9及以下版本的浏览器,建议您使用谷歌Chrome浏览器。 点击下载Chrome浏览器
关闭