In this article, we'll break down the resource farming process. Building on the staking code from our previous article, we'll introduce additional tables and functions specific to resource farming.
The first step is adding a table to our code for resource storage. This table is key to managing and tracking the resources players collect and use within the game.
//scope: owner
struct [[eosio::table]] resources_j
{
uint64_t key_id;
float amount;
std::string resource_name;
uint64_t primary_key() const { return key_id; }
};
typedef multi_index< "resources"_n, resources_j > resources_t;
In the resource farming table, we have the following fields:
- key_id: This is the unique identifier for each resource, represented as a number to facilitate table searches.
- amount: The quantity of the specific resource.
- resource_name: The name of the resource, such as "stone", "wood", etc.
Additionally, we'll use a helper function to convert the resource name into a numerical key_id
. This function simplifies the process of managing and referencing resources in our table.
const uint64_t pixelfarm::stringToUint64(const std::string& str)
{
uint64_t hash = 0;
if (str.size() == 0) return hash;
for (int i = 0; i < str.size(); ++i)
{
int char_s = str[i];
hash = ((hash << 4) - hash) + char_s;
hash = hash & hash;
}
return hash;
}
The function takes the string str
as an input parameter and returns a 64-bit unsigned integer of type uint64_t
. The function uses a simple hashing algorithm to convert a string into a unique integer value.
- Initialize a 64-bit unsigned
hash
variable to 0. - Check the length of the input string
str
. If it's empty (length 0), return thehash
value. - Iterate through each character in
str
.- Calculate and store the ASCII code of each character in
char_s
. - Perform the hashing operation:
- Shift
hash
value 4 bits left:hash << 4
. - Subtract
hash
from the shifted value:(hash << 4) - hash
. - Add the ASCII code of the character:
+ char_s
.
- Shift
- Apply
hash = hash & hash
to limit thehash
value to 64 bits and prevent overflow.
- Calculate and store the ASCII code of each character in
- Return the final
hash
value after the loop ends.
Next, we will incorporate the stamp function into our code for further processing.
void claim(const name& owner, const uint64_t& farmingitem);
And support functions for claiming.
Step-by-Step Explanation
auto item_mdata = get_mdata(assets_itr);
Getting metadata for NFT using theassets_itr
iterator.const uint32_t& lastClaim = std::get<uint32_t>(item_mdata["lastClaim"]);
Getting the value of "lastClaim" from the NFT metadata. This represents the time of the last output of the resource.std::pair<std::string, float> mined_resource;
Creating an object of typestd::pair
, which will be used to store the mined resource and its quantity.if(time_now > lastClaim) { ... }
Checking whether time has passed since the last claim of the resource. If so, additional steps are taken.auto item_template_idata = get_template_idata(assets_itr->template_id, assets_itr->collection_name);
Obtaining template data for NFT using its identifier and collection name.Obtaining the necessary data from the template:
const float& miningRate = std::get<float>(item_template_idata["miningRate"]);
Getting the resource mining rate.const std::string& farmResource = std::get<std::string>(item_template_idata["farmResource"]);
Get the name of the resource to mine.
const uint8_t& current_lvl = std::get<uint8_t>(item_mdata["level"]);
Obtaining the current level of NFT from metadata.Calculating the rate of resource extraction according to the level:
Cfloat miningRate_according2lvl = miningRate; for(uint8_t i = 1; i < current_lvl; ++i) miningRate_according2lvl = miningRate_according2lvl + (miningRate_according2lvl * upgrade_percentage / 100);
Calculating the amount of extracted resource:
Cconst float& reward = (time_now - lastClaim) * miningRate_according2lvl;
item_mdata["lastClaim"] = time_now;
Update the value of "lastClaim" in the NFT metadata to the current time.update_mdata(assets_itr, item_mdata, get_self());
Update NFT metadata with the new value "lastClaim".Filling the
mined_resource
object with data on the mined resource and its quantity.return mined_resource;
Return of themined_resource
object as a result of the function.
Function to Increase Resource Balance
void game::increase_owner_resources_balance(const name& owner, const std::map<std::string, float>& resources)
{
resources_t resources_table(get_self(), owner.value);
for(const auto& map_itr : resources)
{
const uint64_t& key_id = stringToUint64(map_itr.first);
auto resources_table_itr = resources_table.find(key_id);
if(resources_table_itr == std::end(resources_table))
{
resources_table.emplace(get_self(), [&](auto &new_row)
{
new_row.key_id = key_id;
new_row.resource_name = map_itr.first;
new_row.amount = map_itr.second;
});
}
else
{
resources_table.modify(resources_table_itr, get_self(), [&](auto &new_row)
{
new_row.amount += map_itr.second;
});
}
}
}
Function Overview
resources_t resources_table(get_self(), owner.value);
Declaration and initialization of theresources_t
table object, which is used to store the resources of the game owner (owner
). A table object is created for the contract using the owner ID.for(const auto& map_itr : resources) { ... }
A loop that goes through all key-value pairs in the input dictionaryresources
.const uint64_t& key_id = stringToUint64(map_itr.first);
Get a unique identifier (key_id
) for a resource based on the resource name from the input dictionary. ThestringToUint64
function you provided earlier is used.auto resources_table_itr = resources_table.find(key_id);
Search for an entry in the table by the receivedkey_id
.if(resources_table_itr == std::end(resources_table)) { ... }
Checking whether an entry for the specifiedkey_id
exists in the table.If the record is not found (
if
branch):resources_table.emplace(get_self(), [&](auto &new_row) { ... });
Adding a new record to the table using theemplace
function. The record contains a uniquekey_id
, the name of the resource, and its quantity.
If the record exists (
else
branch):resources_table.modify(resources_table_itr, get_self(), [&](auto &new_row) { ... });
Modification of an existing entry in the table, increasing its amount by the value from the input dictionary.
Claim Function
void pixelfarm::claim(const name& owner, const uint64_t& farmingitem)
{
require_auth(owner);
staked_t staked_table(get_self(), owner.value);
auto staked_table_itr = staked_table.require_find(farmingitem, "Could not find staked farming item");
auto assets = atomicassets::get_assets(get_self());
auto assets_itr = assets.find(farmingitem);
// to get mining boost
auto farmingitem_mdata = get_mdata(assets_itr);
float miningBoost = 1;
if(farmingitem_mdata.find("miningBoost") != std::end(farmingitem_mdata))
miningBoost = std::get<float>(farmingitem_mdata["miningBoost"]);
// first - resource name, second - resource amount
std::map<std::string, float> mined_resources;
const uint32_t& time_now = current_time_point().sec_since_epoch();
for(const uint64_t& item_to_collect : staked_table_itr->staked_items)
{
auto assets_itr = assets.find(item_to_collect);
const std::pair<std::string, float> item_reward = claim_item(assets_itr, 2, time_now); // 2 is the percentage of increase in mining rate for each level
if(item_reward != std::pair<std::string,float>())
if(item_reward.second > 0)
mined_resources[item_reward.first] += item_reward.second;
}
check(mined_resources.size() > 0, "Nothing to claim");
increase_owner_resources_balance(owner, mined_resources);
}
require_auth(owner);
Checking whether the user who called the function has sufficient authorization rights for the owner (owner
).staked_t staked_table(get_self(), owner.value);
Declaration and initialization of thestaked_t
table to track nested elements of the game owner (owner
).auto staked_table_itr = staked_table.require_find(farmingitem, "Could not find staked farming item");
Search for an entry in the table of nested items using the unique identifierfarmingitem
. If the record is not found, an error is generated.auto assets = atomicassets::get_assets(get_self());
Get all assets usingatomicassets::get_assets
function.auto assets_itr = assets.find(farmingitem);
Search for an asset with the unique identifierfarmingitem
in the asset collection.auto farmingitem_mdata = get_mdata(assets_itr);
Get metadata for the specified asset.float miningBoost = 1;
Initialize theminingBoost
variable with the value 1.if(farmingitem_mdata.find("miningBoost") != std::end(farmingitem_mdata)) miningBoost = std::get<float>(farmingitem_mdata["miningBoost"]);
Checking the presence of the "miningBoost" key in asset metadata and updateminingBoost
if present.std::map<std::string, float> mined_resources;
Creating a dictionary to store mined resources, where the key is the name of the resource, and the value is its quantity.const uint32_t& time_now = current_time_point().sec_since_epoch();
Get the current time in seconds since the epoch.Loop through each staked item:
const std::pair<std::string, float> item_reward = claim_item(assets_itr, 2, time_now);
Call theclaim_item
function to get a reward for mining from the specified asset.if(item_reward != std::pair<std::string,float>()) if(item_reward.second > 0) mined_resources[item_reward.first] += item_reward.second;
Adding a mined resource to the dictionarymined_resources
if the reward has been received and is positive.
check(mined_resources.size() > 0, "Nothing to claim");
Checking if there is anything to claim using thecheck
function.increase_owner_resources_balance(owner, mined_resources);
Call theincrease_owner_resources_balance
function to increase the resource balance of the game owner.
Additional Resources
For a complete reference and additional examples, you can find more information in the staking and farming repository.