How Does GoCryptobot’s PvP Run on the Blockchain? — PvP Source Code Analysis
The first mobile blockchain game, GoCryptobot(https://gocryptobot.io) was released. The game items are created and exchanged on a blockchain, and thus, the user will have 100% ownership upon purchasing an item. Furthermore, the items can be sold through the market at any time. The blockchain applications are not only limited to the Market, as users can play the PvP mode, where users compete with each other’s parts, and the results of the PvP can be transparently verified.
This article aims to analyze GoCryptobot’s PvP system’s source code. I wanted to explain how PvP is processed, including the score calculation algorithm, so that users can better enjoy the PvP contents. Furthermore, I hope to be of aid to those who are starting to study the blockchain.
PvP Source Code
GoCryptobot’s PvP source code is revealed in the GitHub link below. The source code is around 300 lines, and I plan to explain these lines in detail.
PvP Runs Transparently on the Blockchain Platform
The PvP involves 128 players being divided into 4 teams. Each player’s robot parts contribute to the team’s win depending on the part’s stats. The winning team receives prize money.
PvP has a total of 4 rounds in a single match. Each round calculates scores depending on a certain body part, level, skill level, and the color. At this point, the contract chooses one of the two random colors as the Lucky Color. If the player’s part matches the Lucky Color, then he/she receives bonus points. The team that scores the highest collectively will be the ultimate winners. If there happens to be an equal score among teams, the contract will randomly choose a team as the winner.
If you want to learn more about the the PvP, then check out this link.
The contracts being used in PvP are all divided into different files, and are modularized so that each contract inherits from the previous contract. Thus, the GoCryptobotCore is the final contract, and all data and functions are included within it.
contract GoCryptobotAccessControl
contract GoCryptobotRandom is GoCryptobotAccessControl
contract GoCryptobotScore is GoCryptobotRandom
contract GoCryptobotRounds is GoCryptobotScore
contract GoCryptobotCore is GoCryptobotRounds
GoCryptobotAccessControl: Managing the Owner that has Complete Authority
The Smart Contract cannot be edited once deployed, and thus, when problems arise, it is commonplace to implement a pattern that stops the contract itself. With this contract, the Owner can use the pause() function to pause the contract, and unpause() to reboot the contract.
GoCryptobotAccessControl only allows the owner to be able to call pause() and unpause(), and this is the modifier that defines this:
bool public paused;modifier onlyOwner() {
require(msg.sender == owner); _;
}
modifier onlyOperator() {
require(msg.sender == operator); _;
}
modifier onlyOwnerOrOperator() {
require(msg.sender == owner || msg.sender == operator); _;
}
modifier whenPaused() {require(paused); _;}
modifier whenNotPaused() {require(!paused); _;}function pause() public onlyOwnerOrOperator whenNotPaused {
paused = true;
}function unpause() public onlyOwner whenPaused {
paused = false;
}
The pause() function can only be called by the Owner and Operator as defined by the onlyOwnerOrOperator modifier.
There also exists a function that can change the Owner and the Operator.
address public owner;
address public operator;function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
owner = newOwner;
}function transferOperator(address newOperator) public onlyOwner {
require(newOperator != address(0));
operator = newOperator;
}
GoCryptobotRandom: For a Transparent PvP Experience
When it comes to Ethereum, no matter which node runs the code, the exact same result must be returned. This is how each and every transaction can be verified, and thus, random functions cannot exist. So how can we create a random function for our game, if there is a random factor in the game itself, while also needing to use the Ethereum blockchain?
This contract uses the Commitment Scheme to provide a random seed from the block hash, which can be used in the PvP.
Once the match starts, the game server that operates the PvP will call the contract function twice.
First, it will call the commitment() function, and at this point, once the transaction that called commitment has been mined in the Ethereum Network, the number will be stored in the commitmentNumber variable.
commitmentNumber = block.number;
The block.number is solidity’s special variable that brings the current block’s number.
Once the commitment function has been mined, the Operator will call the run() function that is within the GoCryptoBotRounds contract. Once the _initRandom() function within the run() function is called, the block that corresponds with the commitmentNumber will have its hash value used to set the random seed, randomBytes.
randomBytes = block.blockhash(commitmentNumber);
Here, the block.blockhash is a special function that provides the current block hash.
The _random256() function uses the keccak256 hash function to hash randomBytes so that a random number can be created.
GoCryptobotScore: An Attempt to Lower Gas Fees
This contract calculates the user’s PvP score. The unique part is how it stores and brings the user’s information. The reason this system was made this way is to reduce Ethereum’s gas usage.
In order to reduce gas usage, we first reduced the amount of data. To do this, we divided a single array into many offsets to store and use data.
Broadly speaking, the user data contains whether the Theme Effect is active, and whether all 4 parts are included. Each part’s data includes information about its 3 skills and its parts level and color.
If we were to visualize this data structure, it’s as below:
Furthermore, since the offset is used to bring data, and thus, a getter function is required.
*internal modifier can only be accessed from within the contract.
*private is similar to internet, but it cannot be accessed from an inherited contract.
The way PvP calculates the score is as follows:
- There are 4 types of colors, and each round offers a possibility of 2 colors. When the run() contract is called, a color is chosen out of the 2. The chosen color is the major color and the other is the minor color.
- If Theme Effect is inactive — if the part has the major color, then the part’s skill level’s sum times 3 is rewarded as the score. If the part has the minor color, then it will be times 1.
- If Theme Effect is active — if the part has the major color, then the part’s skill level’s sum times 4 is rewarded as the score. If the part has the minor color, then it will be times 2.
- Finally, the part’s level is added to the score, which results in the final score.
The actual function that calculates the PvP score is as follows:
GoCryptobotRound: Legitimate PvP Match Process
Once GoCryptobotRandom’s commitment transaction is mined, then it is time to actually continue the PvP process.
The PvP Operator will run the run() function, and its functionalities are as follows:
- Reset the random seed
- Randomly choose the Lucky Color(minor, major)
- Add the score
- Choose the winning team, and call the Completion Event.
As mentioned earlier, once _initRandom() is called, the block’s hash stored in the commitmentNumber within the GoCryptobotRandom contract can be used as a random seed.
Even in the function _shuffle(), which chooses each round’s major and minor colors, you can see that a lot of effort went into reducing the gas usage.
Random byte values can be continuously made by repeating the Keccak hash; however, in order to reduce gas usage, we hash it only once and get a random numbers in bytes.
random = uint8(randomBytes[i % 32]) % (deckLength - i);
_shuffle’s deck parater is an array that has a total of 8 numbers, each being either 0 or 1. By going through random swap 8 times, the Lucky Color array is mixed. The 4 numbers in the frontal index is used in each round’s major color.
_shuffle is used to choose the major and minor colors. Then each team’s total points and the winning team are decided.
uint[4] memory totalScores;
You can see that within the run() function, the keyword “memory” is added when declaring a variable. In Ethereum, variables such as arrays and structs are stored in the storage by default. The variables stored in the storage are not deleted even when the call is over. This also eats up a lot of gas. In contrast, variables that are stored in memory are deleted upon the end of the function, and in comparison, uses less gas. Thus, variables such as totalScores that are used temporarily should be stored in the memory for efficiency.
AllFinished(totalScores[0], totalScores[1], totalScores[2], totalScores[3]);WinnerTeam(candidates, candidates[0]);RoundFinished(eventType, eventMajorColor, eventMinorColor, scores[0], scores[1], scores[2], scores[3]);
Once score calculation is finished, AllFinished, WinnerTeam, and RoundFinished events are called, and the Operator gives the rewards to the appropriate users depending on the contract’s result’s event logs.
Time to Participate in PvP!
We have analyzed GoCryptobot PvP’s source code. The logic is very simple as in getting the user’s information and calculating a certain score out of it and returning the results. However, the contract’s source code is pretty complicated.
It was possible to make a transparent and verifiable PvP game through complex code shown above. Since you now know how the scores are being calculated, I hope the PvP became a bit more enjoyable overall.
GoCryptobot can be downloaded for each respective OS through the links below:
- App Store: https://apple.co/2KbMipn
- Google Play: https://bit.ly/2K5tYhx
(For the Korean version of this article, click here)