[WIP] add inventory parsing and adding to a char

main
Alex 2 weeks ago
parent 657ff9f35d
commit 8b50abda63

@ -0,0 +1,21 @@
// Copyright (c) 4lexKidd
// This software is licensed under the GPL v3 as to be fitting with the rathena project with the additional requirement
// that any distributed versions must include an attribution to the original author (4lexKidd) in all copies
// or substantial portions of the software.
#ifndef HELPER_INV_CONVERTER_ID_TO_AEGISNAME_HPP
#define HELPER_INV_CONVERTER_ID_TO_AEGISNAME_HPP
#include "json.hpp"
#include <common/cbasetypes.hpp>
#include <common/database.hpp>
#include <config/core.hpp>
#include <unordered_map>
#include <vector>
#include <memory>
#include <string>
//#include <common/mmo.hpp>
const char* convert_item_id_to_name(int item_id);
#endif /* HELPER_INV_CONVERTER_ID_TO_AEGISNAME_HPP */

@ -0,0 +1,823 @@
// Copyright (c) 4lexKidd
// This software is licensed under the GPL v3 as to be fitting with the rathena project with the additional requirement
// that any distributed versions must include an attribution to the original author (4lexKidd) in all copies
// or substantial portions of the software.
#include "helper_inventory.hpp"
#include <algorithm> //min / max
#include <cstdlib>
#include <cstring>
#include <unordered_map>
#include <array>
#include <vector>
#include <iostream>
#include <fstream>
#include <common/malloc.hpp>
#include <common/mmo.hpp>
#include <common/showmsg.hpp>
#include <common/socket.hpp>
#include <common/sql.hpp>
#include <common/strlib.hpp>
#include <common/utilities.hpp>
#include <common/utils.hpp>
#include "json.hpp" //
#include "brokk.hpp" // brokk_config
#include "char_creation_helpers.hpp"
using namespace rathena;
using json = nlohmann::json;
/// global defines
/// internal structure
typedef struct InventoryDB_SQL {
InventoryDB vtable; // public interface
Sql* bot_inventory; // SQL handle bot inventory storage
std::string db_hostname = "127.0.0.1";
uint16 db_port = 3306;
std::string db_username = "ragnarok";
std::string db_password = "ragnarok";
std::string db_database = "ragnarok";
std::string codepage = "";
// other settings
bool case_sensitive;
//table name
char inventory_db[32];
} InventoryDB_SQL;
/// internal structure
typedef struct InventoryDBIterator_SQL {
InventoryDBIterator vtable; // public interface
InventoryDB_SQL* db;
int char_id;
int last_inv_item_id;
} InventoryDBIterator_SQL;
/// internal functions
static bool bot_inv_item_db_sql_init(InventoryDB* self);
static void bot_inv_item_db_sql_destroy(InventoryDB* self);
static bool bot_inv_item_db_sql_get_property(InventoryDB* self, const char* key, char* buf, size_t buflen);
static bool bot_inv_item_db_sql_set_property(InventoryDB* self, const char* option, const char* value);
static bool bot_inv_item_db_sql_create(InventoryDB* self, struct bot_inv_item* item);
static bool bot_inv_item_db_sql_remove(InventoryDB* self, const uint32 char_id, const uint32 inventory_id);
//static bool bot_inv_item_db_sql_save(InventoryDB* self, const struct bot_inv_item* item);
static bool bot_inv_item_db_sql_load_num(InventoryDB* self, struct bot_inv_item* item);
static InventoryDBIterator* bot_inv_item_db_sql_iterator(InventoryDB* self, const uint32 char_id);
static void bot_inv_item_db_sql_iter_destroy(InventoryDBIterator* self);
static bool bot_inv_item_db_sql_iter_next(InventoryDBIterator* self, const uint32 char_id, struct bot_inv_item* item);
static bool bot_inv_item_db_fromsql(InventoryDB_SQL* db, struct bot_inv_item* item);
static bool bot_inv_item_db_tosql(InventoryDB_SQL* db, const struct bot_inv_item* item, bool is_new);
/// public constructor
InventoryDB* inventory_db_sql(void) {
InventoryDB_SQL* db = (InventoryDB_SQL*)aCalloc(1, sizeof(InventoryDB_SQL));
new(db) InventoryDB_SQL();
// set up the vtable
db->vtable.init = &bot_inv_item_db_sql_init;
db->vtable.destroy = &bot_inv_item_db_sql_destroy;
db->vtable.get_property = &bot_inv_item_db_sql_get_property;
db->vtable.set_property = &bot_inv_item_db_sql_set_property;
//db->vtable.save = &bot_inv_item_db_sql_save;
db->vtable.create = &bot_inv_item_db_sql_create;
db->vtable.remove = &bot_inv_item_db_sql_remove;
db->vtable.load_num = &bot_inv_item_db_sql_load_num;
db->vtable.iterator = &bot_inv_item_db_sql_iterator;
// initialize to default values
db->bot_inventory = nullptr;
// other settings
db->case_sensitive = false;
safestrncpy(db->inventory_db, "inventory", sizeof(db->inventory_db));
return &db->vtable;
}
/* ------------------------------------------------------------------------- */
/**
* Establish the database connection.
* @param self: pointer to db
*/
static bool bot_inv_item_db_sql_init(InventoryDB* self) {
InventoryDB_SQL* db = (InventoryDB_SQL*)self;
Sql* sql_handle;
db->bot_inventory = Sql_Malloc();
sql_handle = db->bot_inventory;
if (SQL_ERROR == Sql_Connect(sql_handle, db->db_username.c_str(), db->db_password.c_str(), db->db_hostname.c_str(), db->db_port, db->db_database.c_str()))
{
ShowError("Couldn't connect with uname='%s',host='%s',port='%hu',database='%s'\n",
db->db_username.c_str(), db->db_hostname.c_str(), db->db_port, db->db_database.c_str());
Sql_ShowDebug(sql_handle);
Sql_Free(db->bot_inventory);
db->bot_inventory = nullptr;
return false;
}
if (!db->codepage.empty() && SQL_ERROR == Sql_SetEncoding(sql_handle, db->codepage.c_str()))
Sql_ShowDebug(sql_handle);
return true;
}
/**
* Destroy the database and close the connection to it.
* @param self: pointer to db
*/
static void bot_inv_item_db_sql_destroy(InventoryDB* self){
InventoryDB_SQL* db = (InventoryDB_SQL*)self;
Sql_Free(db->bot_inventory);
db->bot_inventory = nullptr;
db->~InventoryDB_SQL();
aFree(db);
}
/**
* Get configuration information into buf.
* If the option is supported, adjust the internal state.
* @param self: pointer to db
* @param key: config keyword
* @param buf: value set of the keyword
* @param buflen: size of buffer to avoid out of bound
* @return true if successful, false if something has failed
*/
static bool bot_inv_item_db_sql_get_property(InventoryDB* self, const char* key, char* buf, size_t buflen)
{
InventoryDB_SQL* db = (InventoryDB_SQL*)self;
const char* signature;
signature = "brokk_server_";
if( strncmpi(key, signature, strlen(signature)) == 0 ) {
key += strlen(signature);
if( strcmpi(key, "ip") == 0 )
safesnprintf(buf, buflen, "%s", db->db_hostname.c_str());
else
if( strcmpi(key, "port") == 0 )
safesnprintf(buf, buflen, "%hu", db->db_port);
else
if( strcmpi(key, "id") == 0 )
safesnprintf(buf, buflen, "%s", db->db_username.c_str());
else
if( strcmpi(key, "pw") == 0 )
safesnprintf(buf, buflen, "%s", db->db_password.c_str());
else
if( strcmpi(key, "db") == 0 )
safesnprintf(buf, buflen, "%s", db->db_database.c_str());
else
if( strcmpi(key, "inventory_db") == 0 )
safesnprintf(buf, buflen, "%s", db->inventory_db);
else
return false;// not found
return true;
}
signature = "brokk_";
if( strncmpi(key, signature, strlen(signature)) == 0 ) {
key += strlen(signature);
if( strcmpi(key, "codepage") == 0 )
safesnprintf(buf, buflen, "%s", db->codepage.c_str());
else
if( strcmpi(key, "case_sensitive") == 0 )
safesnprintf(buf, buflen, "%d", (db->case_sensitive ? 1 : 0));
else
return false;// not found
return true;
}
return false;// not found
}
/**
* Read and set configuration.
* If the option is supported, adjust the internal state.
* @param self: pointer to db
* @param key: config keyword
* @param value: config value for keyword
* @return true if successful, false if something has failed
*/
static bool bot_inv_item_db_sql_set_property(InventoryDB* self, const char* key, const char* value) {
InventoryDB_SQL* db = (InventoryDB_SQL*)self;
const char* signature;
signature = "brokk_server_";
if( strncmp(key, signature, strlen(signature)) == 0 ) {
key += strlen(signature);
if( strcmpi(key, "ip") == 0 )
db->db_hostname = value;
else
if( strcmpi(key, "port") == 0 )
db->db_port = (uint16)strtoul( value, nullptr, 10 );
else
if( strcmpi(key, "id") == 0 )
db->db_username = value;
else
if( strcmpi(key, "pw") == 0 )
db->db_password = value;
else
if( strcmpi(key, "db") == 0 )
db->db_database = value;
else
if( strcmpi(key, "inventory_db") == 0 )
safestrncpy(db->inventory_db, value, sizeof(db->inventory_db));
else
return false;// not found
return true;
}
signature = "brokk_";
if( strncmpi(key, signature, strlen(signature)) == 0 ) {
key += strlen(signature);
if( strcmpi(key, "codepage") == 0 )
db->codepage = value;
else
if( strcmpi(key, "case_sensitive") == 0 )
db->case_sensitive = (config_switch(value)==1);
else
return false;// not found
return true;
}
return false;// not found
}
/**
* Create a new inventory entry.
* bot_item->char_id & -> item_id are primary keys,
* and its values will be written to bot_inventpory
* @param self: pointer to db
* @param item: pointer of bot_inv_item to save
* @return true if successful, false if something has failed
*/
static bool bot_inv_item_db_sql_create(InventoryDB* self, struct bot_inv_item* item) {
InventoryDB_SQL* db = (InventoryDB_SQL*)self;
Sql* sql_handle_bot = db->bot_inventory;
// validate char id and skill_id to assign
uint32 char_id, item_id;
char_id = item->char_id;
item_id = item->item_id;
// zero value is prohibited
if(char_id == 0 || item_id == 0 )
return false;
return bot_inv_item_db_tosql(db, item, true);
}
/**
* Delete an existing bot inventory entry.
* @param self: pointer to db
* @param char_id: id of char, the item belongs to
* @param inventory_id: if of item to delete
* @return true if successful, false if something has failed
*/
static bool bot_inv_item_db_sql_remove(InventoryDB* self, const uint32 char_id, const uint32 inventory_id) {
InventoryDB_SQL* db = (InventoryDB_SQL*)self;
Sql* sql_handle = db->bot_inventory;
bool result = false;
if( SQL_SUCCESS != Sql_QueryStr(sql_handle, "START TRANSACTION")
|| SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = %d AND `id` = %d", db->inventory_db, char_id, inventory_id)
)
Sql_ShowDebug(sql_handle);
else
result = true;
result &= ( SQL_SUCCESS == Sql_QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") );
return result;
}
/**
* Retrieve data from db and store it in the provided data structure.
* Filled data structure is done by delegation to bot_inv_item_db_fromsql.
* @param self: pointer to db
* @param bot_inv_item: pointer of bot_inv_item to fill
* @param char_id: id of character
* @param item_id: id of item
* @return true if successful, false if something has failed
*/
static bool bot_inv_item_db_sql_load_num(InventoryDB* self, struct bot_inv_item* item) {
InventoryDB_SQL* db = (InventoryDB_SQL*)self;
return bot_inv_item_db_fromsql(db, item);
}
/**
* Create a new forward iterator.
* @param self: pointer to db iterator
* @return a new db iterator
*/
static InventoryDBIterator* bot_inv_item_db_sql_iterator(InventoryDB* self, const uint32 char_id) {
InventoryDB_SQL* db = (InventoryDB_SQL*)self;
InventoryDBIterator_SQL* iter = (InventoryDBIterator_SQL*)aCalloc(1, sizeof(InventoryDBIterator_SQL));
// set up the vtable
iter->vtable.destroy = &bot_inv_item_db_sql_iter_destroy;
iter->vtable.next = &bot_inv_item_db_sql_iter_next;
// fill data
iter->db = db;
iter->char_id = char_id;
iter->last_inv_item_id = -1;
return &iter->vtable;
}
/**
* Destroys this iterator, releasing all allocated memory (including itself).
* @param self: pointer to db iterator
*/
static void bot_inv_item_db_sql_iter_destroy(InventoryDBIterator* self) {
InventoryDBIterator_SQL* iter = (InventoryDBIterator_SQL*)self;
aFree(iter);
}
/**
* Fetches the next inv item entry in the database.
* @param self: pointer to db iterator
* @param char_id: char_id of the bot the items belong to
* @param inventory_id: id of the inventory table the item belongs to
* @return true if next inventory entry found and filled, false if something has failed
*/
static bool bot_inv_item_db_sql_iter_next(InventoryDBIterator* self, const uint32 char_id, struct bot_inv_item* item) {
InventoryDBIterator_SQL* iter = (InventoryDBIterator_SQL*)self;
InventoryDB_SQL* db = iter->db;
Sql* sql_handle = db->bot_inventory;
char* data;
// get next inventory ID
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id` FROM `%s` WHERE `char_id` = %d AND `id` > '%d' ORDER BY `id` ASC LIMIT 1",
db->inventory_db, iter->char_id, iter->last_inv_item_id) )
{
Sql_ShowDebug(sql_handle);
return false;
}
if( SQL_SUCCESS == Sql_NextRow(sql_handle) &&
SQL_SUCCESS == Sql_GetData(sql_handle, 0, &data, nullptr) &&
data != nullptr )
{// get item data
uint32 inventory_id;
inventory_id = atoi(data); uint32 skill_id;
if( bot_inv_item_db_fromsql(db, item) )
{
iter->last_inv_item_id = inventory_id;
Sql_FreeResult(sql_handle);
return true;
}
}
Sql_FreeResult(sql_handle);
return false;
}
/**
* Fetch a struct bot_inv_item from sql.
* @param db: pointer to db
* @param item: pointer of bot_inv_item to fill
* @param char_id: id of character, item belong to
* @param item_id: id of item to take data from
* @return true if successful, false if something has failed
*/
static bool bot_inv_item_db_fromsql(InventoryDB_SQL* db, struct bot_inv_item* item) {
Sql* sql_handle = db->bot_inventory;
char* data;
// retrieve login entry for the specified account
if( SQL_ERROR == Sql_Query(sql_handle,
#ifdef VIP_ENABLE
"SELECT `id`, `char_id`, `nameid`, `amount`, `equip`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `enchantgrade` FROM `%s` WHERE `char_id` = %d AND `id` = %d",
#else
"SELECT `id`, `char_id`, `nameid`, `amount`, `equip`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `enchantgrade` FROM `%s` WHERE `char_id` = %d AND `id` = %d",
#endif
db->inventory_db, item->char_id, item->inventory_id )
) {
Sql_ShowDebug(sql_handle);
return false;
}
if( SQL_SUCCESS != Sql_NextRow(sql_handle) )
{// no such entry
Sql_FreeResult(sql_handle);
return false;
}
Sql_GetData(sql_handle, 0, &data, nullptr); item->inventory_id = atoi(data);
Sql_GetData(sql_handle, 1, &data, nullptr); item->char_id = atoi(data);
Sql_GetData(sql_handle, 2, &data, nullptr); item->item_id = atoi(data);
Sql_GetData(sql_handle, 3, &data, nullptr); item->amount = atoi(data);
Sql_GetData(sql_handle, 4, &data, nullptr); item->equip = atoi(data);
Sql_GetData(sql_handle, 5, &data, nullptr); item->refine = atoi(data);
Sql_GetData(sql_handle, 6, &data, nullptr); item->attribute = atoi(data);
Sql_GetData(sql_handle, 7, &data, nullptr); item->card0 = atoi(data);
Sql_GetData(sql_handle, 8, &data, nullptr); item->card1 = atoi(data);
Sql_GetData(sql_handle, 9, &data, nullptr); item->card2 = atoi(data);
Sql_GetData(sql_handle, 10, &data, nullptr); item->card3 = atoi(data);
Sql_GetData(sql_handle, 11, &data, nullptr); item->enchantgrade = atoi(data);
//endif
Sql_FreeResult(sql_handle);
//Other fields are more or less irrelevant for bot purposes -> therefor they are pre-filled with default values
item->identify = 1; //identify = 1 means is successfully identified
item->expire_time = 0; //Dont expire
item->favorite = 0;
item->bound = 0;
item->unique_id = 0;
item->equip_switch = 0;
item->option_id0 = 0;
item->option_parm0 = 0;
item->option_val0 = 0;
item->option_id1 = 0;
item->option_parm1 = 0;
item->option_val1 = 0;
item->option_id2 = 0;
item->option_parm2 = 0;
item->option_val2 = 0;
item->option_id3 = 0;
item->option_parm3 = 0;
item->option_val3 = 0;
item->option_id4 = 0;
item->option_parm4 = 0;
item->option_val4 = 0;
return true;
}
/**
* Save a struct bot_inv_item in sql.
* @param db: pointer to db
* @param bot_inv_item: pointer of bot_inv_item to save
* @üaram char_id: char_id of the character the item belongs to
* @param is_new: if it's a new entry or should we update
* @return true if successful, false if something has failed
*/
static bool bot_inv_item_db_tosql(InventoryDB_SQL* db, const struct bot_inv_item* item, bool is_new) {
Sql* sql_handle = db->bot_inventory;
SqlStmt* stmt = SqlStmt_Malloc(sql_handle);
bool result = false;
// try
do
{
if( SQL_SUCCESS != Sql_QueryStr(sql_handle, "START TRANSACTION") )
{
Sql_ShowDebug(sql_handle);
break;
}
if( is_new )
{// insert into account table
if( SQL_SUCCESS != SqlStmt_Prepare(stmt,
#ifdef VIP_ENABLE
"INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `enchantgrade`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
#else
"INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `enchantgrade`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
#endif
db->inventory_db)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_UINT, (void*)&item->char_id, sizeof(item->char_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_UINT, (void*)&item->item_id, sizeof(item->item_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_UINT, (void*)&item->amount, sizeof(item->amount))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_UINT, (void*)&item->equip, sizeof(item->equip))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 4, SQLDT_INT, (void*)&item->identify, sizeof(item->identify))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 5, SQLDT_UINT8, (void*)&item->refine, sizeof(item->refine))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 6, SQLDT_UINT8, (void*)&item->attribute, sizeof(item->attribute))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 7, SQLDT_UINT, (void*)&item->card0, sizeof(item->card0))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 8, SQLDT_UINT, (void*)&item->card1, sizeof(item->card1))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 9, SQLDT_UINT, (void*)&item->card2, sizeof(item->card2))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 10, SQLDT_UINT, (void*)&item->card3, sizeof(item->card3))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_UINT8, (void*)&item->enchantgrade, sizeof(item->enchantgrade))
//ifdef VIP_ENABLE
//endif
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
break;
}
}
else
{ // update is bot state (meaning if controlled by sindri or by other means)
// effectively there should be no reason to every change a bots loadout after creation.
// Per default, amount of an allready existing item is updated here but currently (01/25) no valid usecase is defined yet
if( SQL_SUCCESS != SqlStmt_Prepare(stmt,
#ifdef VIP_ENABLE
"UPDATE `%s` SET `amount`=? WHERE `char_id` = '%d' AND `id` = '%d'",
#else
"UPDATE `%s` SET `amount`=? WHERE `char_id` = '%d' AND `id` = '%d'",
#endif
db->inventory_db, item->char_id, item->inventory_id)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_UINT32, (void*)item->amount, sizeof(item->amount))
#ifdef VIP_ENABLE
#endif
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
break;
}
}
// if we got this far, everything was successful
result = true;
} while (0);
// finally
result &= (SQL_SUCCESS == Sql_QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK"));
SqlStmt_Free(stmt);
return result;
}
/**
* Sets default values for a bot_inv_item
* @param item: pointer to bot_inv_item
*/
void set_bot_inv_item_defaults(bot_inv_item& item) {
item.inventory_id = -1;
item.char_id = 0;
item.item_id = 512; // Default apple
item.amount = 0;
item.equip = 0;
item.identify = 1; // Default indentified
item.refine = 0;
item.attribute = 0;
item.card0 = 0;
item.card1 = 0;
item.card2 = 0;
item.card3 = 0;
item.option_id0 = 0;
item.option_val0 = 0;
item.option_parm0 = 0;
item.option_id1 = 0;
item.option_val1 = 0;
item.option_parm1 = 0;
item.option_id2 = 0;
item.option_val2 = 0;
item.option_parm2 = 0;
item.option_id3 = 0;
item.option_val3 = 0;
item.option_parm3 = 0;
item.option_id4 = 0;
item.option_val4 = 0;
item.option_parm4 = 0;
item.expire_time = 0;
item.favorite = 0;
item.bound = 0;
item.unique_id = 0;
item.equip_switch = 0;
item.enchantgrade = 0;
}
int convert_item_aegisname_to_id(const char* aegisname)
{
// Open the mapping JSON file
std::ifstream bot_itemdb_aegisname_to_id_file("conf/bot_helper_jsons/itemdb_converter_aegisname_to_id.json");
if (!bot_itemdb_aegisname_to_id_file.is_open()) {
ShowError("Failed to open itemdb_converter_aegisname_to_id.json\n");
return 0;
}
// Parse the JSON file
json json_itemdb_aegisname_to_id;
try {
bot_itemdb_aegisname_to_id_file >> json_itemdb_aegisname_to_id;
}
catch (json::parse_error& e) {
ShowError("Error parsing bot_accounts.json: %s\n", e.what());
return false;
}
// Close file after done with it
bot_itemdb_aegisname_to_id_file.close();
//ShowStatus("Mapping aegisname to id...\n");
if (!json_itemdb_aegisname_to_id.contains(aegisname)) {
// Error: aegisname is not inside json file
ShowWarning("Aegisname \"%s\" was not found inside the itemdb. Skipped...", aegisname);
return 0;
}
const int item_id = json_itemdb_aegisname_to_id[aegisname];
//ShowStatus("aegisname found for Item_id %d\n",item_id);
return item_id;
}
std::string convert_item_id_to_name(int item_id)
{
// Open the mapping JSON file
std::ifstream bot_itemdb_item_id_to_name_file("conf/bot_helper_jsons/itemdb_converter_id_to_name.json");
if (!bot_itemdb_item_id_to_name_file.is_open()) {
ShowError("Failed to open itemdb_converter_id_to_name.json\n");
return "UNKNOWN";
}
// Parse the JSON file
json json_itemdb_id_to_name;
try {
bot_itemdb_item_id_to_name_file >> json_itemdb_id_to_name;
}
catch (json::parse_error& e) {
ShowError("Error parsing itemdb_converter_id_to_name.json: %s\n", e.what());
return "UNKNOWN";
}
// Close file after done with it
bot_itemdb_item_id_to_name_file.close();
//ShowStatus("Mapping item_id to item_name...\n");
std::string id_str = std::to_string(item_id);
//ShowStatus("id=%s\n", id_str.c_str());
if (!json_itemdb_id_to_name.contains(id_str)) {
// Error: item_id is not inside json file
ShowWarning("Item_id \"%d\" was not found inside the itemdb. Skipped...", item_id);
return "UNKNOWN";
}
const std::string item_name = json_itemdb_id_to_name[id_str];
//ShowStatus("Entry found. Item Name: %s\n", item_name.c_str());
return item_name;
}
std::string convert_item_id_to_aegisname(int item_id)
{
// Open the mapping JSON file
std::ifstream bot_itemdb_item_id_to_aegisname_file("conf/bot_helper_jsons/itemdb_converter_id_to_aegisname.json");
if (!bot_itemdb_item_id_to_aegisname_file.is_open()) {
ShowError("Failed to open itemdb_converter_id_to_aegisname.json\n");
return "UNKNOWN";
}
// Parse the JSON file
json json_itemdb_id_to_aegisname;
try {
bot_itemdb_item_id_to_aegisname_file >> json_itemdb_id_to_aegisname;
}
catch (json::parse_error& e) {
ShowError("Error parsing itemdb_converter_id_to_name.json: %s\n", e.what());
return "UNKNOWN";
}
// Close file after done with it
bot_itemdb_item_id_to_aegisname_file.close();
//ShowStatus("Mapping item_id to item aegisname...\n");
std::string id_str = std::to_string(item_id);
//ShowStatus("id=%s\n", id_str.c_str());
if (!json_itemdb_id_to_aegisname.contains(id_str)) {
// Error: item_id is not inside json file
ShowWarning("Item_id \"%d\" was not found inside the itemdb. Skipped...", item_id);
return "UNKNOWN";
}
std::string item_name = json_itemdb_id_to_aegisname[id_str];
//ShowStatus("Entry found. Item Name: %s\n", item_name.c_str());
return item_name;
}
/*
* Tool Functions for item name parsing
*/
void prepare_item_bundle(bot_item_bundle& bundle, uint32 char_id, const nlohmann::json& character) {
//Select for the used profile
std::string item_profile = character["char_items_profile"];
if (item_profile == "CUSTOM") {
if (!character.contains("items")) {
throw std::runtime_error("items data is missing for CUSTOM items profile.");
}
const auto& items = character["items"];
//Initialize the items bundle
bundle.items.clear();
// Iterate over the items in the JSON
for (const auto& item : items.items()) {
std::string item_name = item.key();
int item_amount = item.value();
// Convert item name to item ID
int item_id = convert_item_aegisname_to_id(item_name.c_str());
if (item_id <= 0) { // Invalid item ID
ShowWarning("Item ID (Aegisname) %s is Invalid. Skipping...\n", item_name.c_str());
continue; // Skip invalid item
}
// Retrieve ingame name of the item
std::string ingame_name = convert_item_id_to_name(item_id);
if (ingame_name == "UNKNOWN") { // invalid ingame name
ShowWarning("Item id %d is Invalid. Skipping...\n", item_id);
continue; // Skip invalid item id
}
// Add the item to the item bundle
bot_inv_item new_item{};
set_bot_inv_item_defaults(new_item);
new_item.item_id = item_id; // the id number of the item
new_item.amount = item_amount;
new_item.char_id = char_id;
bundle.items.push_back(new_item);
}
}
else {
// Load item loadout Profile
std::string job_str = character["start_job"];
int job_id = convert_jobid_to_number(job_str.c_str());
std::string job_profile_file_path = "conf/bot_profiles/item_profiles/" + job_str + "_item_profile.json";
nlohmann::json json_item_profiles;
// Try to Open iten Profile
std::ifstream item_profile_file(job_profile_file_path);
if (!item_profile_file.is_open()) {
ShowWarning("Failed to open item_profile_file %s. Trying to load DEFAULT instead.\n", job_profile_file_path.c_str());
job_profile_file_path = "conf/bot_profiles/item_profiles/DEFAULT_item_profile.json";
item_profile_file.open(job_profile_file_path);
if (!item_profile_file.is_open()) {
throw std::runtime_error("Failed to open both job and default profiles.");
}
}
// Parse Profile Data
try {
item_profile_file >> json_item_profiles;
}
catch (const nlohmann::json::parse_error& e) {
throw std::runtime_error("Error parsing build profile JSON: " + std::string(e.what()));
}
if (!json_item_profiles.contains(item_profile)) {
// If build profile is not found, default to INIT
item_profile = "INIT";
}
const auto& profile = json_item_profiles[item_profile];
//Initialize the item bundle
bundle.items.clear();
// Iterate over the item in the JSON
for (const auto& item : profile.items()) {
std::string item_name = item.key();
int item_amount = item.value();
int value = item.value();
// Convert item aegisname to item ID
int item_id = convert_item_aegisname_to_id(item_name.c_str());
if (item_id <= 0) { // Invalid item ID
ShowWarning("Item Aegisname %s is Invalid. Skipping...\n", item_name.c_str());
continue; // Skip invalid items
}
// Retrieve ingame name of the item
std::string ingame_name = convert_item_id_to_name(item_id);
if (ingame_name == "UNKNOWN") { // invalid ingame name
ShowWarning("Item id %d is Invalid. Skipping...\n", item_id);
continue; // Skip invalid item id
}
// Add the item to the item bundle
bot_inv_item new_item{};
set_bot_inv_item_defaults(new_item);
new_item.item_id = item_id; // the id number of the item
new_item.amount = item_amount;
new_item.char_id = char_id;
bundle.items.push_back(new_item);
}
}
}

@ -0,0 +1,199 @@
// Copyright (c) 4lexKidd
// This software is licensed under the GPL v3 as to be fitting with the rathena project with the additional requirement
// that any distributed versions must include an attribution to the original author (4lexKidd) in all copies
// or substantial portions of the software.
#ifndef HELPER_INVENTORY_HPP
#define HELPER_INVENTORY_HPP
#include "json.hpp"
#include <common/cbasetypes.hpp>
#include <common/database.hpp>
#include <config/core.hpp>
#include <unordered_map>
#include <vector>
#include <memory>
#include <string>
//#include <common/mmo.hpp>
typedef struct InventoryDB InventoryDB;
typedef struct InventoryDBIterator InventoryDBIterator;
// standard engines
InventoryDB* inventory_db_sql(void);
struct bot_inv_item {
//base information
uint32 inventory_id; // ID of inventory entry
uint32 char_id; // ID of corresponding char account
uint32 item_id; // nameID of item
uint32 amount; // amount of item added
uint32 equip; // binary code for where item is equipped to
// EQP_HEAD_LOW = 0x000001, // 1
// EQP_HEAD_MID = 0x000200, // 512
// EQP_HEAD_TOP = 0x000100, // 256
// EQP_HAND_R = 0x000002, // 2
// EQP_HAND_L = 0x000020, // 32
// EQP_ARMOR = 0x000010, // 16
// EQP_SHOES = 0x000040, // 64
// EQP_GARMENT = 0x000004, // 4
// EQP_ACC_R = 0x000008, // 8
// EQP_ACC_L = 0x000080, // 128
// EQP_COSTUME_HEAD_TOP = 0x000400, // 1024
// EQP_COSTUME_HEAD_MID = 0x000800, // 2048
// EQP_COSTUME_HEAD_LOW = 0x001000, // 4096
// EQP_COSTUME_GARMENT = 0x002000, // 8192
// //EQP_COSTUME_FLOOR = 0x004000, // 16384
// EQP_AMMO = 0x008000, // 32768
// EQP_SHADOW_ARMOR = 0x010000, // 65536
// EQP_SHADOW_WEAPON = 0x020000, // 131072
// EQP_SHADOW_SHIELD = 0x040000, // 262144
// EQP_SHADOW_SHOES = 0x080000, // 524288
// EQP_SHADOW_ACC_R = 0x100000, // 1048576
// EQP_SHADOW_ACC_L = 0x200000, // 2097152
int identify; // state of identification | 1 = identified
uint8 refine; // state of refinement
uint8 attribute; // elemental attribute
// card fields represent inserted cards or other modifiers to an item (like very strong and link to creator,etc)
uint32 card0; // cardid of inserted card || special values for different modifiers possible
uint32 card1;
uint32 card2;
uint32 card3;
//option fields - use ingame currently unknown
int16 option_id0;
int16 option_val0;
int8 option_parm0;
int16 option_id1;
int16 option_val1;
int8 option_parm1;
int16 option_id2;
int16 option_val2;
int8 option_parm2;
int16 option_id3;
int16 option_val3;
int8 option_parm3;
int16 option_id4;
int16 option_val4;
int8 option_parm4;
//mics fields - if unknown leave at 0
uint32 expire_time;
uint8 favorite;
uint8 bound;
uint64 unique_id;
uint32 equip_switch;
uint8 enchantgrade;
};
struct InventoryDBIterator {
/// Destroys this iterator, releasing all allocated memory (including itself).
///
/// @param self Iterator
void (*destroy)(InventoryDBIterator* self);
/// Fetches the next item in the database.
/// Fills bot_inv_item with the item data.
/// @param self Iterator
/// @param item InvItem data
/// @return true if successful
bool (*next)(InventoryDBIterator* self, const uint32 char_id, struct bot_inv_item* item);
};
struct InventoryDB {
/// Initializes this database, making it ready for use.
/// Call this after setting the properties.
///
/// @param self Database
/// @return true if successful
bool (*init)(InventoryDB* self);
/// Destroys this database, releasing all allocated memory (including itself).
///
/// @param self Database
void (*destroy)(InventoryDB* self);
/// Gets a property from this database.
/// These read-only properties must be implemented:
///
/// @param self Database
/// @param key Property name
/// @param buf Buffer for the value
/// @param buflen Buffer length
/// @return true if successful
bool (*get_property)(InventoryDB* self, const char* key, char* buf, size_t buflen);
/// Sets a property in this database.
///
/// @param self Database
/// @param key Property name
/// @param value Property value
/// @return true if successful
bool (*set_property)(InventoryDB* self, const char* key, const char* value);
/// Creates a new item in this database.
/// bot_inv_item->char_id must always be set, because there will never be a valid bot_inv_item without char-table account
///
/// @param self Database
/// @param item bot_inv_item data
/// @return true if successful
bool (*create)(InventoryDB* self, struct bot_inv_item* item);
/// Removes an inventory entry from this database.
///
/// @param self Database
/// @param char_id character id
/// @param item_id id of iteml
/// @return true if successful
bool (*remove)(InventoryDB* self, const uint32 char_id, const uint32 inventory_id);
/// Modifies the data of an existing inventory entry.
/// Uses bot_inv_item->char_id and bot_inv_item->item_id to identify the entry.
///
/// @param self Database
/// @param bot_inv_item data
/// @return true if successful
bool (*save)(InventoryDB* self, const struct bot_inv_item* item);
/// Finds an omvemtory entry with char_id and item_id and copies it to bot_inv_item.
///
/// @param self Database
/// @param bot_inv_item Pointer that receives the item data
/// @return true if successful
bool (*load_num)(InventoryDB* self, struct bot_inv_item* item);
/// Returns a new forward iterator.
///
/// @param self Database
/// @return Iterator
InventoryDBIterator* (*iterator)(InventoryDB* self, const uint32 char_id);
};
void set_bot_inv_item_defaults(bot_inv_item& item);
int convert_item_aegisname_to_id(const char* aegisname);
std::string convert_item_id_to_name(int item_id);
std::string convert_item_id_to_aegisname(int item_id);
struct bot_item {
uint32 item_id;
std::string aegisname;
std::string name;
uint32 amount;
};
struct bot_item_bundle {
std::vector<bot_inv_item> items;
};
void prepare_item_bundle(bot_item_bundle& bundle, uint32 char_id, const nlohmann::json& character);
#endif /* HELPER_INVENTORY_HPP */
Loading…
Cancel
Save