摘要:接上篇文章,這里繼續(xù)學習高級理論。將這個函數(shù)的定義修改為其使用修飾符。我們將用一個到的隨機數(shù)來確定我們的戰(zhàn)斗結(jié)果。在這個教程中,簡單起見我們將這個狀態(tài)保存在結(jié)構(gòu)體中,將其命名為和。在第六章我們計算出來一個到的隨機數(shù)。
接上篇文章,這里繼續(xù)學習Solidity高級理論。一、重構(gòu)通用邏輯
不管誰調(diào)用我們的 attack 函數(shù) —— 我們想確保用戶的確擁有他們用來攻擊的僵尸。如果你能用其他人的僵尸來攻擊將是一個很大的安全問題。
你能想一下我們?nèi)绾翁砑右粋€檢查步驟來看看調(diào)用這個函數(shù)的人就是他們傳入的 _zombieId 的擁有者么?
想一想,看看你能不能自己找到一些答案。
花點時間…… 參考我們前面課程的代碼來獲得靈感。
答案我們在前面的課程里面已經(jīng)做過很多次這樣的檢查了。 在 changeName(), changeDna(), 和 feedAndMultiply()里,我們做過這樣的檢查:
require(msg.sender == zombieToOwner[_zombieId]);
這和我們 attack 函數(shù)將要用到的檢查邏輯是相同的。 正因我們要多次調(diào)用這個檢查邏輯,讓我們把它移到它自己的 modifier 中來清理代碼并避免重復(fù)編碼。
實戰(zhàn)演練我們回到了 zombiefeeding.sol, 因為這是我們第一次調(diào)用檢查邏輯的地方。讓我們把它重構(gòu)進它自己的 modifier。
1、創(chuàng)建一個 modifier, 命名為 ownerOf。它將傳入一個參數(shù), _zombieId (一個 uint)。
它的函數(shù)體應(yīng)該 require msg.sender 等于 zombieToOwner[_zombieId], 然后繼續(xù)這個函數(shù)剩下的內(nèi)容。 如果你忘記了修飾符的寫法,可以參考 zombiehelper.sol。
2、將這個函數(shù)的 feedAndMultiply 定義修改為其使用修飾符 ownerOf。
3、現(xiàn)在我們使用 modifier了,你可以刪除這行了: require(msg.sender == zombieToOwner[_zombieId]);
zombiefeeding.sol
pragma solidity ^0.4.19; import "./zombiefactory.sol"; contract KittyInterface { function getKitty(uint256 _id) external view returns ( bool isGestating, bool isReady, uint256 cooldownIndex, uint256 nextActionAt, uint256 siringWithId, uint256 birthTime, uint256 matronId, uint256 sireId, uint256 generation, uint256 genes ); } contract ZombieFeeding is ZombieFactory { KittyInterface kittyContract; // 1. 在這里創(chuàng)建 modifier modifier ownerOf(uint _zombieId) { require(msg.sender == zombieToOwner[_zombieId]); _; } function setKittyContractAddress(address _address) external onlyOwner { kittyContract = KittyInterface(_address); } function _triggerCooldown(Zombie storage _zombie) internal { _zombie.readyTime = uint32(now + cooldownTime); } function _isReady(Zombie storage _zombie) internal view returns (bool) { return (_zombie.readyTime <= now); } // 2. 在函數(shù)定義時增加 modifier : function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal ownerOf(_zombieId) { // 3. 移除這一行 // require(msg.sender == zombieToOwner[_zombieId]); Zombie storage myZombie = zombies[_zombieId]; require(_isReady(myZombie)); _targetDna = _targetDna % dnaModulus; uint newDna = (myZombie.dna + _targetDna) / 2; if (keccak256(_species) == keccak256("kitty")) { newDna = newDna - newDna % 100 + 99; } _createZombie("NoName", newDna); _triggerCooldown(myZombie); } function feedOnKitty(uint _zombieId, uint _kittyId) public { uint kittyDna; (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId); feedAndMultiply(_zombieId, kittyDna, "kitty"); } }更多重構(gòu)
在 zombiehelper.sol 里有幾處地方,需要我們實現(xiàn)我們新的 modifier—— ownerOf。
實戰(zhàn)演練:
1、修改 changeName() 使其使用 ownerOf
2、修改 changeDna() 使其使用 ownerOf
zombiehelper.sol
pragma solidity ^0.4.19; import "./zombiefeeding.sol"; contract ZombieHelper is ZombieFeeding { uint levelUpFee = 0.001 ether; modifier aboveLevel(uint _level, uint _zombieId) { require(zombies[_zombieId].level >= _level); _; } function withdraw() external onlyOwner { owner.transfer(this.balance); } function setLevelUpFee(uint _fee) external onlyOwner { levelUpFee = _fee; } function levelUp(uint _zombieId) external payable { require(msg.value == levelUpFee); zombies[_zombieId].level++; } // 1. 使用 `ownerOf` 修改這個函數(shù): function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) ownerOf(_zombieId) { // require(msg.sender == zombieToOwner[_zombieId]); zombies[_zombieId].name = _newName; } // 2. 對這個函數(shù)做同樣的事: function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) ownerOf(_zombieId) { // require(msg.sender == zombieToOwner[_zombieId]); zombies[_zombieId].dna = _newDna; } function getZombiesByOwner(address _owner) external view returns(uint[]) { uint[] memory result = new uint[](ownerZombieCount[_owner]); uint counter = 0; for (uint i = 0; i < zombies.length; i++) { if (zombieToOwner[i] == _owner) { result[counter] = i; counter++; } } return result; } }二、回到攻擊
重構(gòu)完成了,回到我們上節(jié)博文學習的游戲?qū)?zhàn) zombieattack.sol。
繼續(xù)來完善我們的 attack 函數(shù), 現(xiàn)在我們有了 ownerOf 修飾符來用了。
實戰(zhàn)演練1、將 ownerOf 修飾符添加到 attack 來確保調(diào)用者擁有_zombieId.
2、我們的函數(shù)所需要做的第一件事就是獲得一個雙方僵尸的 storage 指針, 這樣我們才能很方便和它們交互:
a. 定義一個 Zombie storage 命名為 myZombie,使其值等于 zombies[_zombieId]。
b. 定義一個 Zombie storage 命名為 enemyZombie, 使其值等于 zombies[_targetId]。
3、我們將用一個0到100的隨機數(shù)來確定我們的戰(zhàn)斗結(jié)果。 定義一個 uint,命名為 rand, 設(shè)定其值等于 randMod 函數(shù)的返回值,此函數(shù)傳入 100作為參數(shù)。
zombieattack.sol
pragma solidity ^0.4.19; import "./zombiehelper.sol"; contract ZombieBattle is ZombieHelper { uint randNonce = 0; uint attackVictoryProbability = 70; function randMod(uint _modulus) internal returns(uint) { randNonce++; return uint(keccak256(now, msg.sender, randNonce)) % _modulus; } // 1. 在這里增加 modifier function attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) { // 2. 在這里開始定義函數(shù) Zombie storage myZombie = zombies[_zombieId]; Zombie storage enemyZombie = zombies[_targetId]; uint rand = randMod(100); } }三、輸贏排行榜
對我們的僵尸游戲來說,我們將要追蹤我們的僵尸輸贏了多少場。有了這個我們可以在游戲里維護一個 "僵尸排行榜"。
有多種方法在我們的DApp里面保存一個數(shù)值 — 作為一個多帶帶的映射,作為一個“排行榜”結(jié)構(gòu)體,或者保存在 Zombie 結(jié)構(gòu)體內(nèi)。
每個方法都有其優(yōu)缺點,取決于我們打算如何和這些數(shù)據(jù)打交道。在這個教程中,簡單起見我們將這個狀態(tài)保存在 Zombie 結(jié)構(gòu)體中,將其命名為 winCount 和 lossCount。
我們跳回 zombiefactory.sol, 將這些屬性添加進 Zombie 結(jié)構(gòu)體.
實戰(zhàn)演練實戰(zhàn)演習
1、修改 Zombie 結(jié)構(gòu)體,添加兩個屬性:
a. winCount, 一個 uint16
b. lossCount, 也是一個 uint16
注意: 記住, 因為我們能在結(jié)構(gòu)體中包裝uint, 我們打算用適合我們的最小的 uint。 一個 uint8 太小了, 因為 2^8 = 256 —— 如果我們的僵尸每天都作戰(zhàn),不到一年就溢出了。但是 2^16 = 65536 (uint16)—— 除非一個僵尸連續(xù)179年每天作戰(zhàn),否則我們就是安全的。
2、現(xiàn)在我們的 Zombie 結(jié)構(gòu)體有了新的屬性, 我們需要修改 _createZombie() 中的函數(shù)定義。
修改僵尸生成定義,讓每個新僵尸都有 0 贏和 0 輸。
zombiefactory.sol
pragma solidity ^0.4.19; import "./ownable.sol"; contract ZombieFactory is Ownable { event NewZombie(uint zombieId, string name, uint dna); uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; uint cooldownTime = 1 days; struct Zombie { string name; uint dna; uint32 level; uint32 readyTime; // 1. 在這里添加新的屬性 uint16 winCount; uint16 lossCount; } Zombie[] public zombies; mapping (uint => address) public zombieToOwner; mapping (address => uint) ownerZombieCount; function _createZombie(string _name, uint _dna) internal { // 2. 在這里修改修改新僵尸的創(chuàng)建: uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime), 0, 0)) - 1; zombieToOwner[id] = msg.sender; ownerZombieCount[msg.sender]++; NewZombie(id, _name, _dna); } function _generateRandomDna(string _str) private view returns (uint) { uint rand = uint(keccak256(_str)); return rand % dnaModulus; } function createRandomZombie(string _name) public { require(ownerZombieCount[msg.sender] == 0); uint randDna = _generateRandomDna(_name); randDna = randDna - randDna % 100; _createZombie(_name, randDna); } }四、更新輸贏狀態(tài)
有了 winCount 和 lossCount,我們可以根據(jù)僵尸哪個僵尸贏了戰(zhàn)斗來更新它們了。
在第六章我們計算出來一個0到100的隨機數(shù)?,F(xiàn)在讓我們用那個數(shù)來決定那誰贏了戰(zhàn)斗,并以此更新我們的狀態(tài)。
實戰(zhàn)演練1、創(chuàng)建一個 if 語句來檢查 rand 是不是 小于或者等于 attackVictoryProbability。
2、如果以上條件為 true, 我們的僵尸就贏了!所以:
a. 增加 myZombie 的 winCount。
b. 增加 myZombie 的 level。 (升級了啦!!!!!!!)
c. 增加 enemyZombie 的 lossCount. (輸家!!!!!!)
d. 運行 feedAndMultiply 函數(shù)。 在 zombiefeeding.sol 里查看調(diào)用它的語句。 對于第三個參數(shù) (_species),傳入字符串 "zombie". (現(xiàn)在它實際上什么都不做,不過在稍后, 如果我們愿意,可以添加額外的方法,用來制造僵尸變的僵尸)。
zombieattack.sol
pragma solidity ^0.4.19; import "./zombiehelper.sol"; contract ZombieBattle is ZombieHelper { uint randNonce = 0; uint attackVictoryProbability = 70; function randMod(uint _modulus) internal returns(uint) { randNonce++; return uint(keccak256(now, msg.sender, randNonce)) % _modulus; } function attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) { Zombie storage myZombie = zombies[_zombieId]; Zombie storage enemyZombie = zombies[_targetId]; uint rand = randMod(100); // 在這里開始 if (rand <= attackVictoryProbability) { myZombie.winCount++; myZombie.level++; enemyZombie.lossCount++; feedAndMultiply(_zombieId, enemyZombie.dna, "zombie"); } } }五、失敗觸發(fā)冷卻
我們已經(jīng)編寫了你的僵尸贏了之后會發(fā)生什么, 該看看 輸了 的時候要怎么做了。
在我們的游戲中,僵尸輸了后并不會降級 —— 只是簡單地給 lossCount 加一,并觸發(fā)冷卻,等待一天后才能再次參戰(zhàn)。
要實現(xiàn)這個邏輯,我們需要一個 else 語句。
else 語句和 JavaScript 以及很多其他語言的 else 語句一樣。
if (zombieCoins[msg.sender] > 100000000) { // 你好有錢!!! } else { // 我們需要更多的僵尸幣... }實戰(zhàn)演練
1、添加一個 else 語句。 若我們的僵尸輸了:
a. 增加 myZombie 的 lossCount。
b. 增加 enemyZombie 的 winCount。
2、在 else 最后, 對 myZombie 運行 _triggerCooldown 方法。這讓每個僵尸每天只能參戰(zhàn)一次。
zombieattack.sol
pragma solidity ^0.4.19; import "./zombiehelper.sol"; contract ZombieBattle is ZombieHelper { uint randNonce = 0; uint attackVictoryProbability = 70; function randMod(uint _modulus) internal returns(uint) { randNonce++; return uint(keccak256(now, msg.sender, randNonce)) % _modulus; } function attack(uint _zombieId, uint _targetId) external ownerOf(_zombieId) { Zombie storage myZombie = zombies[_zombieId]; Zombie storage enemyZombie = zombies[_targetId]; uint rand = randMod(100); if (rand <= attackVictoryProbability) { myZombie.winCount++; myZombie.level++; enemyZombie.lossCount++; feedAndMultiply(_zombieId, enemyZombie.dna, "zombie"); } else { // 在這里開始 myZombie.lossCount++; enemyZombie.winCount++; _triggerCooldown(myZombie); } } }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/24130.html
摘要:第一個例子,在你把智能協(xié)議傳上以太坊之后,它就變得不可更改這種永固性意味著你的代碼永遠不能被調(diào)整或更新。允許將合約所有權(quán)轉(zhuǎn)讓給他人。為何要來驅(qū)動以太坊就像一個巨大緩慢但非常安全的電腦。 通過前邊的 Solidity 基礎(chǔ)語法學習,我們已經(jīng)有了Solidity編程經(jīng)驗,在這節(jié)就要學學 Ethereum 開發(fā)的技術(shù)細節(jié),編寫真正的 DApp 時必知的:智能協(xié)議的所有權(quán),Gas的花費,代碼優(yōu)...
摘要:接上篇文章,這里繼續(xù)學習高級理論。實戰(zhàn)演練我們來寫一個返回某玩家的整個僵尸軍團的函數(shù)。但這樣每做一筆交易,都會改變僵尸軍團的秩序。在這里開始五可支付截至目前,我們只接觸到很少的函數(shù)修飾符。 接上篇文章,這里繼續(xù)學習Solidity高級理論。 一、深入函數(shù)修飾符 接下來,我們將添加一些輔助方法。我們?yōu)槟鷦?chuàng)建了一個名為 zombiehelper.sol 的新文件,并且將 zombiefee...
摘要:引言給迷失在如何學習區(qū)塊鏈技術(shù)的同學一個指引,區(qū)塊鏈技術(shù)是隨比特幣誕生,因此要搞明白區(qū)塊鏈技術(shù),應(yīng)該先了解下比特幣。但區(qū)塊鏈技術(shù)不單應(yīng)用于比特幣,還有非常多的現(xiàn)實應(yīng)用場景,想做區(qū)塊鏈應(yīng)用開發(fā),可進一步閱讀以太坊系列。 本文始發(fā)于深入淺出區(qū)塊鏈社區(qū), 原文:區(qū)塊鏈技術(shù)學習指引 原文已更新,請讀者前往原文閱讀 本章的文章越來越多,本文是一個索引帖,方便找到自己感興趣的文章,你也可以使用左側(cè)...
摘要:和比特幣協(xié)議有所不同的是,以太坊的設(shè)計十分靈活,極具適應(yīng)性。超級賬本區(qū)塊鏈的商業(yè)應(yīng)用超級賬本超級賬本是基金會下的眾多項目中的一個。證書頒發(fā)機構(gòu)負責簽發(fā)撤 showImg(https://segmentfault.com/img/bV2ge9?w=900&h=385); 從比特幣開始 一個故事告訴你比特幣的原理及運作機制 這篇文章的定位會比較科普,盡量用類比的方法將比特幣的基本原理講出來...
摘要:以太坊開發(fā)高級語言學習。地址以太坊區(qū)塊鏈由賬戶組成,你可以把它想象成銀行賬戶。使用很安全,因為它具有以太坊區(qū)塊鏈的安全保障除非竊取與以太坊地址相關(guān)聯(lián)的私鑰,否則是沒有辦法修改其他人的數(shù)據(jù)的。 以太坊開發(fā)高級語言學習。 一、映射(Mapping)和地址(Address) 我們通過給數(shù)據(jù)庫中的僵尸指定主人, 來支持多玩家模式。 如此一來,我們需要引入2個新的數(shù)據(jù)類型:mapping(映射)...
閱讀 2183·2021-11-24 09:39
閱讀 2802·2021-07-29 13:49
閱讀 2328·2019-08-29 14:15
閱讀 2244·2019-08-29 12:40
閱讀 3322·2019-08-26 13:42
閱讀 643·2019-08-26 12:13
閱讀 2077·2019-08-26 11:41
閱讀 3355·2019-08-23 18:32