En este artículo, desglosaremos el proceso de cultivo de recursos. Basándonos en el código de staking de nuestro artículo anterior, introduciremos tablas y funciones adicionales específicas para el cultivo de recursos.
El primer paso es agregar una tabla a nuestro código para el almacenamiento de recursos. Esta tabla es clave para gestionar y rastrear los recursos que los jugadores recolectan y utilizan dentro del juego.
//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;
En la tabla de cultivo de recursos, tenemos los siguientes campos:
- key_id: Es el identificador único para cada recurso, representado como un número para facilitar las búsquedas en la tabla.
- amount: La cantidad del recurso específico.
- resource_name: El nombre del recurso, como "piedra", "madera", etc.
Además, utilizaremos una función auxiliar para convertir el nombre del recurso en un key_id
numérico. Esta función simplifica el proceso de gestionar y hacer referencia a los recursos en nuestra tabla.
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;
}
La función toma la cadena str
como parámetro de entrada y devuelve un entero sin signo de 64 bits de tipo uint64_t
. La función utiliza un algoritmo de hashing simple para convertir una cadena en un valor entero único.
- Inicializa una variable
hash
sin signo de 64 bits en 0. - Verifica la longitud de la cadena de entrada
str
. Si está vacía (longitud 0), devuelve el valor dehash
. - Itera a través de cada carácter en
str
.- Calcula y almacena el código ASCII de cada carácter en
char_s
. - Realiza la operación de hashing:
- Desplaza el valor de
hash
4 bits a la izquierda:hash << 4
. - Resta
hash
del valor desplazado:(hash << 4) - hash
. - Agrega el código ASCII del carácter:
+ char_s
.
- Desplaza el valor de
- Aplica
hash = hash & hash
para limitar el valor dehash
a 64 bits y evitar desbordamientos.
- Calcula y almacena el código ASCII de cada carácter en
- Devuelve el valor final de
hash
después de finalizar el bucle.
Luego, incorporaremos la función de reclamación en nuestro código para continuar con el procesamiento.
void claim(const name& owner, const uint64_t& farmingitem);
Y funciones de soporte para realizar la reclamación.
Explicación Paso a Paso
auto item_mdata = get_mdata(assets_itr);
Obtiene los metadatos del NFT utilizando el iteradorassets_itr
.const uint32_t& lastClaim = std::get<uint32_t>(item_mdata["lastClaim"]);
Obtiene el valor de "lastClaim" de los metadatos del NFT. Esto representa la última vez que se generó el recurso.std::pair<std::string, float> mined_resource;
Crea un objeto de tipostd::pair
, que se utilizará para almacenar el recurso extraído y su cantidad.if(time_now > lastClaim) { ... }
Verifica si ha pasado suficiente tiempo desde la última reclamación del recurso. Si es así, se toman pasos adicionales.auto item_template_idata = get_template_idata(assets_itr->template_id, assets_itr->collection_name);
Obtiene los datos de la plantilla del NFT utilizando su identificador y el nombre de la colección.Obtener los datos necesarios de la plantilla:
const float& miningRate = std::get<float>(item_template_idata["miningRate"]);
Obtiene la tasa de extracción del recurso.const std::string& farmResource = std::get<std::string>(item_template_idata["farmResource"]);
Obtiene el nombre del recurso a extraer.
const uint8_t& current_lvl = std::get<uint8_t>(item_mdata["level"]);
Obtiene el nivel actual del NFT de los metadatos.Calculando la tasa de extracción de recursos según el nivel:
Cfloat miningRate_according2lvl = miningRate; for(uint8_t i = 1; i < current_lvl; ++i) miningRate_according2lvl = miningRate_according2lvl + (miningRate_according2lvl * upgrade_percentage / 100);
Calculando la cantidad de recursos extraídos:
Cconst float& reward = (time_now - lastClaim) * miningRate_according2lvl;
item_mdata["lastClaim"] = time_now;
Actualiza el valor de "lastClaim" en los metadatos del NFT al tiempo actual.update_mdata(assets_itr, item_mdata, get_self());
Actualiza los metadatos del NFT con el nuevo valor de "lastClaim".Llenar el objeto
mined_resource
con los datos del recurso extraído y su cantidad.return mined_resource;
Devuelve el objetomined_resource
como resultado de la función.
Función para Incrementar el Balance de Recursos
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;
});
}
}
}
Descripción de la Funcionalidad
resources_t resources_table(get_self(), owner.value);
Declaración e inicialización del objeto de tablaresources_t
, que se utiliza para almacenar los recursos del propietario del juego (owner
). Se crea un objeto de tabla para el contrato usando el ID del propietario.for(const auto& map_itr : resources) { ... }
Un bucle que recorre todos los pares clave-valor en el diccionario de entradaresources
.const uint64_t& key_id = stringToUint64(map_itr.first);
Obtiene un identificador único (key_id
) para un recurso basado en el nombre del recurso del diccionario de entrada. Se utiliza la funciónstringToUint64
proporcionada anteriormente.auto resources_table_itr = resources_table.find(key_id);
Busca una entrada en la tabla con elkey_id
recibido.if(resources_table_itr == std::end(resources_table)) { ... }
Verifica si existe una entrada para elkey_id
especificado en la tabla.Si no se encuentra el registro (rama
if
):resources_table.emplace(get_self(), [&](auto &new_row) { ... });
Agrega un nuevo registro a la tabla usando la funciónemplace
. El registro contiene unkey_id
único, el nombre del recurso y su cantidad.
Si el registro existe (rama
else
):resources_table.modify(resources_table_itr, get_self(), [&](auto &new_row) { ... });
Modifica una entrada existente en la tabla, aumentando su cantidad por el valor del diccionario de entrada.
Función de Reclamación
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 es el porcentaje de aumento en la tasa de minería por cada nivel
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);
Verifica si el usuario que llamó a la función tiene suficientes derechos de autorización para el propietario (owner
).staked_t staked_table(get_self(), owner.value);
Declaración e inicialización de la tablastaked_t
para rastrear los elementos anidados del propietario del juego (owner
).auto staked_table_itr = staked_table.require_find(farmingitem, "Could not find staked farming item");
Busca una entrada en la tabla de elementos anidados usando el identificador únicofarmingitem
. Si no se encuentra el registro, se genera un error.auto assets = atomicassets::get_assets(get_self());
Obtiene todos los activos usando la funciónatomicassets::get_assets
.auto assets_itr = assets.find(farmingitem);
Busca un activo con el identificador únicofarmingitem
en la colección de activos.auto farmingitem_mdata = get_mdata(assets_itr);
Obtiene los metadatos del activo especificado.float miningBoost = 1;
Inicializa la variableminingBoost
con el valor 1.if(farmingitem_mdata.find("miningBoost") != std::end(farmingitem_mdata)) miningBoost = std::get<float>(farmingitem_mdata["miningBoost"]);
Verifica la presencia de la clave "miningBoost" en los metadatos del activo y actualizaminingBoost
si está presente.std::map<std::string, float> mined_resources;
Crea un diccionario para almacenar los recursos extraídos, donde la clave es el nombre del recurso y el valor es su cantidad.const uint32_t& time_now = current_time_point().sec_since_epoch();
Obtiene el tiempo actual en segundos desde la época.Bucle a través de cada elemento en staking:
const std::pair<std::string, float> item_reward = claim_item(assets_itr, 2, time_now);
Llama a la funciónclaim_item
para obtener una recompensa por la minería del activo especificado.if(item_reward != std::pair<std::string,float>()) if(item_reward.second > 0) mined_resources[item_reward.first] += item_reward.second;
Agrega un recurso extraído al diccionariomined_resources
si se ha recibido la recompensa y es positiva.
check(mined_resources.size() > 0, "Nothing to claim");
Verifica si hay algo que reclamar usando la funcióncheck
.increase_owner_resources_balance(owner, mined_resources);
Llama a la funciónincrease_owner_resources_balance
para aumentar el balance de recursos del propietario del juego.
Recursos Adicionales
Para una referencia completa y ejemplos adicionales, puedes encontrar más información en el repositorio de staking y cultivo.