Compare commits

..

No commits in common. 'b23c9c45f0bceef4d142c9caa0b08f2e5e098fcc' and 'dc7267744b9787fba8aef93689e1999164606d49' have entirely different histories.

@ -521,8 +521,6 @@ if (WIN32)
"${CMAKE_CURRENT_SOURCE_DIR}/tools/charserv.bat"
"${CMAKE_CURRENT_SOURCE_DIR}/tools/logserv.bat"
"${CMAKE_CURRENT_SOURCE_DIR}/tools/mapserv.bat"
"${CMAKE_CURRENT_SOURCE_DIR}/tools/brokkserver.bat"
"${CMAKE_CURRENT_SOURCE_DIR}/tools/sindriserver.bat"
"${CMAKE_CURRENT_SOURCE_DIR}/tools/runserver.bat"
"${CMAKE_CURRENT_SOURCE_DIR}/tools/serv.bat"
)

@ -12,7 +12,7 @@ passwd: p1
// NOTE: Do not use spaces or any of these characters which are not allowed in
// Windows filenames \/:*?"<>|
// ... or else guild emblems won't work client-side!
server_name: SanderRO
server_name: rAthena
// Wisp name for server: used to send wisp from server to players (between 4 to 23 characters)
wisp_server_name: Server
@ -214,7 +214,7 @@ db_path: db
// NOTE: Requires client 2011-03-09aragexeRE or newer.
// A window is opened before you can select your character and you will have to enter a pincode by using only your mouse.
// Default: yes
pincode_enabled: no
pincode_enabled: yes
// How often does a user have to change his pincode?
// 0: never (default)

@ -76,24 +76,6 @@ log_db_db: ragnarok
log_codepage:
log_login_db: loginlog
// MySQL Brokk server
brokk_server_ip: 127.0.0.1
brokk_server_port: 3306
brokk_server_id: ragnarok
brokk_server_pw: ragnarok
brokk_server_db: ragnarok
brokk_codepage:
brokk_case_sensitive: no
// MySQL Sindri server
sindri_server_ip: 127.0.0.1
sindri_server_port: 3306
sindri_server_id: ragnarok
sindri_server_pw: ragnarok
sindri_server_db: ragnarok
sindri_codepage:
sindri_case_sensitive: no
// MySQL Reconnect Settings
// - mysql_reconnect_type:
// 1: When MySQL disconnects during runtime, the server tries to reconnect

@ -53,7 +53,7 @@ console: off
// Can you use _M/_F to make new accounts on the server?
// Note: This only works if client side password encryption is not enabled.
new_account: yes
new_account: no
// If new_account is enabled, changes the minimum length for the account name.
// By default is set to '4' or '6' (depending on the new login UI).

@ -1,11 +0,0 @@
--
-- Table structure for table `bot_accounts`
-- Creator id = 0 -> Created by Brokk via Auto-Generate from Configs
--
CREATE TABLE IF NOT EXISTS `bot_accounts` (
`account_id` int(11) unsigned NOT NULL,
`is_bot` tinyint(2) NOT NULL default '1',
`creator_id` int(11) unsigned NOT NULL default '0',
`creator_name` varchar(23) NOT NULL default 'Brokk',
PRIMARY KEY (`account_id`)
) ENGINE=MyISAM;

@ -1126,7 +1126,3 @@ CREATE TABLE IF NOT EXISTS `vendings` (
`autotrade` tinyint(4) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
-- Add bot_accounts table
SOURCE bot_accounts.sql;

@ -113,7 +113,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
<PrecompiledHeader />
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\rapidyaml\src;$(SolutionDir)3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)3rdparty\libconfig\;$(SolutionDir)src\login;$(SolutionDir)src\map;$(SolutionDir)src\char;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)src;$(SolutionDir)3rdparty\rapidyaml\src;$(SolutionDir)3rdparty\rapidyaml\ext\c4core\src;$(SolutionDir)3rdparty\libconfig\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
@ -139,21 +139,10 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="account.cpp" />
<ClCompile Include="bot_account.cpp" />
<ClCompile Include="brokk.cpp" />
<ClCompile Include="charakters.cpp" />
<ClCompile Include="char_creation_helpers.cpp" />
<ClCompile Include="helper_skills.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\login\account.hpp" />
<ClInclude Include="bot_account.hpp" />
<ClInclude Include="brokk.hpp" />
<ClInclude Include="charakters.hpp" />
<ClInclude Include="char_creation_helpers.hpp" />
<ClInclude Include="helper_skills.hpp" />
<ClInclude Include="json.hpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

@ -18,43 +18,10 @@
<ClCompile Include="brokk.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="account.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="bot_account.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="charakters.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="char_creation_helpers.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="helper_skills.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="brokk.hpp">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="..\..\login\account.hpp">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="json.hpp">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="bot_account.hpp">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="charakters.hpp">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="char_creation_helpers.hpp">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="helper_skills.hpp">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup>
</Project>

@ -1,954 +0,0 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
#include "account.hpp"
#include <algorithm> //min / max
#include <cstdlib>
#include <cstring>
#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 "brokk.hpp" // brokk_config
/// global defines
/// internal structure
typedef struct AccountDB_SQL {
AccountDB vtable; // public interface
Sql* accounts; // SQL handle accounts storage
std::string db_hostname = "127.0.0.1";
uint16 db_port = 3306;
std::string db_username = "ragnarok";
std::string db_password = "";
std::string db_database = "ragnarok";
std::string codepage = "";
// other settings
bool case_sensitive;
//table name
char account_db[32];
char global_acc_reg_num_table[32];
char global_acc_reg_str_table[32];
} AccountDB_SQL;
/// internal structure
typedef struct AccountDBIterator_SQL {
AccountDBIterator vtable; // public interface
AccountDB_SQL* db;
int last_account_id;
} AccountDBIterator_SQL;
/// internal functions
static bool account_db_sql_init(AccountDB* self);
static void account_db_sql_destroy(AccountDB* self);
static bool account_db_sql_get_property(AccountDB* self, const char* key, char* buf, size_t buflen);
static bool account_db_sql_set_property(AccountDB* self, const char* option, const char* value);
static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc);
static bool account_db_sql_remove(AccountDB* self, const uint32 account_id);
static bool account_db_sql_enable_webtoken( AccountDB* self, const uint32 account_id );
static bool account_db_sql_disable_webtoken( AccountDB* self, const uint32 account_id );
static bool account_db_sql_remove_webtokens( AccountDB* self );
static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc, bool refresh_token);
static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const uint32 account_id);
static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid);
static AccountDBIterator* account_db_sql_iterator(AccountDB* self);
static void account_db_sql_iter_destroy(AccountDBIterator* self);
static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc);
static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, uint32 account_id);
static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new, bool refresh_token);
/// public constructor
AccountDB* account_db_sql(void) {
AccountDB_SQL* db = (AccountDB_SQL*)aCalloc(1, sizeof(AccountDB_SQL));
new(db) AccountDB_SQL();
// set up the vtable
db->vtable.init = &account_db_sql_init;
db->vtable.destroy = &account_db_sql_destroy;
db->vtable.get_property = &account_db_sql_get_property;
db->vtable.set_property = &account_db_sql_set_property;
db->vtable.save = &account_db_sql_save;
db->vtable.create = &account_db_sql_create;
db->vtable.remove = &account_db_sql_remove;
db->vtable.enable_webtoken = &account_db_sql_enable_webtoken;
db->vtable.disable_webtoken = &account_db_sql_disable_webtoken;
db->vtable.remove_webtokens = &account_db_sql_remove_webtokens;
db->vtable.load_num = &account_db_sql_load_num;
db->vtable.load_str = &account_db_sql_load_str;
db->vtable.iterator = &account_db_sql_iterator;
// initialize to default values
db->accounts = nullptr;
// other settings
db->case_sensitive = false;
safestrncpy(db->account_db, "login", sizeof(db->account_db));
safestrncpy(db->global_acc_reg_num_table, "global_acc_reg_num", sizeof(db->global_acc_reg_num_table));
safestrncpy(db->global_acc_reg_str_table, "global_acc_reg_str", sizeof(db->global_acc_reg_str_table));
return &db->vtable;
}
/* ------------------------------------------------------------------------- */
/**
* Establish the database connection.
* @param self: pointer to db
*/
static bool account_db_sql_init(AccountDB* self) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
Sql* sql_handle;
db->accounts = Sql_Malloc();
sql_handle = db->accounts;
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->accounts);
db->accounts = nullptr;
return false;
}
if( !db->codepage.empty() && SQL_ERROR == Sql_SetEncoding(sql_handle, db->codepage.c_str()) )
Sql_ShowDebug(sql_handle);
self->remove_webtokens( self );
return true;
}
/**
* Destroy the database and close the connection to it.
* @param self: pointer to db
*/
static void account_db_sql_destroy(AccountDB* self){
AccountDB_SQL* db = (AccountDB_SQL*)self;
if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token` = NULL", db->account_db ) ){
Sql_ShowDebug( db->accounts );
}
Sql_Free(db->accounts);
db->accounts = nullptr;
db->~AccountDB_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 account_db_sql_get_property(AccountDB* self, const char* key, char* buf, size_t buflen)
{
AccountDB_SQL* db = (AccountDB_SQL*)self;
const char* signature;
signature = "login_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, "account_db") == 0 )
safesnprintf(buf, buflen, "%s", db->account_db);
else
if( strcmpi(key, "global_acc_reg_str_table") == 0 )
safesnprintf(buf, buflen, "%s", db->global_acc_reg_str_table);
else
if( strcmpi(key, "global_acc_reg_num_table") == 0 )
safesnprintf(buf, buflen, "%s", db->global_acc_reg_num_table);
else
return false;// not found
return true;
}
signature = "login_";
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 account_db_sql_set_property(AccountDB* self, const char* key, const char* value) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
const char* signature;
signature = "login_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, "account_db") == 0 )
safestrncpy(db->account_db, value, sizeof(db->account_db));
else
if( strcmpi(key, "global_acc_reg_str_table") == 0 )
safestrncpy(db->global_acc_reg_str_table, value, sizeof(db->global_acc_reg_str_table));
else
if( strcmpi(key, "global_acc_reg_num_table") == 0 )
safestrncpy(db->global_acc_reg_num_table, value, sizeof(db->global_acc_reg_num_table));
else
return false;// not found
return true;
}
signature = "login_";
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 account entry.
* If acc->account_id is -1, the account id will be auto-generated,
* and its value will be written to acc->account_id if everything succeeds.
* @param self: pointer to db
* @param acc: pointer of mmo_account to save
* @return true if successful, false if something has failed
*/
static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
Sql* sql_handle = db->accounts;
// decide on the account id to assign
uint32 account_id;
if( acc->account_id != -1 )
{// caller specifies it manually
account_id = acc->account_id;
}
else
{// ask the database
char* data;
size_t len;
if( SQL_SUCCESS != Sql_Query(sql_handle, "SELECT MAX(`account_id`)+1 FROM `%s`", db->account_db) )
{
Sql_ShowDebug(sql_handle);
return false;
}
if( SQL_SUCCESS != Sql_NextRow(sql_handle) )
{
Sql_ShowDebug(sql_handle);
Sql_FreeResult(sql_handle);
return false;
}
Sql_GetData(sql_handle, 0, &data, &len);
account_id = ( data != nullptr ) ? atoi(data) : 0;
Sql_FreeResult(sql_handle);
account_id = max((uint32_t) START_ACCOUNT_NUM, account_id);
}
// zero value is prohibited
if( account_id == 0 )
return false;
// absolute maximum
if( account_id > END_ACCOUNT_NUM )
return false;
// insert the data into the database
acc->account_id = account_id;
return mmo_auth_tosql(db, acc, true, false);
}
/**
* Delete an existing account entry and its regs.
* @param self: pointer to db
* @param account_id: id of user account
* @return true if successful, false if something has failed
*/
static bool account_db_sql_remove(AccountDB* self, const uint32 account_id) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
Sql* sql_handle = db->accounts;
bool result = false;
if( SQL_SUCCESS != Sql_QueryStr(sql_handle, "START TRANSACTION")
|| SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->account_db, account_id)
|| SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->global_acc_reg_num_table, account_id)
|| SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->global_acc_reg_str_table, account_id) )
Sql_ShowDebug(sql_handle);
else
result = true;
result &= ( SQL_SUCCESS == Sql_QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") );
return result;
}
/**
* Update an existing account with the new data provided (both account and regs).
* @param self: pointer to db
* @param acc: pointer of mmo_account to save
* @return true if successful, false if something has failed
*/
static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc, bool refresh_token) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
return mmo_auth_tosql(db, acc, false, refresh_token);
}
/**
* Retrieve data from db and store it in the provided data structure.
* Filled data structure is done by delegation to mmo_auth_fromsql.
* @param self: pointer to db
* @param acc: pointer of mmo_account to fill
* @param account_id: id of user account
* @return true if successful, false if something has failed
*/
static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const uint32 account_id) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
return mmo_auth_fromsql(db, acc, account_id);
}
/**
* Retrieve data from db and store it in the provided data structure.
* Doesn't actually retrieve data yet: escapes and checks userid, then transforms it to accid for fetching.
* Filled data structure is done by delegation to account_db_sql_load_num.
* @param self: pointer to db
* @param acc: pointer of mmo_account to fill
* @param userid: name of user account
* @return true if successful, false if something has failed
*/
static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
Sql* sql_handle = db->accounts;
char esc_userid[2*NAME_LENGTH+1];
uint32 account_id;
char* data;
Sql_EscapeString(sql_handle, esc_userid, userid);
// get the list of account IDs for this user ID
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `userid`= %s '%s'",
db->account_db, (db->case_sensitive ? "BINARY" : ""), esc_userid) )
{
Sql_ShowDebug(sql_handle);
return false;
}
if( Sql_NumRows(sql_handle) > 1 )
{// serious problem - duplicit account
ShowError("account_db_sql_load_str: multiple accounts found when retrieving data for account '%s'!\n", userid);
Sql_FreeResult(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);
account_id = atoi(data);
return account_db_sql_load_num(self, acc, account_id);
}
/**
* Create a new forward iterator.
* @param self: pointer to db iterator
* @return a new db iterator
*/
static AccountDBIterator* account_db_sql_iterator(AccountDB* self) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)aCalloc(1, sizeof(AccountDBIterator_SQL));
// set up the vtable
iter->vtable.destroy = &account_db_sql_iter_destroy;
iter->vtable.next = &account_db_sql_iter_next;
// fill data
iter->db = db;
iter->last_account_id = -1;
return &iter->vtable;
}
/**
* Destroys this iterator, releasing all allocated memory (including itself).
* @param self: pointer to db iterator
*/
static void account_db_sql_iter_destroy(AccountDBIterator* self) {
AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self;
aFree(iter);
}
/**
* Fetches the next account in the database.
* @param self: pointer to db iterator
* @param acc: pointer of mmo_account to fill
* @return true if next account found and filled, false if something has failed
*/
static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc) {
AccountDBIterator_SQL* iter = (AccountDBIterator_SQL*)self;
AccountDB_SQL* db = iter->db;
Sql* sql_handle = db->accounts;
char* data;
// get next account ID
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `account_id` > '%d' ORDER BY `account_id` ASC LIMIT 1",
db->account_db, iter->last_account_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 account data
uint32 account_id;
account_id = atoi(data);
if( mmo_auth_fromsql(db, acc, account_id) )
{
iter->last_account_id = account_id;
Sql_FreeResult(sql_handle);
return true;
}
}
Sql_FreeResult(sql_handle);
return false;
}
/**
* Fetch a struct mmo_account from sql, excluding web_auth_token.
* @param db: pointer to db
* @param acc: pointer of mmo_account to fill
* @param account_id: id of user account to take data from
* @return true if successful, false if something has failed
*/
static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, uint32 account_id) {
Sql* sql_handle = db->accounts;
char* data;
// retrieve login entry for the specified account
if( SQL_ERROR == Sql_Query(sql_handle,
#ifdef VIP_ENABLE
"SELECT `account_id`,`userid`,`user_pass`,`sex`,`email`,`group_id`,`state`,`unban_time`,`expiration_time`,`logincount`,`lastlogin`,`last_ip`,`birthdate`,`character_slots`,`pincode`, `pincode_change`, `vip_time`, `old_group` FROM `%s` WHERE `account_id` = %d",
#else
"SELECT `account_id`,`userid`,`user_pass`,`sex`,`email`,`group_id`,`state`,`unban_time`,`expiration_time`,`logincount`,`lastlogin`,`last_ip`,`birthdate`,`character_slots`,`pincode`, `pincode_change` FROM `%s` WHERE `account_id` = %d",
#endif
db->account_db, account_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); acc->account_id = atoi(data);
Sql_GetData(sql_handle, 1, &data, nullptr); safestrncpy(acc->userid, data, sizeof(acc->userid));
Sql_GetData(sql_handle, 2, &data, nullptr); safestrncpy(acc->pass, data, sizeof(acc->pass));
Sql_GetData(sql_handle, 3, &data, nullptr); acc->sex = data[0];
Sql_GetData(sql_handle, 4, &data, nullptr); safestrncpy(acc->email, data, sizeof(acc->email));
Sql_GetData(sql_handle, 5, &data, nullptr); acc->group_id = (unsigned int) atoi(data);
Sql_GetData(sql_handle, 6, &data, nullptr); acc->state = (unsigned int) strtoul(data, nullptr, 10);
Sql_GetData(sql_handle, 7, &data, nullptr); acc->unban_time = atol(data);
Sql_GetData(sql_handle, 8, &data, nullptr); acc->expiration_time = atol(data);
Sql_GetData(sql_handle, 9, &data, nullptr); acc->logincount = (unsigned int) strtoul(data, nullptr, 10);
Sql_GetData(sql_handle, 10, &data, nullptr); safestrncpy(acc->lastlogin, data==nullptr?"":data, sizeof(acc->lastlogin));
Sql_GetData(sql_handle, 11, &data, nullptr); safestrncpy(acc->last_ip, data, sizeof(acc->last_ip));
Sql_GetData(sql_handle, 12, &data, nullptr); safestrncpy(acc->birthdate, data==nullptr?"":data, sizeof(acc->birthdate));
Sql_GetData(sql_handle, 13, &data, nullptr); acc->char_slots = (uint8) atoi(data);
Sql_GetData(sql_handle, 14, &data, nullptr); safestrncpy(acc->pincode, data, sizeof(acc->pincode));
Sql_GetData(sql_handle, 15, &data, nullptr); acc->pincode_change = atol(data);
#ifdef VIP_ENABLE
Sql_GetData(sql_handle, 16, &data, nullptr); acc->vip_time = atol(data);
Sql_GetData(sql_handle, 17, &data, nullptr); acc->old_group = atoi(data);
#endif
Sql_FreeResult(sql_handle);
acc->web_auth_token[0] = '\0';
if( acc->char_slots > MAX_CHARS ){
ShowError( "Account %s (AID=%u) exceeds MAX_CHARS. Capping...\n", acc->userid, acc->account_id );
acc->char_slots = MAX_CHARS;
}
return true;
}
/**
* Save a struct mmo_account in sql.
* @param db: pointer to db
* @param acc: pointer of mmo_account to save
* @param is_new: if it's a new entry or should we update
* @return true if successful, false if something has failed
*/
static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new, bool refresh_token) {
Sql* sql_handle = db->accounts;
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` (`account_id`, `userid`, `user_pass`, `sex`, `email`, `group_id`, `state`, `unban_time`, `expiration_time`, `logincount`, `lastlogin`, `last_ip`, `birthdate`, `character_slots`, `pincode`, `pincode_change`, `vip_time`, `old_group` ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
#else
"INSERT INTO `%s` (`account_id`, `userid`, `user_pass`, `sex`, `email`, `group_id`, `state`, `unban_time`, `expiration_time`, `logincount`, `lastlogin`, `last_ip`, `birthdate`, `character_slots`, `pincode`, `pincode_change`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
#endif
db->account_db)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, (void*)&acc->account_id, sizeof(acc->account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, (void*)acc->pass, strlen(acc->pass))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_ENUM, (void*)&acc->sex, sizeof(acc->sex))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 4, SQLDT_STRING, (void*)&acc->email, strlen(acc->email))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 5, SQLDT_INT, (void*)&acc->group_id, sizeof(acc->group_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 6, SQLDT_UINT, (void*)&acc->state, sizeof(acc->state))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 7, SQLDT_LONG, (void*)&acc->unban_time, sizeof(acc->unban_time))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 8, SQLDT_INT, (void*)&acc->expiration_time, sizeof(acc->expiration_time))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 9, SQLDT_UINT, (void*)&acc->logincount, sizeof(acc->logincount))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 10, acc->lastlogin[0]?SQLDT_STRING:SQLDT_NULL, (void*)&acc->lastlogin, strlen(acc->lastlogin))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_STRING, (void*)&acc->last_ip, strlen(acc->last_ip))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, acc->birthdate[0]?SQLDT_STRING:SQLDT_NULL, (void*)&acc->birthdate, strlen(acc->birthdate))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_UCHAR, (void*)&acc->char_slots, sizeof(acc->char_slots))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_STRING, (void*)&acc->pincode, strlen(acc->pincode))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 15, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change))
#ifdef VIP_ENABLE
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 16, SQLDT_LONG, (void*)&acc->vip_time, sizeof(acc->vip_time))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 17, SQLDT_INT, (void*)&acc->old_group, sizeof(acc->old_group))
#endif
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
break;
}
}
else
{// update account table
if( SQL_SUCCESS != SqlStmt_Prepare(stmt,
#ifdef VIP_ENABLE
"UPDATE `%s` SET `userid`=?,`user_pass`=?,`sex`=?,`email`=?,`group_id`=?,`state`=?,`unban_time`=?,`expiration_time`=?,`logincount`=?,`lastlogin`=?,`last_ip`=?,`birthdate`=?,`character_slots`=?,`pincode`=?, `pincode_change`=?, `vip_time`=?, `old_group`=? WHERE `account_id` = '%d'",
#else
"UPDATE `%s` SET `userid`=?,`user_pass`=?,`sex`=?,`email`=?,`group_id`=?,`state`=?,`unban_time`=?,`expiration_time`=?,`logincount`=?,`lastlogin`=?,`last_ip`=?,`birthdate`=?,`character_slots`=?,`pincode`=?, `pincode_change`=? WHERE `account_id` = '%d'",
#endif
db->account_db, acc->account_id)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->pass, strlen(acc->pass))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_ENUM, (void*)&acc->sex, sizeof(acc->sex))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_STRING, (void*)acc->email, strlen(acc->email))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 4, SQLDT_INT, (void*)&acc->group_id, sizeof(acc->group_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 5, SQLDT_UINT, (void*)&acc->state, sizeof(acc->state))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 6, SQLDT_LONG, (void*)&acc->unban_time, sizeof(acc->unban_time))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 7, SQLDT_LONG, (void*)&acc->expiration_time, sizeof(acc->expiration_time))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 8, SQLDT_UINT, (void*)&acc->logincount, sizeof(acc->logincount))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 9, acc->lastlogin[0]?SQLDT_STRING:SQLDT_NULL, (void*)&acc->lastlogin, strlen(acc->lastlogin))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 10, SQLDT_STRING, (void*)&acc->last_ip, strlen(acc->last_ip))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, acc->birthdate[0]?SQLDT_STRING:SQLDT_NULL, (void*)&acc->birthdate, strlen(acc->birthdate))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, SQLDT_UCHAR, (void*)&acc->char_slots, sizeof(acc->char_slots))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_STRING, (void*)&acc->pincode, strlen(acc->pincode))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change))
#ifdef VIP_ENABLE
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 15, SQLDT_LONG, (void*)&acc->vip_time, sizeof(acc->vip_time))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 16, SQLDT_INT, (void*)&acc->old_group, sizeof(acc->old_group))
#endif
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
break;
}
}
if( acc->sex != 'S' && brokk_config.use_web_auth_token && refresh_token ){
static bool initialized = false;
static const char* query;
// Pseudo Scope to break out
while( !initialized ){
if( SQL_SUCCESS == Sql_Query( sql_handle, "SELECT SHA2( 'test', 256 )" ) ){
query = "UPDATE `%s` SET `web_auth_token` = LEFT( SHA2( CONCAT( UUID(), RAND() ), 256 ), %d ), `web_auth_token_enabled` = '1' WHERE `account_id` = '%d'";
initialized = true;
break;
}
if( SQL_SUCCESS == Sql_Query( sql_handle, "SELECT MD5( 'test' )" ) ){
query = "UPDATE `%s` SET `web_auth_token` = LEFT( MD5( CONCAT( UUID(), RAND() ) ), %d ), `web_auth_token_enabled` = '1' WHERE `account_id` = '%d'";
initialized = true;
break;
}
ShowWarning( "Your MySQL does not support SHA2 and MD5 - no hashing will be used for login token creation.\n" );
ShowWarning( "If you are using an old version of MySQL consider upgrading to a newer release.\n" );
query = "UPDATE `%s` SET `web_auth_token` = LEFT( CONCAT( UUID(), RAND() ), %d ), `web_auth_token_enabled` = '1' WHERE `account_id` = '%d'";
initialized = true;
break;
}
const int MAX_RETRIES = 20;
int i = 0;
bool success = false;
// Retry it for a maximum number of retries
do{
if( SQL_SUCCESS == Sql_Query( sql_handle, query, db->account_db, WEB_AUTH_TOKEN_LENGTH - 1, acc->account_id ) ){
success = true;
break;
}
}while( i < MAX_RETRIES && Sql_GetError( sql_handle ) == 1062 );
if( !success ){
if( i == MAX_RETRIES ){
ShowError( "Failed to generate a unique web_auth_token with %d retries...\n", i );
}else{
Sql_ShowDebug( sql_handle );
}
break;
}
char* data;
size_t len;
if( SQL_SUCCESS != Sql_Query( sql_handle, "SELECT `web_auth_token` from `%s` WHERE `account_id` = '%d'", db->account_db, acc->account_id ) ||
SQL_SUCCESS != Sql_NextRow( sql_handle ) ||
SQL_SUCCESS != Sql_GetData( sql_handle, 0, &data, &len )
){
Sql_ShowDebug( sql_handle );
break;
}
safestrncpy( (char *)&acc->web_auth_token, data, sizeof( acc->web_auth_token ) );
Sql_FreeResult( sql_handle );
}
// 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;
}
void mmo_save_global_accreg(AccountDB* self, int fd, uint32 account_id, uint32 char_id) {
Sql* sql_handle = ((AccountDB_SQL*)self)->accounts;
AccountDB_SQL* db = (AccountDB_SQL*)self;
uint16 count = RFIFOW(fd, 12);
if (count) {
int cursor = 14, i;
char key[32], sval[254], esc_key[32*2+1], esc_sval[254*2+1];
for (i = 0; i < count; i++) {
uint32 index;
safestrncpy(key, RFIFOCP(fd, cursor + 1), RFIFOB(fd, cursor));
Sql_EscapeString(sql_handle, esc_key, key);
cursor += RFIFOB(fd, cursor) + 1;
index = RFIFOL(fd, cursor);
cursor += 4;
switch (RFIFOB(fd, cursor++)) {
// int
case 0:
if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%" PRIu32 "','%s','%" PRIu32 "','%" PRId64 "')", db->global_acc_reg_num_table, account_id, esc_key, index, RFIFOQ(fd, cursor)) )
Sql_ShowDebug(sql_handle);
cursor += 8;
break;
case 1:
if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%" PRIu32 "' AND `key` = '%s' AND `index` = '%" PRIu32 "' LIMIT 1", db->global_acc_reg_num_table, account_id, esc_key, index) )
Sql_ShowDebug(sql_handle);
break;
// str
case 2:
safestrncpy(sval, RFIFOCP(fd, cursor + 1), RFIFOB(fd, cursor));
cursor += RFIFOB(fd, cursor) + 1;
Sql_EscapeString(sql_handle, esc_sval, sval);
if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%" PRIu32 "','%s','%" PRIu32 "','%s')", db->global_acc_reg_str_table, account_id, esc_key, index, esc_sval) )
Sql_ShowDebug(sql_handle);
break;
case 3:
if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%" PRIu32 "' AND `key` = '%s' AND `index` = '%" PRIu32 "' LIMIT 1", db->global_acc_reg_str_table, account_id, esc_key, index) )
Sql_ShowDebug(sql_handle);
break;
default:
ShowError("mmo_save_global_accreg: unknown type %d\n",RFIFOB(fd, cursor - 1));
return;
}
}
}
}
void mmo_send_global_accreg(AccountDB* self, int fd, uint32 account_id, uint32 char_id) {
Sql* sql_handle = ((AccountDB_SQL*)self)->accounts;
AccountDB_SQL* db = (AccountDB_SQL*)self;
char* data;
int16 plen = 0;
size_t len;
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%" PRIu32 "'", db->global_acc_reg_str_table, account_id) )
Sql_ShowDebug(sql_handle);
WFIFOHEAD(fd, 60000 + 300);
WFIFOW(fd, 0) = 0x2726;
// 0x2 = length, set prior to being sent
WFIFOL(fd, 4) = account_id;
WFIFOL(fd, 8) = char_id;
WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
WFIFOB(fd, 13) = 1; // is string type
WFIFOW(fd, 14) = 0; // count
plen = 16;
/**
* Vessel!
*
* str type
* { keyLength(B), key(<keyLength>), index(L), valLength(B), val(<valLength>) }
**/
while ( SQL_SUCCESS == Sql_NextRow(sql_handle) ) {
Sql_GetData(sql_handle, 0, &data, nullptr);
len = strlen(data)+1;
WFIFOB(fd, plen) = (unsigned char)len; // won't be higher; the column size is 32
plen += 1;
safestrncpy(WFIFOCP(fd,plen), data, len);
plen += static_cast<decltype(plen)>( len );
Sql_GetData(sql_handle, 1, &data, nullptr);
WFIFOL(fd, plen) = (uint32)atol(data);
plen += 4;
Sql_GetData(sql_handle, 2, &data, nullptr);
len = strlen(data)+1;
WFIFOB(fd, plen) = (unsigned char)len; // won't be higher; the column size is 254
plen += 1;
safestrncpy(WFIFOCP(fd,plen), data, len);
plen += static_cast<decltype(plen)>( len );
WFIFOW(fd, 14) += 1;
if( plen > 60000 ) {
WFIFOW(fd, 2) = plen;
WFIFOSET(fd, plen);
// prepare follow up
WFIFOHEAD(fd, 60000 + 300);
WFIFOW(fd, 0) = 0x2726;
// 0x2 = length, set prior to being sent
WFIFOL(fd, 4) = account_id;
WFIFOL(fd, 8) = char_id;
WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
WFIFOB(fd, 13) = 1; // is string type
WFIFOW(fd, 14) = 0; // count
plen = 16;
}
}
WFIFOW(fd, 2) = plen;
WFIFOSET(fd, plen);
Sql_FreeResult(sql_handle);
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%" PRIu32 "'", db->global_acc_reg_num_table, account_id) )
Sql_ShowDebug(sql_handle);
WFIFOHEAD(fd, 60000 + 300);
WFIFOW(fd, 0) = 0x2726;
// 0x2 = length, set prior to being sent
WFIFOL(fd, 4) = account_id;
WFIFOL(fd, 8) = char_id;
WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
WFIFOB(fd, 13) = 0; // is int type
WFIFOW(fd, 14) = 0; // count
plen = 16;
/**
* Vessel!
*
* int type
* { keyLength(B), key(<keyLength>), index(L), value(L) }
**/
while ( SQL_SUCCESS == Sql_NextRow(sql_handle) ) {
Sql_GetData(sql_handle, 0, &data, nullptr);
len = strlen(data)+1;
WFIFOB(fd, plen) = (unsigned char)len; // won't be higher; the column size is 32
plen += 1;
safestrncpy(WFIFOCP(fd,plen), data, len);
plen += static_cast<decltype(plen)>( len );
Sql_GetData(sql_handle, 1, &data, nullptr);
WFIFOL(fd, plen) = (uint32)atol(data);
plen += 4;
Sql_GetData(sql_handle, 2, &data, nullptr);
WFIFOQ(fd, plen) = strtoll(data,nullptr,10);
plen += 8;
WFIFOW(fd, 14) += 1;
if( plen > 60000 ) {
WFIFOW(fd, 2) = plen;
WFIFOSET(fd, plen);
// prepare follow up
WFIFOHEAD(fd, 60000 + 300);
WFIFOW(fd, 0) = 0x2726;
// 0x2 = length, set prior to being sent
WFIFOL(fd, 4) = account_id;
WFIFOL(fd, 8) = char_id;
WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
WFIFOB(fd, 13) = 0; // is int type
WFIFOW(fd, 14) = 0; // count
plen = 16;
}
}
WFIFOB(fd, 12) = 1;
WFIFOW(fd, 2) = plen;
WFIFOSET(fd, plen);
Sql_FreeResult(sql_handle);
}
bool account_db_sql_enable_webtoken( AccountDB* self, const uint32 account_id ){
AccountDB_SQL* db = (AccountDB_SQL*)self;
if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token_enabled` = '1' WHERE `account_id` = '%u'", db->account_db, account_id ) ){
Sql_ShowDebug( db->accounts );
return false;
}
return true;
}
/**
* Timered function to disable webtoken for user
* If the user is online, then they must have logged since we started the timer.
* In that case, do nothing. The new authtoken must be valid.
* @param tid: timer id
* @param tick: tick of execution
* @param id: user account id
* @param data: AccountDB // because we don't use singleton???
* @return :0
TIMER_FUNC(account_disable_webtoken_timer){
const struct online_login_data* p = login_get_online_user(id);
AccountDB_SQL* db = reinterpret_cast<AccountDB_SQL*>(data);
if (p == nullptr) {
ShowInfo("Web Auth Token for account %d was disabled\n", id);
if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token_enabled` = '0' WHERE `account_id` = '%u'", db->account_db, id ) ){
Sql_ShowDebug( db->accounts );
return 0;
}
}
return 0;
}
*/
bool account_db_sql_disable_webtoken( AccountDB* self, const uint32 account_id ){
AccountDB_SQL* db = (AccountDB_SQL*)self;
//add_timer(gettick() + brokk_config.disable_webtoken_delay, account_disable_webtoken_timer, account_id, reinterpret_cast<intptr_t>(db));
return true;
}
bool account_db_sql_remove_webtokens( AccountDB* self ){
AccountDB_SQL* db = (AccountDB_SQL*)self;
if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token` = NULL, `web_auth_token_enabled` = '0'", db->account_db ) ){
Sql_ShowDebug( db->accounts );
return false;
}
return true;
}

@ -1,149 +0,0 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
#ifndef ACCOUNT_HPP
#define ACCOUNT_HPP
#include <common/cbasetypes.hpp>
#include <common/mmo.hpp> // ACCOUNT_REG2_NUM, WEB_AUTH_TOKEN_LENGTH
#include <config/core.hpp>
typedef struct AccountDB AccountDB;
typedef struct AccountDBIterator AccountDBIterator;
// standard engines
AccountDB* account_db_sql(void);
struct mmo_account {
uint32 account_id;
char userid[NAME_LENGTH];
char pass[32+1]; // 23+1 for plaintext, 32+1 for md5-ed passwords
char sex; // gender (M/F/S)
char email[40]; // e-mail (by default: a@a.com)
unsigned int group_id; // player group id
uint8 char_slots; // this accounts maximum character slots (maximum is limited to MAX_CHARS define in char server)
unsigned int state; // packet 0x006a value + 1 (0: compte OK)
time_t unban_time; // (timestamp): ban time limit of the account (0 = no ban)
time_t expiration_time; // (timestamp): validity limit of the account (0 = unlimited)
unsigned int logincount;// number of successful auth attempts
char lastlogin[24]; // date+time of last successful login
char last_ip[16]; // save of last IP of connection
char birthdate[10+1]; // assigned birth date (format: YYYY-MM-DD)
char pincode[PINCODE_LENGTH+1]; // pincode system
time_t pincode_change; // (timestamp): last time of pincode change
char web_auth_token[WEB_AUTH_TOKEN_LENGTH]; // web authentication token (randomized on each login)
#ifdef VIP_ENABLE
int old_group;
time_t vip_time;
#endif
};
struct AccountDBIterator {
/// Destroys this iterator, releasing all allocated memory (including itself).
///
/// @param self Iterator
void (*destroy)(AccountDBIterator* self);
/// Fetches the next account in the database.
/// Fills acc with the account data.
/// @param self Iterator
/// @param acc Account data
/// @return true if successful
bool (*next)(AccountDBIterator* self, struct mmo_account* acc);
};
struct AccountDB {
/// Initializes this database, making it ready for use.
/// Call this after setting the properties.
///
/// @param self Database
/// @return true if successful
bool (*init)(AccountDB* self);
/// Destroys this database, releasing all allocated memory (including itself).
///
/// @param self Database
void (*destroy)(AccountDB* 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)(AccountDB* 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)(AccountDB* self, const char* key, const char* value);
/// Creates a new account in this database.
/// If acc->account_id is not -1, the provided value will be used.
/// Otherwise the account_id will be auto-generated and written to acc->account_id.
///
/// @param self Database
/// @param acc Account data
/// @return true if successful
bool (*create)(AccountDB* self, struct mmo_account* acc);
/// Removes an account from this database.
///
/// @param self Database
/// @param account_id Account id
/// @return true if successful
bool (*remove)(AccountDB* self, const uint32 account_id);
/// Enables the web auth token for the given account id
bool (*enable_webtoken)(AccountDB* self, const uint32 account_id);
/// Disables the web auth token for the given account id
bool (*disable_webtoken)(AccountDB* self, const uint32 account_id);
/// Removes the web auth token for all accounts
bool (*remove_webtokens)(AccountDB* self);
/// Modifies the data of an existing account.
/// Uses acc->account_id to identify the account.
///
/// @param self Database
/// @param acc Account data
/// @param refresh_token Whether or not to refresh the web auth token
/// @return true if successful
bool (*save)(AccountDB* self, const struct mmo_account* acc, bool refresh_token);
/// Finds an account with account_id and copies it to acc.
///
/// @param self Database
/// @param acc Pointer that receives the account data
/// @param account_id Target account id
/// @return true if successful
bool (*load_num)(AccountDB* self, struct mmo_account* acc, const uint32 account_id);
/// Finds an account with userid and copies it to acc.
///
/// @param self Database
/// @param acc Pointer that receives the account data
/// @param userid Target username
/// @return true if successful
bool (*load_str)(AccountDB* self, struct mmo_account* acc, const char* userid);
/// Returns a new forward iterator.
///
/// @param self Database
/// @return Iterator
AccountDBIterator* (*iterator)(AccountDB* self);
};
void mmo_send_global_accreg(AccountDB* self, int fd, uint32 account_id, uint32 char_id);
void mmo_save_global_accreg(AccountDB* self, int fd, uint32 account_id, uint32 char_id);
#endif /* ACCOUNT_HPP */

File diff suppressed because it is too large Load Diff

@ -1,787 +0,0 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
#ifndef BATTLE_HPP
#define BATTLE_HPP
#include <bitset>
#include <common/cbasetypes.hpp>
#include <common/mmo.hpp>
#include <config/core.hpp>
#include "map.hpp" //ELE_MAX
#include "skill.hpp"
//fwd declaration
class map_session_data;
struct mob_data;
struct block_list;
enum e_damage_type : uint8;
/// State of a single attack attempt; used in flee/def penalty calculations when mobbed
enum damage_lv : uint8 {
ATK_NONE, /// Not an attack
ATK_LUCKY, /// Attack was lucky-dodged
ATK_FLEE, /// Attack was dodged
ATK_MISS, /// Attack missed because of element/race modifier.
ATK_BLOCK, /// Attack was blocked by some skills.
ATK_DEF /// Attack connected
};
/// Flag for base damage calculation
enum e_base_damage_flag : uint16 {
BDMG_NONE = 0x0000, /// None
BDMG_CRIT = 0x0001, /// Critical hit damage
BDMG_ARROW = 0x0002, /// Add arrow attack and use ranged damage formula
BDMG_MAGIC = 0x0004, /// Use MATK for base damage (e.g. Magic Crasher)
BDMG_NOSIZE = 0x0008, /// Skip target size adjustment (e.g. Weapon Perfection)
};
/// Flag of the final calculation
enum e_battle_flag : uint16 {
BF_NONE = 0x0000, /// None
BF_WEAPON = 0x0001, /// Weapon attack
BF_MAGIC = 0x0002, /// Magic attack
BF_MISC = 0x0004, /// Misc attack
BF_SHORT = 0x0010, /// Short attack
BF_LONG = 0x0040, /// Long attack
BF_SKILL = 0x0100, /// Skill attack
BF_NORMAL = 0x0200, /// Normal attack
BF_WEAPONMASK = BF_WEAPON|BF_MAGIC|BF_MISC, /// Weapon attack mask
BF_RANGEMASK = BF_SHORT|BF_LONG, /// Range attack mask
BF_SKILLMASK = BF_SKILL|BF_NORMAL, /// Skill attack mask
};
/// Battle check target [Skotlex]
enum e_battle_check_target : uint32 {
BCT_NOONE = 0x000000, ///< No one
BCT_SELF = 0x010000, ///< Self
BCT_ENEMY = 0x020000, ///< Enemy
BCT_PARTY = 0x040000, ///< Party members
BCT_GUILDALLY = 0x080000, ///< Only allies, NOT guildmates
BCT_NEUTRAL = 0x100000, ///< Neutral target
BCT_SAMEGUILD = 0x200000, ///< Guildmates, No Guild Allies
BCT_ALL = 0x3F0000, ///< All targets
BCT_WOS = 0x400000, ///< Except self (currently used for skipping if src == bl in skill_area_sub)
BCT_GUILD = BCT_SAMEGUILD|BCT_GUILDALLY, ///< Guild AND Allies (BCT_SAMEGUILD|BCT_GUILDALLY)
BCT_NOGUILD = BCT_ALL&~BCT_GUILD, ///< Except guildmates
BCT_NOPARTY = BCT_ALL&~BCT_PARTY, ///< Except party members
BCT_NOENEMY = BCT_ALL&~BCT_ENEMY, ///< Except enemy
BCT_ALLY = BCT_PARTY|BCT_GUILD,
BCT_FRIEND = BCT_NOENEMY,
};
/// Check flag for common damage bonuses such as: ATKpercent, Refine, Passive Mastery, Spirit Spheres and Star Crumbs
enum e_bonus_chk_flag : uint8 {
BCHK_ALL, /// Check if all of the common damage bonuses apply to this skill
BCHK_REFINE, /// Check if refine bonus is applied (pre-renewal only currently)
BCHK_STAR, /// Check if Star Crumb bonus is applied (pre-renewal only currently)
};
/// Damage structure
struct Damage {
#ifdef RENEWAL
int64 statusAtk, statusAtk2, weaponAtk, weaponAtk2, equipAtk, equipAtk2, masteryAtk, masteryAtk2, percentAtk, percentAtk2;
#else
int64 basedamage; /// Right hand damage that a normal attack would deal
#endif
int64 damage, /// Right hand damage
damage2; /// Left hand damage
enum e_damage_type type; /// Check clif_damage for type
short div_; /// Number of hit
int amotion,
dmotion;
int blewcount; /// Number of knockback
int flag; /// chk e_battle_flag
int miscflag;
enum damage_lv dmg_lv; /// ATK_LUCKY,ATK_FLEE,ATK_DEF
bool isspdamage; /// Display blue damage numbers in clif_damage
};
// Damage Calculation
struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct block_list *target,uint16 skill_id,uint16 skill_lv,int flag);
int64 battle_calc_return_damage(struct block_list *bl, struct block_list *src, int64 *, int flag, uint16 skill_id, bool status_reflect);
void battle_drain(map_session_data *sd, struct block_list *tbl, int64 rdamage, int64 ldamage, int race, int class_);
int64 battle_attr_fix(struct block_list* src, struct block_list* target, int64 damage, int atk_elem, int def_type, int def_lv, int flag = 0);
int battle_calc_cardfix(int attack_type, struct block_list *src, struct block_list *target, std::bitset<NK_MAX> nk, int s_ele, int s_ele_, int64 damage, int left, int flag);
// Final calculation Damage
int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damage *d,int64 damage,uint16 skill_id,uint16 skill_lv);
int64 battle_calc_gvg_damage(struct block_list *src,struct block_list *bl,int64 damage,uint16 skill_id,int flag);
int64 battle_calc_bg_damage(struct block_list *src,struct block_list *bl,int64 damage,uint16 skill_id,int flag);
int64 battle_calc_pk_damage(block_list &src, block_list &bl, int64 damage, uint16 skill_id, int flag);
int battle_damage(struct block_list *src, struct block_list *target, int64 damage, t_tick delay, uint16 skill_lv, uint16 skill_id, enum damage_lv dmg_lv, unsigned short attack_type, bool additional_effects, t_tick tick, bool spdamage);
int battle_delay_damage (t_tick tick, int amotion, struct block_list *src, struct block_list *target, int attack_type, uint16 skill_id, uint16 skill_lv, int64 damage, enum damage_lv dmg_lv, t_tick ddelay, bool additional_effects, bool spdamage);
int battle_fix_damage(struct block_list* src, struct block_list* target, int64 damage, t_tick walkdelay, uint16 skill_id);
int battle_calc_chorusbonus(map_session_data *sd);
// Summary normal attack treatment (basic attack)
enum damage_lv battle_weapon_attack( struct block_list *bl,struct block_list *target,t_tick tick,int flag);
// Accessors
struct block_list* battle_get_master(struct block_list *src);
struct block_list* battle_gettargeted(struct block_list *target);
struct block_list* battle_getenemy(struct block_list *target, int type, int range);
int battle_gettarget(struct block_list *bl);
uint16 battle_getcurrentskill(struct block_list *bl);
int battle_check_undead(int race,int element);
int battle_check_target(struct block_list *src, struct block_list *target,int flag);
bool battle_check_range(struct block_list *src,struct block_list *bl,int range);
bool battle_check_coma(map_session_data& sd, struct block_list& target, e_battle_flag attack_type);
void battle_consume_ammo(map_session_data* sd, int skill, int lv);
bool is_infinite_defense(struct block_list *target, int flag);
// Settings
#define MIN_HAIR_STYLE battle_config.min_hair_style
#define MAX_HAIR_STYLE battle_config.max_hair_style
#define MIN_HAIR_COLOR battle_config.min_hair_color
#define MAX_HAIR_COLOR battle_config.max_hair_color
#define MIN_CLOTH_COLOR battle_config.min_cloth_color
#define MAX_CLOTH_COLOR battle_config.max_cloth_color
#define MIN_BODY_STYLE battle_config.min_body_style
#define MAX_BODY_STYLE battle_config.max_body_style
struct Battle_Config
{
int warp_point_debug;
int enable_critical;
int mob_critical_rate;
int critical_rate;
int enable_baseatk, enable_baseatk_renewal;
int enable_perfect_flee;
int cast_rate, delay_rate;
int delay_dependon_dex, delay_dependon_agi;
int sdelay_attack_enable;
int left_cardfix_to_right;
int cardfix_monster_physical;
int skill_add_range;
int skill_out_range_consume;
int skill_amotion_leniency;
int skillrange_by_distance; //[Skotlex]
int use_weapon_skill_range; //[Skotlex]
int pc_damage_delay_rate;
int defnotenemy;
int vs_traps_bctall;
int traps_setting;
int summon_flora; //[Skotlex]
int clear_unit_ondeath; //[Skotlex]
int clear_unit_onwarp; //[Skotlex]
int random_monster_checklv;
int attr_recover;
int item_auto_get;
int flooritem_lifetime;
int item_first_get_time;
int item_second_get_time;
int item_third_get_time;
int mvp_item_first_get_time;
int mvp_item_second_get_time;
int mvp_item_third_get_time;
int base_exp_rate,job_exp_rate;
int drop_rate0item;
int death_penalty_type;
int death_penalty_base,death_penalty_job;
int pvp_exp; // [MouseJstr]
int gtb_sc_immunity;
int zeny_penalty;
int restart_hp_rate;
int restart_sp_rate;
int mvp_exp_rate;
int mvp_hp_rate;
int monster_hp_rate;
int monster_max_aspd;
int view_range_rate;
int chase_range_rate;
int atc_spawn_quantity_limit;
int atc_slave_clone_limit;
int partial_name_scan;
int skillfree;
int skillup_limit;
int wp_rate;
int pp_rate;
int monster_active_enable;
int monster_damage_delay_rate;
int monster_loot_type;
int mob_skill_rate; //[Skotlex]
int mob_skill_delay; //[Skotlex]
int mob_count_rate;
int no_spawn_on_player; //[Skotlex]
int force_random_spawn; //[Skotlex]
int mob_spawn_delay, plant_spawn_delay, boss_spawn_delay; // [Skotlex]
int slaves_inherit_mode;
int slaves_inherit_speed;
int summons_trigger_autospells;
int pc_walk_delay_rate; //Adjusts can't walk delay after being hit for players. [Skotlex]
int walk_delay_rate; //Adjusts can't walk delay after being hit. [Skotlex]
int multihit_delay; //Adjusts can't walk delay per hit on multi-hitting skills. [Skotlex]
int quest_skill_learn;
int quest_skill_reset;
int basic_skill_check;
int guild_emperium_check;
int guild_exp_limit;
int guild_max_castles;
int guild_skill_relog_delay;
int guild_skill_relog_type;
int emergency_call;
int guild_aura;
int pc_invincible_time;
int pet_catch_rate;
int pet_rename;
int pet_friendly_rate;
int pet_hungry_delay_rate;
int pet_hungry_friendly_decrease;
int pet_status_support;
int pet_attack_support;
int pet_damage_support;
int pet_support_min_friendly; //[Skotlex]
int pet_support_rate;
int pet_attack_exp_to_master;
int pet_attack_exp_rate;
int pet_lv_rate; //[Skotlex]
int pet_max_stats; //[Skotlex]
int pet_max_atk1; //[Skotlex]
int pet_max_atk2; //[Skotlex]
int pet_no_gvg; //Disables pets in gvg. [Skotlex]
int pet_equip_required;
int pet_unequip_destroy;
int pet_master_dead;
int skill_min_damage;
int finger_offensive_type;
int heal_exp;
int max_heal_lv;
int max_heal; //Mitternacht
int resurrection_exp;
int shop_exp;
int combo_delay_rate;
int item_check;
int item_use_interval; //[Skotlex]
int cashfood_use_interval;
int wedding_modifydisplay;
int wedding_ignorepalette; //[Skotlex]
int xmas_ignorepalette; // [Valaris]
int summer_ignorepalette; // [Zephyrus]
int hanbok_ignorepalette;
int oktoberfest_ignorepalette;
int natural_healhp_interval;
int natural_healsp_interval;
int natural_heal_skill_interval;
int natural_heal_weight_rate;
int natural_heal_weight_rate_renewal;
int arrow_decrement;
int ammo_unequip;
int ammo_check_weapon;
int max_aspd;
int max_walk_speed; //Maximum walking speed after buffs [Skotlex]
int max_hp_lv99;
int max_hp_lv150;
int max_hp;
int max_sp;
int max_lv, aura_lv;
int max_parameter, max_baby_parameter;
int max_cart_weight;
int skill_log;
int battle_log;
int etc_log;
int save_clothcolor;
int undead_detect_type;
int auto_counter_type;
int min_hitrate; //[Skotlex]
int max_hitrate; //[Skotlex]
int agi_penalty_target;
int agi_penalty_type;
int agi_penalty_count;
int agi_penalty_num;
int vit_penalty_target;
int vit_penalty_type;
int vit_penalty_count;
int vit_penalty_num;
int weapon_defense_type;
int magic_defense_type;
int skill_reiteration;
int skill_nofootset;
int pc_cloak_check_type;
int monster_cloak_check_type;
int estimation_type;
int gvg_short_damage_rate;
int gvg_long_damage_rate;
int gvg_weapon_damage_rate;
int gvg_magic_damage_rate;
int gvg_misc_damage_rate;
int gvg_flee_penalty;
int pk_short_damage_rate;
int pk_long_damage_rate;
int pk_weapon_damage_rate;
int pk_magic_damage_rate;
int pk_misc_damage_rate;
int mob_changetarget_byskill;
int attack_direction_change;
int land_skill_limit;
int monster_class_change_recover;
int produce_item_name_input;
int display_skill_fail;
int chat_warpportal;
int mob_warp;
int dead_branch_active;
int vending_max_value;
int vending_over_max;
int vending_tax;
int vending_tax_min;
int show_steal_in_same_party;
int party_share_type;
int party_hp_mode;
int party_show_share_picker;
int show_picker_item_type;
int attack_attr_none;
int item_rate_mvp, item_rate_common, item_rate_common_boss, item_rate_card, item_rate_card_boss,
item_rate_equip, item_rate_equip_boss, item_rate_heal, item_rate_heal_boss, item_rate_use,
item_rate_use_boss, item_rate_treasure, item_rate_adddrop;
int item_rate_common_mvp, item_rate_heal_mvp, item_rate_use_mvp, item_rate_equip_mvp, item_rate_card_mvp;
int logarithmic_drops;
int item_drop_common_min,item_drop_common_max; // Added by TyrNemesis^
int item_drop_card_min,item_drop_card_max;
int item_drop_equip_min,item_drop_equip_max;
int item_drop_mvp_min,item_drop_mvp_max; // End Addition
int item_drop_mvp_mode; //rAthena addition [Playtester]
int item_drop_heal_min,item_drop_heal_max; // Added by Valatris
int item_drop_use_min,item_drop_use_max; //End
int item_drop_treasure_min,item_drop_treasure_max; //by [Skotlex]
int item_drop_adddrop_min,item_drop_adddrop_max; //[Skotlex]
int prevent_logout; // Added by RoVeRT
int prevent_logout_trigger;
int land_protector_behavior;
int npc_emotion_behavior;
int alchemist_summon_reward; // [Valaris]
int drops_by_luk;
int drops_by_luk2;
int equip_natural_break_rate; //Base Natural break rate for attacks.
int equip_self_break_rate; //Natural & Penalty skills break rate
int equip_skill_break_rate; //Offensive skills break rate
int multi_level_up;
int multi_level_up_base;
int multi_level_up_job;
int max_exp_gain_rate; //Max amount of exp bar % you can get in one go.
int pk_mode;
int pk_mode_mes;
int pk_level_range;
int manner_system; // end additions [Valaris]
int show_mob_info;
int gx_allhit;
int gx_disptype;
int devotion_level_difference;
int player_skill_partner_check;
int invite_request_check;
int skill_removetrap_type;
int disp_experience;
int disp_zeny;
int backstab_bow_penalty;
int hp_rate;
int sp_rate;
int bone_drop;
int buyer_name;
int dancing_weaponswitch_fix;
// eAthena additions
int night_at_start; // added by [Yor]
int day_duration; // added by [Yor]
int night_duration; // added by [Yor]
int ban_hack_trade; // added by [Yor]
int min_hair_style; // added by [MouseJstr]
int max_hair_style; // added by [MouseJstr]
int min_hair_color; // added by [MouseJstr]
int max_hair_color; // added by [MouseJstr]
int min_cloth_color; // added by [MouseJstr]
int max_cloth_color; // added by [MouseJstr]
int pet_hair_style; // added by [Skotlex]
int castrate_dex_scale; // added by [MouseJstr]
int area_size; // added by [MouseJstr]
int max_def, over_def_bonus; //added by [Skotlex]
int zeny_from_mobs; // [Valaris]
int mobs_level_up; // [Valaris]
int mobs_level_up_exp_rate; // [Valaris]
int pk_min_level; // [celest]
int skill_steal_max_tries; //max steal skill tries on a mob. if 0, then w/o limit [Lupus]
int skill_steal_random_options;
int motd_type; // [celest]
int finding_ore_rate; // orn
int exp_calc_type;
int exp_bonus_attacker;
int exp_bonus_max_attacker;
int min_skill_delay_limit;
int default_walk_delay;
int no_skill_delay;
int attack_walk_delay;
int require_glory_guild;
int idle_no_share;
int party_update_interval;
int party_even_share_bonus;
int delay_battle_damage;
int hide_woe_damage;
int display_version;
int display_hallucination; // [Skotlex]
int use_statpoint_table; // [Skotlex]
int debuff_on_logout; // Removes a few "official" negative Scs on logout. [Skotlex]
int mob_ai; //Configures various mob_ai settings to make them smarter or dumber(official). [Skotlex]
int hom_setting; //Configures various homunc settings which make them behave unlike normal characters.. [Skotlex]
int dynamic_mobs; // Dynamic Mobs [Wizputer] - battle_athena flag implemented by [random]
int mob_remove_damaged; // Dynamic Mobs - Remove mobs even if damaged [Wizputer]
int mob_remove_delay; // Dynamic Mobs - delay before removing mobs from a map [Skotlex]
int mob_active_time; //Duration through which mobs execute their Hard AI after players leave their area of sight.
int boss_active_time;
int show_hp_sp_drain, show_hp_sp_gain; //[Skotlex]
int mob_npc_event_type; //Determines on who the npc_event is executed. [Skotlex]
int character_size; // if riders have size=2, and baby class riders size=1 [Lupus]
int mob_max_skilllvl; // Max possible skill level [Lupus]
int rare_drop_announce; // chance <= to show rare drops global announces
int drop_rate_cap; // Drop rate can't be raised above this amount by drop bonus items
int drop_rate_cap_vip;
int retaliate_to_master; //Whether when a mob is attacked by another mob, it will retaliate versus the mob or the mob's master. [Skotlex]
int duel_allow_pvp; // [LuzZza]
int duel_allow_gvg; // [LuzZza]
int duel_allow_teleport; // [LuzZza]
int duel_autoleave_when_die; // [LuzZza]
int duel_time_interval; // [LuzZza]
int duel_only_on_same_map; // [Toms]
int skip_teleport_lv1_menu; // possibility to disable (skip) Teleport Lv1 menu, that have only two lines `Random` and `Cancel` [LuzZza]
int allow_skill_without_day; // [Komurka]
int allow_es_magic_pc; // [Skotlex]
int skill_wall_check; // [Skotlex]
int official_cell_stack_limit; // [Playtester]
int custom_cell_stack_limit; // [Skotlex]
int skill_caster_check; // [Skotlex]
int sc_castcancel; // [Skotlex]
int pc_sc_def_rate; // [Skotlex]
int mob_sc_def_rate;
int pc_max_sc_def;
int mob_max_sc_def;
int sg_angel_skill_ratio;
int sg_miracle_skill_ratio;
int sg_miracle_skill_duration;
int autospell_stacking; //Enables autospell cards to stack. [Skotlex]
int override_mob_names; //Enables overriding spawn mob names with the mob_db names. [Skotlex]
int min_chat_delay; //Minimum time between client messages. [Skotlex]
int friend_auto_add; //When accepting friends, both get friended. [Skotlex]
int hvan_explosion_intimate; // fix [albator]
int hom_rename;
int homunculus_show_growth ; //[orn]
int homunculus_friendly_rate;
int quest_exp_rate;
int autotrade_mapflag;
int at_timeout;
int homunculus_autoloot;
int idle_no_autoloot;
int max_guild_alliance;
int ksprotection;
int auction_feeperhour;
int auction_maximumprice;
int homunculus_auto_vapor; //Keep Homunculus from Vaporizing when master dies. [L0ne_W0lf]
int display_status_timers; //Show or hide skill buff/delay timers in recent clients [Sara]
int skill_add_heal_rate; //skills that bHealPower has effect on [Inkfish]
int eq_single_target_reflectable;
int invincible_nodamage;
int mob_slave_keep_target;
int autospell_check_range; //Enable range check for autospell bonus. [L0ne_W0lf]
int knockback_left;
int client_reshuffle_dice; // Reshuffle /dice
int client_sort_storage;
int feature_buying_store;
int feature_search_stores;
int searchstore_querydelay;
int searchstore_maxresults;
int display_party_name;
int cashshop_show_points;
int mail_show_status;
int client_limit_unit_lv;
int hom_max_level;
int hom_S_max_level;
int hom_S_growth_level;
// [BattleGround Settings]
int bg_update_interval;
int bg_short_damage_rate;
int bg_long_damage_rate;
int bg_weapon_damage_rate;
int bg_magic_damage_rate;
int bg_misc_damage_rate;
int bg_flee_penalty;
// rAthena
int max_third_parameter;
int max_baby_third_parameter;
int max_trans_parameter;
int max_third_trans_parameter;
int max_extended_parameter;
int max_summoner_parameter;
int max_fourth_parameter;
int max_third_aspd;
int max_summoner_aspd;
int vcast_stat_scale;
int mvp_tomb_enabled;
int mvp_tomb_delay;
int atcommand_suggestions_enabled;
int min_npc_vendchat_distance;
int atcommand_mobinfo_type;
int mob_size_influence; // Enable modifications on earned experience, drop rates and monster status depending on monster size. [mkbu95]
int skill_trap_type;
int allow_consume_restricted_item;
int allow_equip_restricted_item;
int max_walk_path;
int item_enabled_npc;
int item_onfloor; // Whether to drop an undroppable item on the map or destroy it if inventory is full.
int bowling_bash_area;
int drop_rateincrease;
int feature_auction;
int feature_banking;
int vip_storage_increase;
int vip_base_exp_increase;
int vip_job_exp_increase;
int vip_zeny_penalty;
int vip_bm_increase;
int vip_drop_increase;
int vip_gemstone;
int vip_exp_penalty_base;
int vip_exp_penalty_job;
int vip_disp_rate;
int mon_trans_disable_in_gvg;
int discount_item_point_shop;
int update_enemy_position;
int devotion_rdamage;
int feature_itemlink;
int feature_mesitemlink;
int feature_mesitemlink_brackets;
int feature_mesitemlink_dbname;
// autotrade persistency
int feature_autotrade;
int feature_autotrade_direction;
int feature_autotrade_head_direction;
int feature_autotrade_sit;
int feature_autotrade_open_delay;
// Fame points
int fame_taekwon_mission;
int fame_refine_lv1;
int fame_refine_lv2;
int fame_refine_lv3;
int fame_forge;
int fame_pharmacy_3;
int fame_pharmacy_5;
int fame_pharmacy_7;
int fame_pharmacy_10;
int disp_servervip_msg;
int warg_can_falcon;
int path_blown_halt;
int rental_mount_speed_boost;
int warp_suggestions_enabled;
int taekwon_mission_mobname;
int teleport_on_portal;
int cart_revo_knockback;
int guild_notice_changemap;
int transcendent_status_points;
int taekwon_ranker_min_lv;
int revive_onwarp;
int mail_delay;
int autotrade_monsterignore;
int idletime_option;
int spawn_direction;
int arrow_shower_knockback;
int devotion_rdamage_skill_only;
int max_extended_aspd;
int mob_chase_refresh; //How often a monster should refresh its chase [Playtester]
int mob_icewall_walk_block; //How a normal monster should be trapped in icewall [Playtester]
int boss_icewall_walk_block; //How a boss monster should be trapped in icewall [Playtester]
int snap_dodge; // Enable or disable dodging damage snapping away [csnv]
int stormgust_knockback;
int default_fixed_castrate;
int default_bind_on_equip;
int pet_ignore_infinite_def; // Makes fixed damage of petskillattack2 ignores infinite defense
int homunculus_evo_intimacy_need;
int homunculus_evo_intimacy_reset;
int monster_loot_search_type;
int feature_roulette;
int feature_roulette_bonus_reward;
int monster_hp_bars_info;
int min_body_style;
int max_body_style;
int save_body_style;
int mob_eye_range_bonus; //Vulture's Eye and Snake's Eye range bonus
int mob_stuck_warning; //Show warning if a monster is stuck too long
int skill_eightpath_algorithm; //Official path algorithm
int skill_eightpath_same_cell;
int death_penalty_maxlv;
int exp_cost_redemptio;
int exp_cost_redemptio_limit;
int mvp_exp_reward_message;
int can_damage_skill; //Which BL types can damage traps
int atcommand_levelup_events;
int atcommand_disable_npc;
int block_account_in_same_party;
int tarotcard_equal_chance; //Official or equal chance for each card
int change_party_leader_samemap;
int dispel_song; //Can songs be dispelled?
int guild_maprespawn_clones; // Should clones be killed by maprespawnguildid?
int hide_fav_sell;
int mail_daily_count;
int mail_zeny_fee;
int mail_attachment_price;
int mail_attachment_weight;
int banana_bomb_duration;
int guild_leaderchange_delay;
int guild_leaderchange_woe;
int guild_alliance_onlygm;
int feature_achievement;
int allow_bound_sell;
int autoloot_adjust;
int feature_petevolution;
int feature_pet_autofeed;
int feature_pet_autofeed_rate;
int pet_autofeed_always;
int broadcast_hide_name;
int skill_drop_items_full;
int switch_remove_edp;
int feature_homunculus_autofeed;
int feature_homunculus_autofeed_rate;
int summoner_race;
int summoner_size;
int homunculus_autofeed_always;
int feature_attendance;
int feature_privateairship;
int rental_transaction;
int min_shop_buy;
int min_shop_sell;
int feature_equipswitch;
int pet_walk_speed;
int blacksmith_fame_refine_threshold;
int mob_nopc_idleskill_rate;
int mob_nopc_move_rate;
int boss_nopc_idleskill_rate;
int boss_nopc_move_rate;
int hom_idle_no_share;
int idletime_hom_option;
int devotion_standup_fix;
int feature_bgqueue;
int bgqueue_nowarp_mapflag;
int homunculus_exp_gain;
int rental_item_novalue;
int ping_timer_interval;
int ping_time;
int show_skill_scale;
int achievement_mob_share;
int slave_stick_with_master;
int at_logout_event;
int homunculus_starving_rate;
int homunculus_starving_delay;
int drop_connection_on_quit;
int mob_spawn_variance;
int mercenary_autoloot;
int mer_idle_no_share;
int idletime_mer_option;
int feature_refineui;
int rndopt_drop_pillar;
int pet_legacy_formula;
int pet_distance_check;
int pet_hide_check;
int instance_block_leave;
int instance_block_leaderchange;
int instance_block_invite;
int instance_block_expulsion;
// 4th Jobs Stuff
int trait_points_job_change;
int use_traitpoint_table;
int max_trait_parameter;
int max_res_mres_ignored;
int max_ap;
int ap_rate;
int restart_ap_rate;
int loose_ap_on_death;
int loose_ap_on_map;
int keep_ap_on_logout;
int attack_machine_level_difference;
int feature_barter;
int feature_barter_extended;
int break_mob_equip;
int macro_detection_retry;
int macro_detection_timeout;
int macro_detection_punishment;
int macro_detection_punishment_time;
int feature_dynamicnpc_timeout;
int feature_dynamicnpc_rangex;
int feature_dynamicnpc_rangey;
int feature_dynamicnpc_direction;
int mob_respawn_time;
int mob_unlock_time;
int map_edge_size;
int randomize_center_cell;
int feature_stylist;
int feature_banking_state_enforce;
int instance_allow_reconnect;
int synchronize_damage;
int item_stacking;
int hom_delay_reset_vaporize;
int hom_delay_reset_warp;
#include <custom/battle_config_struct.inc>
};
extern struct Battle_Config battle_config;
void do_init_battle(void);
void do_final_battle(void);
extern int battle_config_read(const char *cfgName);
extern void battle_set_defaults(void);
int battle_set_value(const char* w1, const char* w2);
int battle_get_value(const char* w1);
//
struct block_list* battle_getenemyarea(struct block_list *src, int x, int y, int range, int type, int ignore_id);
/**
* Royal Guard
**/
int battle_damage_area( struct block_list *bl, va_list ap);
#endif /* BATTLE_HPP */

@ -1,825 +0,0 @@
// 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 "bot_account.hpp"
#include <algorithm> //min / max
#include <cstdlib>
#include <cstring>
#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 "brokk.hpp" // brokk_config
/// global defines
/// internal structure
typedef struct BotAccountDB_SQL {
BotAccountDB vtable; // public interface
Sql* bot_accounts; // SQL handle bot accounts 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 bot_account_db[32];
char login_account_db[32];
char global_acc_reg_num_table[32];
char global_acc_reg_str_table[32];
} BotAccountDB_SQL;
/// internal structure
typedef struct BotAccountDBIterator_SQL {
BotAccountDBIterator vtable; // public interface
BotAccountDB_SQL* db;
int last_bot_account_id;
} BotAccountDBIterator_SQL;
/// internal functions
static bool bot_account_db_sql_init(BotAccountDB* self);
static void bot_account_db_sql_destroy(BotAccountDB* self);
static bool bot_account_db_sql_get_property(BotAccountDB* self, const char* key, char* buf, size_t buflen);
static bool bot_account_db_sql_set_property(BotAccountDB* self, const char* option, const char* value);
static bool bot_account_db_sql_create(BotAccountDB* self, struct mmo_bot_account* bot_acc);
static bool bot_account_db_sql_remove(BotAccountDB* self, const uint32 account_id);
static bool bot_account_db_sql_save(BotAccountDB* self, const struct mmo_bot_account* bot_acc, bool refresh_token);
static bool bot_account_db_sql_load_num(BotAccountDB* self, struct mmo_bot_account* bot_acc, const uint32 account_id);
static bool bot_account_db_sql_load_str(BotAccountDB* self, struct mmo_bot_account* bot_acc, const char* creator_name);
static BotAccountDBIterator* bot_account_db_sql_iterator(BotAccountDB* self);
static void bot_account_db_sql_iter_destroy(BotAccountDBIterator* self);
static bool bot_account_db_sql_iter_next(BotAccountDBIterator* self, struct mmo_bot_account* bot_acc);
static bool mmo_bot_auth_fromsql(BotAccountDB_SQL* db, struct mmo_bot_account* bot_acc, uint32 account_id);
static bool mmo_bot_auth_tosql(BotAccountDB_SQL* db, const struct mmo_bot_account* bot_acc, bool is_new, bool refresh_token);
/// public constructor
BotAccountDB* bot_account_db_sql(void) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)aCalloc(1, sizeof(BotAccountDB_SQL));
new(db) BotAccountDB_SQL();
// set up the vtable
db->vtable.init = &bot_account_db_sql_init;
db->vtable.destroy = &bot_account_db_sql_destroy;
db->vtable.get_property = &bot_account_db_sql_get_property;
db->vtable.set_property = &bot_account_db_sql_set_property;
db->vtable.save = &bot_account_db_sql_save;
db->vtable.create = &bot_account_db_sql_create;
db->vtable.remove = &bot_account_db_sql_remove;
db->vtable.load_num = &bot_account_db_sql_load_num;
db->vtable.load_str = &bot_account_db_sql_load_str;
db->vtable.iterator = &bot_account_db_sql_iterator;
// initialize to default values
db->bot_accounts = nullptr;
// other settings
db->case_sensitive = false;
safestrncpy(db->bot_account_db, "bot_accounts", sizeof(db->bot_account_db));
safestrncpy(db->login_account_db, "login", sizeof(db->login_account_db));
//safestrncpy(db->global_acc_reg_num_table, "global_acc_reg_num", sizeof(db->global_acc_reg_num_table));
//safestrncpy(db->global_acc_reg_str_table, "global_acc_reg_str", sizeof(db->global_acc_reg_str_table));
return &db->vtable;
}
/* ------------------------------------------------------------------------- */
/**
* Establish the database connection.
* @param self: pointer to db
*/
static bool bot_account_db_sql_init(BotAccountDB* self) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
Sql* sql_handle;
db->bot_accounts = Sql_Malloc();
sql_handle = db->bot_accounts;
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_accounts);
db->bot_accounts = nullptr;
return false;
}
if (!db->codepage.empty() && SQL_ERROR == Sql_SetEncoding(sql_handle, db->codepage.c_str()))
Sql_ShowDebug(sql_handle);
//self->remove_webtokens( self );
return true;
}
/**
* Destroy the database and close the connection to it.
* @param self: pointer to db
*/
static void bot_account_db_sql_destroy(BotAccountDB* self){
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
//if( SQL_ERROR == Sql_Query( db->bot_accounts, "UPDATE `%s` SET `web_auth_token` = NULL", db->bot_account_db ) ){
// Sql_ShowDebug( db->accounts );
//}
Sql_Free(db->bot_accounts);
db->bot_accounts = nullptr;
db->~BotAccountDB_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_account_db_sql_get_property(BotAccountDB* self, const char* key, char* buf, size_t buflen)
{
BotAccountDB_SQL* db = (BotAccountDB_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, "bot_account_db") == 0 )
safesnprintf(buf, buflen, "%s", db->bot_account_db);
//else
//if( strcmpi(key, "global_acc_reg_str_table") == 0 )
// safesnprintf(buf, buflen, "%s", db->global_acc_reg_str_table);
//else
//if( strcmpi(key, "global_acc_reg_num_table") == 0 )
// safesnprintf(buf, buflen, "%s", db->global_acc_reg_num_table);
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_account_db_sql_set_property(BotAccountDB* self, const char* key, const char* value) {
BotAccountDB_SQL* db = (BotAccountDB_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, "bot_account_db") == 0 )
safestrncpy(db->bot_account_db, value, sizeof(db->bot_account_db));
else
if (strcmpi(key, "login_account_db") == 0)
safestrncpy(db->login_account_db, value, sizeof(db->login_account_db));
//else
//if( strcmpi(key, "global_acc_reg_str_table") == 0 )
// safestrncpy(db->global_acc_reg_str_table, value, sizeof(db->global_acc_reg_str_table));
//else
//if( strcmpi(key, "global_acc_reg_num_table") == 0 )
// safestrncpy(db->global_acc_reg_num_table, value, sizeof(db->global_acc_reg_num_table));
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 account entry.
* acc->account_id = -1 means auto_decidet by server
* bot_acc->account_id is always matching its login table counter part,
* and its value will be written to bot_acc->account_id if everything succeeds.
* @param self: pointer to db
* @param acc: pointer of mmo_bot_account to save
* @return true if successful, false if something has failed
*/
static bool bot_account_db_sql_create(BotAccountDB* self, struct mmo_bot_account* bot_acc) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
Sql* sql_handle_bot = db->bot_accounts;
// validate account id to assign
uint32 account_id;
account_id = bot_acc->account_id;
// zero value is prohibited
if( account_id == 0 )
return false;
// absolute maximum
if( account_id > END_ACCOUNT_NUM )
return false;
// insert the data into the database
bot_acc->account_id = account_id;
return mmo_bot_auth_tosql(db, bot_acc, true, false);
}
/**
* Delete an existing bot account entry and its regs.
* @param self: pointer to db
* @param account_id: id of user account
* @return true if successful, false if something has failed
*/
static bool bot_account_db_sql_remove(BotAccountDB* self, const uint32 account_id) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
Sql* sql_handle = db->bot_accounts;
bool result = false;
if( SQL_SUCCESS != Sql_QueryStr(sql_handle, "START TRANSACTION")
|| SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->bot_account_db, account_id)
)
Sql_ShowDebug(sql_handle);
else
result = true;
result &= ( SQL_SUCCESS == Sql_QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") );
return result;
}
/**
* Update an existing bot_account with the new data provided.
* @param self: pointer to db
* @param bot_acc: pointer of mmo_bot_account to save
* @return true if successful, false if something has failed
*/
static bool bot_account_db_sql_save(BotAccountDB* self, const struct mmo_bot_account* bot_acc, bool refresh_token) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
return mmo_bot_auth_tosql(db, bot_acc, false, refresh_token);
}
/**
* Retrieve data from db and store it in the provided data structure.
* Filled data structure is done by delegation to mmo_bot_auth_fromsql.
* @param self: pointer to db
* @param bot_acc: pointer of mmo_bot_account to fill
* @param account_id: id of user account
* @return true if successful, false if something has failed
*/
static bool bot_account_db_sql_load_num(BotAccountDB* self, struct mmo_bot_account* bot_acc, const uint32 account_id) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
return mmo_bot_auth_fromsql(db, bot_acc, account_id);
}
/**
* Retrieve data from db and store it in the provided data structure.
* Doesn't actually retrieve data yet: escapes and checks userid, then transforms it to accid for fetching.
* Filled data structure is done by delegation to bot_account_db_sql_load_num.
* This function basicly fetches account_ids of all bots of an account
* @param self: pointer to db
* @param bot_acc: pointer of mmo_bot_account to fill
* @param userid: name of user account
* @return true if successful, false if something has failed
*/
static bool bot_account_db_sql_load_str(BotAccountDB* self, struct mmo_bot_account* bot_acc, const char* userid) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
Sql* sql_handle = db->bot_accounts;
char esc_userid[2*NAME_LENGTH+1];
uint32 account_id;
char* data;
Sql_EscapeString(sql_handle, esc_userid, userid);
// get the list of account IDs for this userid
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `userid`= %s '%s'",
db->login_account_db, (db->case_sensitive ? "BINARY" : ""), esc_userid) )
{
Sql_ShowDebug(sql_handle);
return false;
}
if( Sql_NumRows(sql_handle) > 1 )
{// serious problem - duplicit account
ShowError("bot_account_db_sql_load_str: multiple accounts found when retrieving data for account '%s'!\n", userid);
Sql_FreeResult(sql_handle);
return false;
}
if( SQL_SUCCESS != Sql_NextRow(sql_handle) )
{// no such entry
Sql_FreeResult(sql_handle);
return false;
}
//TODO FIXME this will only get the first found bot to a creator (Currently not a problem, because only one bot per account)
Sql_GetData(sql_handle, 0, &data, nullptr);
account_id = atoi(data);
return bot_account_db_sql_load_num(self, bot_acc, account_id);
}
/**
* Create a new forward iterator.
* @param self: pointer to db iterator
* @return a new db iterator
*/
static BotAccountDBIterator* bot_account_db_sql_iterator(BotAccountDB* self) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
BotAccountDBIterator_SQL* iter = (BotAccountDBIterator_SQL*)aCalloc(1, sizeof(BotAccountDBIterator_SQL));
// set up the vtable
iter->vtable.destroy = &bot_account_db_sql_iter_destroy;
iter->vtable.next = &bot_account_db_sql_iter_next;
// fill data
iter->db = db;
iter->last_bot_account_id = -1;
return &iter->vtable;
}
/**
* Destroys this iterator, releasing all allocated memory (including itself).
* @param self: pointer to db iterator
*/
static void bot_account_db_sql_iter_destroy(BotAccountDBIterator* self) {
BotAccountDBIterator_SQL* iter = (BotAccountDBIterator_SQL*)self;
aFree(iter);
}
/**
* Fetches the next account in the database.
* @param self: pointer to db iterator
* @param bot_acc: pointer of mmo_bot_account to fill
* @return true if next account found and filled, false if something has failed
*/
static bool bot_account_db_sql_iter_next(BotAccountDBIterator* self, struct mmo_bot_account* bot_acc) {
BotAccountDBIterator_SQL* iter = (BotAccountDBIterator_SQL*)self;
BotAccountDB_SQL* db = iter->db;
Sql* sql_handle = db->bot_accounts;
char* data;
// get next account ID
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `account_id` > '%d' ORDER BY `account_id` ASC LIMIT 1",
db->bot_account_db, iter->last_bot_account_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 account data
uint32 account_id;
account_id = atoi(data);
if( mmo_bot_auth_fromsql(db, bot_acc, account_id) )
{
iter->last_bot_account_id = account_id;
Sql_FreeResult(sql_handle);
return true;
}
}
Sql_FreeResult(sql_handle);
return false;
}
/**
* Fetch a struct mmo_bot_account from sql.
* @param db: pointer to db
* @param acc: pointer of mmo_bot_account to fill
* @param account_id: id of user account to take data from
* @return true if successful, false if something has failed
*/
static bool mmo_bot_auth_fromsql(BotAccountDB_SQL* db, struct mmo_bot_account* bot_acc, uint32 account_id) {
Sql* sql_handle = db->bot_accounts;
char* data;
// retrieve login entry for the specified account
if( SQL_ERROR == Sql_Query(sql_handle,
#ifdef VIP_ENABLE
"SELECT `account_id`,`is_bot`,`creator_id`,`creator_name` FROM `%s` WHERE `account_id` = %d",
#else
"SELECT `account_id`,`is_bot`,`creator_id`,`creator_name` FROM `%s` WHERE `account_id` = %d",
#endif
db->bot_account_db, account_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); bot_acc->account_id = atoi(data);
Sql_GetData(sql_handle, 1, &data, nullptr); bot_acc->is_bot = (uint8)atoi(data);
Sql_GetData(sql_handle, 2, &data, nullptr); bot_acc->creator_id = atoi(data);
Sql_GetData(sql_handle, 3, &data, nullptr); safestrncpy(bot_acc->creator_name, data, sizeof(bot_acc->creator_name));
//ifdef VIP_ENABLE
//endif
Sql_FreeResult(sql_handle);
return true;
}
/**
* Save a struct mmo_bot_account in sql.
* @param db: pointer to db
* @param bot_acc: pointer of mmo_account to save
* @param is_new: if it's a new entry or should we update
* @return true if successful, false if something has failed
*/
static bool mmo_bot_auth_tosql(BotAccountDB_SQL* db, const struct mmo_bot_account* bot_acc, bool is_new, bool refresh_token) {
Sql* sql_handle = db->bot_accounts;
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` (`account_id`, `is_bot`, `creator_id`, `creator_name`) VALUES (?, ?, ?, ?)",
#else
"INSERT INTO `%s` (`account_id`, `is_bot`, `creator_id`, `creator_name`) VALUES (?, ?, ?, ?)",
#endif
db->bot_account_db)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, (void*)&bot_acc->account_id, sizeof(bot_acc->account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_SHORT, (void*)&bot_acc->is_bot, sizeof(bot_acc->is_bot))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_INT, (void*)&bot_acc->creator_id, sizeof(bot_acc->creator_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_STRING, (void*)&bot_acc->creator_name, strlen(bot_acc->creator_name))
//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)
if( SQL_SUCCESS != SqlStmt_Prepare(stmt,
#ifdef VIP_ENABLE
"UPDATE `%s` SET `is_bot`=? WHERE `account_id` = '%d'",
#else
"UPDATE `%s` SET `is_bot`=? WHERE `account_id` = '%d'",
#endif
db->bot_account_db, bot_acc->account_id)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void*)bot_acc->is_bot, sizeof(bot_acc->is_bot))
#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;
}
/*
void mmo_save_global_accreg(AccountDB* self, int fd, uint32 account_id, uint32 char_id) {
Sql* sql_handle = ((AccountDB_SQL*)self)->accounts;
AccountDB_SQL* db = (AccountDB_SQL*)self;
uint16 count = RFIFOW(fd, 12);
if (count) {
int cursor = 14, i;
char key[32], sval[254], esc_key[32*2+1], esc_sval[254*2+1];
for (i = 0; i < count; i++) {
uint32 index;
safestrncpy(key, RFIFOCP(fd, cursor + 1), RFIFOB(fd, cursor));
Sql_EscapeString(sql_handle, esc_key, key);
cursor += RFIFOB(fd, cursor) + 1;
index = RFIFOL(fd, cursor);
cursor += 4;
switch (RFIFOB(fd, cursor++)) {
// int
case 0:
if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%" PRIu32 "','%s','%" PRIu32 "','%" PRId64 "')", db->global_acc_reg_num_table, account_id, esc_key, index, RFIFOQ(fd, cursor)) )
Sql_ShowDebug(sql_handle);
cursor += 8;
break;
case 1:
if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%" PRIu32 "' AND `key` = '%s' AND `index` = '%" PRIu32 "' LIMIT 1", db->global_acc_reg_num_table, account_id, esc_key, index) )
Sql_ShowDebug(sql_handle);
break;
// str
case 2:
safestrncpy(sval, RFIFOCP(fd, cursor + 1), RFIFOB(fd, cursor));
cursor += RFIFOB(fd, cursor) + 1;
Sql_EscapeString(sql_handle, esc_sval, sval);
if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%" PRIu32 "','%s','%" PRIu32 "','%s')", db->global_acc_reg_str_table, account_id, esc_key, index, esc_sval) )
Sql_ShowDebug(sql_handle);
break;
case 3:
if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%" PRIu32 "' AND `key` = '%s' AND `index` = '%" PRIu32 "' LIMIT 1", db->global_acc_reg_str_table, account_id, esc_key, index) )
Sql_ShowDebug(sql_handle);
break;
default:
ShowError("mmo_save_global_accreg: unknown type %d\n",RFIFOB(fd, cursor - 1));
return;
}
}
}
}*/
/*
void mmo_send_global_accreg(AccountDB* self, int fd, uint32 account_id, uint32 char_id) {
Sql* sql_handle = ((AccountDB_SQL*)self)->accounts;
AccountDB_SQL* db = (AccountDB_SQL*)self;
char* data;
int16 plen = 0;
size_t len;
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%" PRIu32 "'", db->global_acc_reg_str_table, account_id) )
Sql_ShowDebug(sql_handle);
WFIFOHEAD(fd, 60000 + 300);
WFIFOW(fd, 0) = 0x2726;
// 0x2 = length, set prior to being sent
WFIFOL(fd, 4) = account_id;
WFIFOL(fd, 8) = char_id;
WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
WFIFOB(fd, 13) = 1; // is string type
WFIFOW(fd, 14) = 0; // count
plen = 16;
//
// * Vessel!
// *
// * str type
// * { keyLength(B), key(<keyLength>), index(L), valLength(B), val(<valLength>) }
//
while ( SQL_SUCCESS == Sql_NextRow(sql_handle) ) {
Sql_GetData(sql_handle, 0, &data, nullptr);
len = strlen(data)+1;
WFIFOB(fd, plen) = (unsigned char)len; // won't be higher; the column size is 32
plen += 1;
safestrncpy(WFIFOCP(fd,plen), data, len);
plen += static_cast<decltype(plen)>( len );
Sql_GetData(sql_handle, 1, &data, nullptr);
WFIFOL(fd, plen) = (uint32)atol(data);
plen += 4;
Sql_GetData(sql_handle, 2, &data, nullptr);
len = strlen(data)+1;
WFIFOB(fd, plen) = (unsigned char)len; // won't be higher; the column size is 254
plen += 1;
safestrncpy(WFIFOCP(fd,plen), data, len);
plen += static_cast<decltype(plen)>( len );
WFIFOW(fd, 14) += 1;
if( plen > 60000 ) {
WFIFOW(fd, 2) = plen;
WFIFOSET(fd, plen);
// prepare follow up
WFIFOHEAD(fd, 60000 + 300);
WFIFOW(fd, 0) = 0x2726;
// 0x2 = length, set prior to being sent
WFIFOL(fd, 4) = account_id;
WFIFOL(fd, 8) = char_id;
WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
WFIFOB(fd, 13) = 1; // is string type
WFIFOW(fd, 14) = 0; // count
plen = 16;
}
}
WFIFOW(fd, 2) = plen;
WFIFOSET(fd, plen);
Sql_FreeResult(sql_handle);
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%" PRIu32 "'", db->global_acc_reg_num_table, account_id) )
Sql_ShowDebug(sql_handle);
WFIFOHEAD(fd, 60000 + 300);
WFIFOW(fd, 0) = 0x2726;
// 0x2 = length, set prior to being sent
WFIFOL(fd, 4) = account_id;
WFIFOL(fd, 8) = char_id;
WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
WFIFOB(fd, 13) = 0; // is int type
WFIFOW(fd, 14) = 0; // count
plen = 16;
//
// * Vessel!
// *
// * int type
// * { keyLength(B), key(<keyLength>), index(L), value(L) }
//
while ( SQL_SUCCESS == Sql_NextRow(sql_handle) ) {
Sql_GetData(sql_handle, 0, &data, nullptr);
len = strlen(data)+1;
WFIFOB(fd, plen) = (unsigned char)len; // won't be higher; the column size is 32
plen += 1;
safestrncpy(WFIFOCP(fd,plen), data, len);
plen += static_cast<decltype(plen)>( len );
Sql_GetData(sql_handle, 1, &data, nullptr);
WFIFOL(fd, plen) = (uint32)atol(data);
plen += 4;
Sql_GetData(sql_handle, 2, &data, nullptr);
WFIFOQ(fd, plen) = strtoll(data,nullptr,10);
plen += 8;
WFIFOW(fd, 14) += 1;
if( plen > 60000 ) {
WFIFOW(fd, 2) = plen;
WFIFOSET(fd, plen);
// prepare follow up
WFIFOHEAD(fd, 60000 + 300);
WFIFOW(fd, 0) = 0x2726;
// 0x2 = length, set prior to being sent
WFIFOL(fd, 4) = account_id;
WFIFOL(fd, 8) = char_id;
WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
WFIFOB(fd, 13) = 0; // is int type
WFIFOW(fd, 14) = 0; // count
plen = 16;
}
}
WFIFOB(fd, 12) = 1;
WFIFOW(fd, 2) = plen;
WFIFOSET(fd, plen);
Sql_FreeResult(sql_handle);
}*/
/*
bool account_db_sql_enable_webtoken( AccountDB* self, const uint32 account_id ){
AccountDB_SQL* db = (AccountDB_SQL*)self;
if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token_enabled` = '1' WHERE `account_id` = '%u'", db->account_db, account_id ) ){
Sql_ShowDebug( db->accounts );
return false;
}
return true;
}*/
/**
* Timered function to disable webtoken for user
* If the user is online, then they must have logged since we started the timer.
* In that case, do nothing. The new authtoken must be valid.
* @param tid: timer id
* @param tick: tick of execution
* @param id: user account id
* @param data: BotAccountDB // because we don't use singleton???
* @return :0
TIMER_FUNC(account_disable_webtoken_timer){
const struct online_login_data* p = login_get_online_user(id);
AccountDB_SQL* db = reinterpret_cast<AccountDB_SQL*>(data);
if (p == nullptr) {
ShowInfo("Web Auth Token for account %d was disabled\n", id);
if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token_enabled` = '0' WHERE `account_id` = '%u'", db->account_db, id ) ){
Sql_ShowDebug( db->accounts );
return 0;
}
}
return 0;
}
*/
/*
bool account_db_sql_disable_webtoken( AccountDB* self, const uint32 account_id ){
AccountDB_SQL* db = (AccountDB_SQL*)self;
//add_timer(gettick() + brokk_config.disable_webtoken_delay, account_disable_webtoken_timer, account_id, reinterpret_cast<intptr_t>(db));
return true;
}*/
/*
bool account_db_sql_remove_webtokens( AccountDB* self ){
AccountDB_SQL* db = (AccountDB_SQL*)self;
if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token` = NULL, `web_auth_token_enabled` = '0'", db->account_db ) ){
Sql_ShowDebug( db->accounts );
return false;
}
return true;
}*/

@ -1,133 +0,0 @@
// 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 BOT_ACCOUNT_HPP
#define BOT_ACCOUNT_HPP
#include <common/cbasetypes.hpp>
#include <common/mmo.hpp> // ACCOUNT_REG2_NUM, WEB_AUTH_TOKEN_LENGTH
#include <config/core.hpp>
typedef struct BotAccountDB BotAccountDB;
typedef struct BotAccountDBIterator BotAccountDBIterator;
// standard engines
BotAccountDB* bot_account_db_sql(void);
struct mmo_bot_account {
uint32 account_id; // ID of corresponding login account
int is_bot; // Flag for if account is declared bot (will be controllable by sindri)
uint32 creator_id; // ID of login account of creating/summoning player [Default: 0 for config generated bots)
char creator_name[NAME_LENGTH]; // Name of the player character that spawned/summoned the bot [Default: Brokk for generated bots)
};
struct BotAccountDBIterator {
/// Destroys this iterator, releasing all allocated memory (including itself).
///
/// @param self Iterator
void (*destroy)(BotAccountDBIterator* self);
/// Fetches the next account in the database.
/// Fills acc with the account data.
/// @param self Iterator
/// @param acc Account data
/// @return true if successful
bool (*next)(BotAccountDBIterator* self, struct mmo_bot_account* bot_acc);
};
struct BotAccountDB {
/// Initializes this database, making it ready for use.
/// Call this after setting the properties.
///
/// @param self Database
/// @return true if successful
bool (*init)(BotAccountDB* self);
/// Destroys this database, releasing all allocated memory (including itself).
///
/// @param self Database
void (*destroy)(BotAccountDB* 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)(BotAccountDB* 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)(BotAccountDB* self, const char* key, const char* value);
/// Creates a new account in this database.
/// bot_acc->account_id must always be set, because there will never be a valid bot_account without login-table account
///
/// @param self Database
/// @param acc Account data
/// @return true if successful
bool (*create)(BotAccountDB* self, struct mmo_bot_account* bot_acc);
/// Removes an account from this database.
///
/// @param self Database
/// @param account_id Account id
/// @return true if successful
bool (*remove)(BotAccountDB* self, const uint32 account_id);
/// Enables the web auth token for the given account id
//bool (*enable_webtoken)(BotAccountDB* self, const uint32 account_id);
/// Disables the web auth token for the given account id
//bool (*disable_webtoken)(BotAccountDB* self, const uint32 account_id);
/// Removes the web auth token for all accounts
//bool (*remove_webtokens)(BotAccountDB* self);
/// Modifies the data of an existing account.
/// Uses bot_acc->account_id to identify the account.
///
/// @param self Database
/// @param bot_acc Account data
/// @param refresh_token Whether or not to refresh the web auth token
/// @return true if successful
bool (*save)(BotAccountDB* self, const struct mmo_bot_account* bot_acc, bool refresh_token);
/// Finds an bot_account with account_id and copies it to bot_acc.
///
/// @param self Database
/// @param bot_acc Pointer that receives the account data
/// @param account_id Target account id
/// @return true if successful
bool (*load_num)(BotAccountDB* self, struct mmo_bot_account* bot_acc, const uint32 account_id);
/// Finds an bot_account with creator_name and copies it to bot_acc.
///
/// @param self Database
/// @param bot_acc Pointer that receives the account data
/// @param userid Target username
/// @return true if successful
bool (*load_str)(BotAccountDB* self, struct mmo_bot_account* bot_acc, const char* userid);
/// Returns a new forward iterator.
///
/// @param self Database
/// @return Iterator
BotAccountDBIterator* (*iterator)(BotAccountDB* self);
};
//void mmo_send_global_accreg(BotAccountDB* self, int fd, uint32 account_id, uint32 char_id);
//void mmo_save_global_accreg(BotAccountDB* self, int fd, uint32 account_id, uint32 char_id);
#endif /* BOT_ACCOUNT_HPP */

@ -6,7 +6,7 @@
#pragma warning(disable:4800)
#include "brokk.hpp"
//#include <common/cli.hpp>
#include <common/cli.hpp>
#include <common/core.hpp>
#include <common/malloc.hpp>
#include <common/md5calc.hpp>
@ -16,753 +16,17 @@
#include <common/showmsg.hpp>
#include <common/socket.hpp> //ip2str
#include <common/strlib.hpp>
#include <common/sql.hpp>
#include <common/timer.hpp>
#include <common/utilities.hpp>
#include <common/utils.hpp>
#include <config/core.hpp>
#include <vector>
#include <fstream> // for file handling
#include <map>
#include <memory>
#include <string>
#include <sstream>
#include <iostream>
#include <char/char.hpp>
#include "pc.hpp"
#include "account.hpp"
#include "bot_account.hpp"
#include "charakters.hpp"
#include "json.hpp" // The header-only library for JSON parsing
//helpers (Store adepted functions from different areas in rathena
//#include "config_helpers.hpp"
#include "char_creation_helpers.hpp"
#include "helper_skills.hpp"
using namespace rathena;
using namespace rathena::server_brokk;
using json = nlohmann::json;
struct Brokk_Config brokk_config; /// Configuration of brokk-server
// DB Object of all touched infrastructure
AccountDB* login_accounts = nullptr;
BotAccountDB* bot_accounts = nullptr;
CharDB* charakters = nullptr;
SkillDB* skills = nullptr;
// YAML DB Objects of all touched infrastucture
BotStatPointDatabase bot_statpoint_db;
BotJobDatabase bot_job_db;
//SkillTreeDatabase skill_tree_db;
// Advanced subnet check [LuzZza]
struct s_subnet {
uint32 mask;
uint32 char_ip;
uint32 map_ip;
} subnet[16];
int subnet_count = 0; //number of subnet config
int brokk_fd; // brokk server file descriptor socket
/**
* Retrieves if account_id is online
struct online_login_data* login_get_online_user(uint32 account_id) {
return util::umap_find(online_db, account_id);
}
*/
TIMER_FUNC(periodic_cleanup_timer) {
bot_account_cleanup(); // Call the cleanup function
return 0; // Return 0 to keep the timer going
}
void setup_periodic_cleanup_timer() {
// Set up a timer to run the cleanup function every 15 minutes (900,000 milliseconds)
add_timer_interval(gettick() + 900000, periodic_cleanup_timer, 0, 0, 900000);
}
/**
* Create a new bot account and save it in db/sql.
* @param userid: string for bot login
* @param pass: string for bot password
* @param sex: should be M|F|S
* @param last_ip: the IP address to associate with the bot
* @return :
* -1: success
* 0: unregistered id (invalid sex or failure to create in db)
* 1: incorrect pass or userid (userid/pass too short or already exists)
* 2: Error while writing into bot_accounts table
* 3: Registration denied
*/
int brokk_mmo_auth_new(const char* userid, const char* pass, const char sex, const char* last_ip, const bool auto_generated, int creator_id = 0, const char* creator_name = "Brokk") {
static int num_regs = 0; // bot registration counter
static t_tick new_reg_tick = 0;
t_tick tick = gettick();
struct mmo_account acc;
struct mmo_bot_account bot_acc;
// Bot account registration flood protection
if (new_reg_tick == 0)
new_reg_tick = gettick();
if (auto_generated == false) {
if (DIFF_TICK(tick, new_reg_tick) < 0 && num_regs >= brokk_config.allowed_bot_creations) {
ShowNotice("Bot account registration denied (registration limit exceeded)\n");
return 3;
}
}
// Validate user ID and password length
if (strlen(userid) < brokk_config.acc_name_min_length || strlen(pass) < brokk_config.password_min_length)
return 1;
// Ensure valid sex input (M/F for bots)
if (sex != 'M' && sex != 'F')
return 0; // Unregistered ID
// Check if the bot account already exists
if (login_accounts->load_str(login_accounts, &acc, userid)) {
ShowNotice("Attempt to create an already existing bot account (account: %s, sex: %c)\n", userid, sex);
return 1; // Account exists
}
// First create main login account
// Clear and populate account structure
memset(&acc, '\0', sizeof(acc));
acc.account_id = -1; // Assigned by account db
safestrncpy(acc.userid, userid, sizeof(acc.userid));
safestrncpy(acc.pass, pass, sizeof(acc.pass));
acc.sex = sex;
safestrncpy(acc.email, "bot@brokk.ai", sizeof(acc.email)); // Default email for bots
acc.expiration_time = 0; // Bots don't expire
safestrncpy(acc.lastlogin, "", sizeof(acc.lastlogin));
safestrncpy(acc.last_ip, last_ip, sizeof(acc.last_ip));
safestrncpy(acc.birthdate, "", sizeof(acc.birthdate));
safestrncpy(acc.pincode, "", sizeof(acc.pincode));
acc.pincode_change = 0;
acc.char_slots = MIN_CHARS;
#ifdef VIP_ENABLE
acc.vip_time = 0;
acc.old_group = 0;
#endif
// Create the account in the database
if (!login_accounts->create(login_accounts, &acc))
return 0; // Account creation failed
// Second create bot account entry
// Check if the bot account already exists
if (bot_accounts->load_num(bot_accounts, &bot_acc, acc.account_id)) {
ShowNotice("Attempt to create an already existing bot account (account: %s, sex: %c)\n", acc.account_id, sex);
return 1; // Account exists
}
// Clear and populate account structure
memset(&bot_acc, '\0', sizeof(bot_acc));
bot_acc.account_id = acc.account_id; // Assigned by account db
bot_acc.is_bot = 1;
bot_acc.creator_id = creator_id; // Assigned by account db
safestrncpy(bot_acc.creator_name, creator_name, sizeof(bot_acc.creator_name));
//ifdef VIP_ENABLE
//endif
// Create the account in the database
if (!bot_accounts->create(bot_accounts, &bot_acc))
return 0; // Account creation failed
ShowNotice("Bot account creation successful (account %s, id: %d, sex: %c)\n", acc.userid, acc.account_id, acc.sex);
// Registration rate limit logic
if (DIFF_TICK(tick, new_reg_tick) > 0) {
num_regs = 0;
new_reg_tick = tick + brokk_config.time_allowed * 1000;
}
++num_regs;
return -1; // Success
}
/**
* Delete a bot account from db/sql by userid.
* @param userid: string for bot login
* @return :
* 0: success
* 1: failure (account does not exist)
*/
int brokk_mmo_auth_delete_str(const char* userid) {
struct mmo_account acc;
struct mmo_bot_account bot_acc;
// Check if the account exists
if (!login_accounts->load_str(login_accounts, &acc, userid)) {
ShowNotice("Attempt to delete non-existent account (account: %s)\n", userid);
return 1; // Account does not exist
}
// Use loaded acc to remove bot(s)
//TODO FIXME: Potential error when is_bot is disabled, deletion of account on other ways then by brokk
// could result in orphaned bot_account entries-> needs check
if (!bot_accounts->load_num(bot_accounts, &bot_acc, acc.account_id)) {
ShowNotice("Attempt to delete non_exiting bot account (accid: %s)\n", acc.account_id);
return 1; // Bot account does not exist
}
// Delete login account and corresponding bot account
if (!login_accounts->remove(login_accounts, acc.account_id)) {
ShowError("Failed to delete login account (account: %s)\n", userid);
return 1; // Deletion failed
}
if (!bot_accounts->remove(bot_accounts, bot_acc.account_id)) {
ShowError("Failed to delete bot account (account: %s)\n", userid);
return 1; // Deletion failed
}
ShowNotice("Bot account deleted successfully (account: %s, id: %d)\n", acc.userid, acc.account_id);
return 0; // Success
}
/**
* Delete a bot account from db/sql by accid.
* @param acccount_id: int
* @return :
* 0: success
* 1: failure (account does not exist)
*/
int brokk_mmo_auth_delete_num(uint32 account_id) {
struct mmo_account acc;
struct mmo_bot_account bot_acc;
struct mmo_charakter charakter;
std::vector<uint32> char_ids_to_delete;
// Check if the account exists
if (!login_accounts->load_num(login_accounts, &acc, account_id)) {
ShowNotice("Attempt to delete non-existent account (account_id: %d)\n", account_id);
return 1; // Account does not exist
}
// Use loaded acc to remove bot(s)
//TODO FIXME: Potential error when is_bot is disabled, deletion of account on other ways then by brokk
// could result in orphaned bot_account entries-> needs check
if (!bot_accounts->load_num(bot_accounts, &bot_acc, account_id)) {
ShowNotice("Attempt to delete non_exiting bot account (accid: %d)\n", account_id);
return 1; // Bot account does not exist
}
// Delete login account and corresponding bot account
if (!login_accounts->remove(login_accounts, acc.account_id)) {
ShowError("Failed to delete login account (account_id: %d)\n", account_id);
return 1; // Deletion failed
}
if (!bot_accounts->remove(bot_accounts, bot_acc.account_id)) {
ShowError("Failed to delete bot account (account_id: %d)\n", account_id);
return 1; // Deletion failed
}
//Attempt to delete charakters of the bot (if any)
// First pass: Collect char_ids to delete based on config
CharakterDBIterator* iter_chars = charakters->iterator(charakters); // Create the iterator
while (iter_chars->next(iter_chars, &charakter)) {
//ShowDebug("Charakter %s scanned\n", charakter.name);
if (charakter.account_id == account_id) {
//ShowInfo("Charakter %s marked for deletion\n", charakter.name);
char_ids_to_delete.push_back(charakter.char_id);
}
}
iter_chars->destroy(iter_chars); // Destroy the iterator after use
// Second pass: Delete the collected charakters
for (const auto& char_id : char_ids_to_delete) {
ShowInfo("Deleting charakter with ID %d.\n", char_id);
if (!charakters->remove(charakters, char_id)) {
ShowError("Failed to delete charakter (char_id: %s)\n", char_id);
continue; // Deletion failed
}
}
ShowNotice("Bot account deletion completed (account: %s, id: %d)\n\n", acc.userid, acc.account_id);
return 0; // Success
}
/// Set and read Configurations
/**
* Reading Lan Support configuration.
* @param lancfgName: Name of the lan configuration (could be fullpath)
* @return 0:success, 1:failure (file not found|readable)
int brokk_lan_config_read(const char* lancfgName) {
FILE* fp;
int line_num = 0, s_subnet = ARRAYLENGTH(subnet);
char line[1024], w1[64], w2[64], w3[64], w4[64];
if ((fp = fopen(lancfgName, "r")) == nullptr) {
ShowWarning("LAN Support configuration file is not found: %s\n", lancfgName);
return 1;
}
while (fgets(line, sizeof(line), fp))
{
line_num++;
if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n')
continue;
if (sscanf(line, "%63[^:]: %63[^:]:%63[^:]:%63[^\r\n]", w1, w2, w3, w4) != 4)
{
ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num);
continue;
}
if (strcmpi(w1, "subnet") == 0) {
if (subnet_count >= s_subnet) { //We skip instead of break in case we want to add other conf in that file.
ShowError("%s: Too many subnets defined, skipping line %d...\n", lancfgName, line_num);
continue;
}
subnet[subnet_count].mask = str2ip(w2);
subnet[subnet_count].char_ip = str2ip(w3);
subnet[subnet_count].map_ip = str2ip(w4);
if ((subnet[subnet_count].char_ip & subnet[subnet_count].mask) != (subnet[subnet_count].map_ip & subnet[subnet_count].mask))
{
ShowError("%s: Configuration Error: The char server (%s) and map server (%s) belong to different subnetworks!\n", lancfgName, w3, w4);
continue;
}
subnet_count++;
}
}
if (subnet_count > 1) // only useful if there is more than 1 available /
ShowStatus("Read information about %d subnetworks.\n", subnet_count);
fclose(fp);
return 0;
}*/
/**
* Reading main configuration file.
* @param cfgName: Name of the configuration (could be fullpath)
* @param normal: Config read normally when server started
* @return True:success, Fals:failure (file not found|readable)
*/
bool brokk_config_read(const char* cfgName, bool normal) {
char line[1024], w1[32], w2[1024];
FILE* fp = fopen(cfgName, "r");
if (fp == nullptr) {
ShowError("Configuration file (%s) not found.\n", cfgName);
return false;
}
while (fgets(line, sizeof(line), fp)) {
if (line[0] == '/' && line[1] == '/')
continue;
if (sscanf(line, "%31[^:]: %1023[^\r\n]", w1, w2) < 2)
continue;
// Config that loaded only when server started, not by reloading config file
if (normal) {
if (!strcmpi(w1, "bind_ip")) {
brokk_config.brokk_ip = host2ip(w2);
if (brokk_config.brokk_ip) {
char ip_str[16];
ShowStatus("Brokk server binding IP address : %s -> %s\n", w2, ip2str(brokk_config.brokk_ip, ip_str));
}
}
else if (!strcmpi(w1, "brokk_port"))
brokk_config.brokk_port = (uint16)atoi(w2);
else if (!strcmpi(w1, "console"))
brokk_config.console = (bool)config_switch(w2);
}
if (!strcmpi(w1, "timestamp_format"))
safestrncpy(timestamp_format, w2, 20);
else if (strcmpi(w1, "db_path") == 0)
safestrncpy(db_path, w2, ARRAYLENGTH(db_path));
else if (!strcmpi(w1, "stdout_with_ansisequence"))
stdout_with_ansisequence = config_switch(w2);
else if (!strcmpi(w1, "console_silent")) {
msg_silent = atoi(w2);
if (msg_silent) /* only bother if we actually have this enabled */
ShowInfo("Console Silent Setting: %d\n", atoi(w2));
}
else if (strcmpi(w1, "console_msg_log") == 0)
console_msg_log = atoi(w2);
else if (strcmpi(w1, "console_log_filepath") == 0)
safestrncpy(console_log_filepath, w2, sizeof(console_log_filepath));
else if (!strcmpi(w1, "log_brokk"))
brokk_config.log_brokk = (bool)config_switch(w2);
else if (!strcmpi(w1, "date_format"))
safestrncpy(brokk_config.date_format, w2, sizeof(brokk_config.date_format));
else if (!strcmpi(w1, "allowed_bot_creations"))
brokk_config.allowed_bot_creations = (uint32)atoi(w2);
else if (!strcmpi(w1, "destroy_all_on_shutdown"))
brokk_config.destroy_all_on_shutdown = atoi(w2);
else if (!strcmpi(w1, "acc_name_min_length"))
brokk_config.acc_name_min_length = cap_value(atoi(w2), 0, NAME_LENGTH - 1);
else if (!strcmpi(w1, "password_min_length"))
brokk_config.password_min_length = cap_value(atoi(w2), 0, PASSWD_LENGTH - 1);
else if (!strcmpi(w1, "time_allowed"))
brokk_config.time_allowed = atoi(w2);
else if (!strcmpi(w1, "import"))
brokk_config_read(w2, normal);
else {// try the account engines
if (!normal)
continue;
if (login_accounts && login_accounts->set_property(login_accounts, w1, w2))
continue;
}
}
fclose(fp);
ShowInfo("Finished reading %s.\n", cfgName);
return true;
}
/**
* Init brokk-serv default configuration.
*/
void brokk_set_defaults() {
brokk_config.brokk_ip = INADDR_ANY;
brokk_config.brokk_port = 7500;
brokk_config.log_brokk = true;
safestrncpy(brokk_config.date_format, "%Y-%m-%d %H:%M:%S", sizeof(brokk_config.date_format));
brokk_config.console = false;
brokk_config.destroy_all_on_shutdown = 0; //Default all Bots are preserved on shutdown
brokk_config.allowed_bot_creations = 10;
brokk_config.acc_name_min_length = 6;
brokk_config.password_min_length = 6;
brokk_config.time_allowed = 10; //in second
//other default conf
safestrncpy(brokk_config.brokkconf_name, "conf/brokk_athena.conf", sizeof(brokk_config.brokkconf_name));
safestrncpy(brokk_config.msgconf_name, "conf/msg_conf/brokk_msg.conf", sizeof(brokk_config.msgconf_name));
}
void bot_account_cleanup() {
struct mmo_bot_account bot_acc;
BotAccountDBIterator* iter_bots = bot_accounts->iterator(bot_accounts);
std::vector<uint32> account_ids_to_delete;
// First pass: Collect account_ids to delete based on config
while (iter_bots->next(iter_bots, &bot_acc)) {
// Check based on config
if (brokk_config.destroy_all_on_shutdown == 2 || (brokk_config.destroy_all_on_shutdown == 1 && strcmp(bot_acc.creator_name, "Brokk") == 0)) {
// Collect account_id for deletion
account_ids_to_delete.push_back(bot_acc.account_id);
}
}
// Destroy the iterator after use
iter_bots->destroy(iter_bots);
// Second pass: Delete the collected accounts
for (const auto& account_id : account_ids_to_delete) {
ShowInfo("Deleting bot account with ID %d.\n", account_id);
brokk_mmo_auth_delete_num(account_id); // Delete from `login`, `bot_accounts` and 'char' tables
}
ShowNotice("Bot account destruction completed successfully.\n\n");
}
/*
* initializes dbs for all used non-yaml databases
* returns true if successfull
*/
bool brokk_initialize_dbs()
{
// Account database init (login table)
if (login_accounts == nullptr) {
ShowFatalError("do_init: account engine not found.\n");
return false;
}
else {
if (!login_accounts->init(login_accounts)) {
ShowFatalError("do_init: Failed to initialize account engine.\n");
return false;
}
}
// bot Account database init (bot_accounts table)
if (bot_accounts == nullptr) {
ShowFatalError("do_init: bot account engine not found.\n");
return false;
}
else {
if (!bot_accounts->init(bot_accounts)) {
ShowFatalError("do_init: Failed to initialize bot account engine.\n");
return false;
}
}
// Char database init (char table)
if (charakters == nullptr) {
ShowFatalError("do_init: charakter engine not found.\n");
return false;
}
else {
if (!charakters->init(charakters)) {
ShowFatalError("do_init: Failed to initialize charakters engine.\n");
return false;
}
}
// Skill database init (skill table)
if (skills == nullptr) {
ShowFatalError("do_init: skill engine not found.\n");
return false;
}
else {
if (!skills->init(skills)) {
ShowFatalError("do_init: Failed to initialize skill engine.\n");
return false;
}
}
return true;
}
// Convert time_t to string and store it in last_login (char[24])
void set_last_login(char last_login[24]) {
time_t now = time(nullptr);
struct tm timeinfo;
#if defined(_WIN32) || defined(_WIN64)
// Windows-specific localtime_s version
localtime_s(&timeinfo, &now);
#else
// Standard version for POSIX systems
localtime_r(&now, &timeinfo);
#endif
// Format time as string "YYYY-MM-DD HH:MM:SS"
strftime(last_login, 24, "%Y-%m-%d %H:%M:%S", &timeinfo);
}
/**
* Create a new charakter and save it in db/sql.
* @param account_id: int for account, the charakter belongs to
* @param char_name: string for charakter name
* @param slot: int which charakter slot to put it in (char_num in db)
* @param <multiple stat fields>: stats of the charakter (str,agi,vit,etc)
* @param hair_style: int for hair_style id
* @param hair_color: int for hair_color id *
* @param start_job: short for job class
* @param sex: string for gender of character
* @return :
* -1: success
* 0: unregistered id (invalid sex or failure to create in db)
* 1: incorrect char_name (char_name too short or already exists)
* 2: Error while writing into char table
* 3: Registration denied (limit reached)
*/
int brokk_create_character(uint32 account_id, const char* char_name, int slot, int base_level, int job_level, int str, int agi, int vit, int int_, int dex, int luk, int hair_style, int hair_color, short start_job, char sex, const bool auto_generated) {
static int num_regs = 0; // char registration counter
static t_tick new_reg_tick = 0;
t_tick tick = gettick();
struct mmo_charakter charakter;
struct mmo_account acc;
// Check if the account exists
if (!login_accounts->load_num(login_accounts, &acc, account_id)) {
ShowNotice("Attempt to create char for non-existent account (account_id: %s)\n", account_id);
return 0; // Account does not exist
}
// char registration flood protection
if (new_reg_tick == 0)
new_reg_tick = gettick();
if (auto_generated == false) {
if (DIFF_TICK(tick, new_reg_tick) < 0 && num_regs >= brokk_config.allowed_bot_creations) {
ShowNotice("Charakter registration denied (registration limit exceeded)\n");
return 3;
}
}
// Ensure valid sex input (M/F for bots)
if (sex != 'M' && sex != 'F')
return 0; // Unregistered ID
// Check if the char already exists
if (charakters->load_str(charakters, &charakter, char_name)) {
ShowNotice("Attempt to create an already existing charakter (CharName: %s, sex: %c)\n", char_name, sex);
return 1; // Char exists
}
//TODO Herausfinden welche char Level und stats wann plausibel sind.
// -> Plausibility checks aus map.cpp extrahieren
//TODO2 Homogene Base-Level grenzen herausfinden (bei welchem Base-Level ist man welches Job-Level bei Renewal
// Create charakter
// Clear and populate charakter structure
memset(&charakter, '\0', sizeof(charakter));
charakter.char_id = -1; // Assigned by char db
// Assign values from parameters
charakter.account_id = account_id;
charakter.char_num = slot;
safestrncpy(charakter.name, char_name, sizeof(charakter.name));
charakter.str = str;
charakter.agi = agi;
charakter.vit = vit;
charakter.int_ = int_;
charakter.dex = dex;
charakter.luk = luk;
charakter.hair = hair_style;
charakter.hair_color = hair_color;
charakter.class_ = start_job;
charakter.sex = sex;
// Default values for new character creation
charakter.base_level = base_level;
charakter.job_level = job_level;
charakter.base_exp = bot_job_db.get_baseExp(start_job, base_level);
charakter.job_exp = bot_job_db.get_jobExp(start_job, job_level);
charakter.zeny = 1000000;
charakter.pow = 0;
charakter.sta = 0;
charakter.wis = 0;
charakter.spl = 0;
charakter.con = 0;
charakter.crt = 0;
charakter.max_hp = 40 * (100 + vit) / 100; // Default calculation for max HP
charakter.hp = charakter.max_hp;
charakter.max_sp = 11 * (100 + int_) / 100; // Default calculation for max SP
charakter.sp = charakter.max_sp;
charakter.max_ap = 0; // Default AP for new characters
charakter.ap = 0;
// Calculate Status Points from current level
charakter.status_point = bot_statpoint_db.get_table_point(base_level);
charakter.skill_point = 0;
charakter.trait_point = bot_statpoint_db.get_trait_table_point(base_level);;
charakter.option = 0;
charakter.karma = 0;
charakter.manner = 0;
charakter.party_id = 0;
charakter.guild_id = 0;
charakter.pet_id = 0;
charakter.homun_id = 0;
charakter.elemental_id = 0;
charakter.body = 0;
charakter.weapon = 0;
charakter.shield = 0;
charakter.head_top = 0;
charakter.head_mid = 0;
charakter.head_bottom = 0;
charakter.robe = 0;
safestrncpy(charakter.last_map, "morocc", sizeof(charakter.last_map)); // Default spawn map
charakter.last_x = 159; // Default spawn position x
charakter.last_y = 91; // Default spawn position y
charakter.last_instanceid = 0;
safestrncpy(charakter.save_map, "morocc", sizeof(charakter.save_map)); // Default save point map
charakter.save_x = 156; // Default save point position x
charakter.save_y = 45; // Default save point position y
/*TODO Meta Profiles hier laden : Für payon_citizen
* safestrncpy(charakter.save_map, "payon", sizeof(charakter.save_map)); // Default save point map
* charakter.save_x = 161; // Default save point position x
* charakter.save_y = 57; // Default save point position y
*/
charakter.partner_id = 0;
charakter.online = 0;
charakter.fame = 0;
charakter.rename = 0;
charakter.delete_date = 0;
charakter.moves = 0;
charakter.unban_time = 0;
charakter.font = 0;
charakter.uniqueitem_counter = 0;
charakter.hotkey_rowshift = 0;
charakter.hotkey_rowshift2 = 0;
charakter.clan_id = 0;
charakter.title_id = 0;
charakter.show_equip = 0;
charakter.inventory_slots = 100; // Default inventory slots
charakter.body_direction = 0;
charakter.disable_call = 0;
// Set default timestamp for last login to current time (or keep it empty for new character)
memset(charakter.last_login, '\0', sizeof(charakter.last_login)); // Clear the memory
set_last_login(charakter.last_login);
//#ifdef VIP_ENABLE
//#endif
// Create the account in the database
if (!charakters->create(charakters, &charakter))
return 0; // Charakter creation failed
ShowNotice("Bot charakter creation successful (name: %s, char_id: %d, sex: %c)\n", charakter.name, charakter.char_id, charakter.sex);
// Registration rate limit logic
if (DIFF_TICK(tick, new_reg_tick) > 0) {
num_regs = 0;
new_reg_tick = tick + brokk_config.time_allowed * 1000;
}
++num_regs;
//ShowInfo("Bot character created: %s (account: %s, char_name: %d)\n", charakter.account_id, charakter.name);
return -1;
}
void BrokkServer::finalize() {
AccountDB* db = login_accounts;
BotAccountDB* bot_db = bot_accounts;
CharDB* char_db = charakters;
SkillDB* skill_db = skills;
//brokk_log(0, "brokk server", 100, "brokk server shutdown");
ShowStatus("Terminating...\n");
//if (brokk_config.log_brokk)
// brokklog_final();
// Attempt to delete the bot account on shutdown
bot_account_cleanup();
if (db) { // destroy account engine
db->destroy(db);
db = nullptr;
}
if (bot_db) { // destroy bot account engine
bot_db->destroy(bot_db);
bot_db = nullptr;
}
if (char_db) { // destroy charakter engine
char_db->destroy(char_db);
char_db = nullptr;
}
if (skill_db) { // destroy skill engine
skill_db->destroy(skill_db);
skill_db = nullptr;
}
login_accounts = nullptr; // destroyed in account_engine
bot_accounts = nullptr; // destroyed in bot account_engine
charakters = nullptr;// destroyed in charakter engine
skills = nullptr; //destroy in skills engine
if (brokk_fd != -1)
{
do_close(brokk_fd);
brokk_fd = -1;
}
ShowStatus("Finished.\n");
}
@ -773,226 +37,7 @@ void BrokkServer::handle_shutdown() {
}
bool BrokkServer::initialize(int argc, char* argv[]) {
// Init default value
safestrncpy(console_log_filepath, "./log/brokk-msg_log.log", sizeof(console_log_filepath));
// initialize engine
login_accounts = account_db_sql();
struct mmo_account acc;
bot_accounts = bot_account_db_sql();
charakters = char_db_sql();
skills = skill_db_sql();
// Initialize Yaml-DBs
bot_read_yaml_dbs();
// read brokk-server configuration
brokk_set_defaults();
brokk_config_read(brokk_config.brokkconf_name, true);
if (!brokk_initialize_dbs()) {
//Message will be triggered inside init
return false;
}
/**/
//debug skills von gm acc aufzählen
SkillDBIterator* iter_skills = skills->iterator(skills, 150000);
struct bot_skill skill;
while (iter_skills->next(iter_skills, 150000, &skill)) {
// Check based on config
ShowDebug("Skill %s (LV: %d) found in gm acc.\n", convert_skill_id_to_str(skill.skill_id), skill.level);
}
// Destroy the iterator after use
iter_skills->destroy(iter_skills);
// initialize logging
//if (brokk_config.log_brokk)
// brokklog_init();
// server port open & binding
if ((brokk_fd = make_listen_bind(brokk_config.brokk_ip, brokk_config.brokk_port)) == -1) {
ShowFatalError("Failed to bind to port '" CL_WHITE "%d" CL_RESET "'\n", brokk_config.brokk_port);
return false;
}
// Trigger bot account creation by reading config for bot creation (conf_bot_acc.json)
// Open the JSON file
std::ifstream bot_accounts_file("conf/conf_bot_acc.json");
if (!bot_accounts_file.is_open()) {
ShowError("Failed to open conf_bot_acc.json\n");
return false;
}
// Parse the JSON file
json json_bot_accounts;
try {
bot_accounts_file >> json_bot_accounts;
}
catch (json::parse_error& e) {
ShowError("Error parsing bot_accounts.json: %s\n", e.what());
return false;
}
// Close file after done with it
bot_accounts_file.close();
ShowStatus("Starting to create bot accounts...\n");
// Iterate over each bot entry in the JSON file
for (const auto& bot : json_bot_accounts) {
const std::string userid_str = bot["userid"];
const char* userid = userid_str.c_str();
const std::string pass = bot["pass"];
const char sex = bot["sex"].get<std::string>()[0]; // Extract single char from string
const std::string last_ip = bot["last_ip"];
// Debug: Print the entire bot entry
//ShowInfo("Processing bot: %s\n", bot.dump(4).c_str());
// Debug: Iterate over the keys in the bot object and print them
//for (auto it = bot.begin(); it != bot.end(); ++it) {
// ShowInfo("Key: %s\n", it.key().c_str());
//}
// DEBUG: Show bot details
//ShowStatus("Processing bot: userid=%s, pass=%s, sex=%c\n", userid, pass.c_str(), sex);
// Call brokk_mmo_auth_new to handle account creation
if (!brokk_mmo_auth_new(userid, pass.c_str(), sex, last_ip.c_str(), true)) {
ShowError("Failed to create bot account (userid: %s)\n", userid);
continue; // Skip to the next bot account
}
// Iterate over each character for this bot account
// Retrieve the account ID for the newly created account (assuming your function updates a global or accessible database)
// Check if the bot account already exists
if (!login_accounts->load_str(login_accounts, &acc, userid)) {
ShowNotice("Error in loading Account_id. Please check the following account data (account: %s, sex: %c)\n", userid, sex);
return 1; // Account exists
}
uint32 account_id = acc.account_id;
// Iterate over each character for this bot account
if (bot.contains("characters")) {
const auto& characters = bot["characters"];
//TODO ähnlichen Test für skills einbauen
for (const auto& character : characters) {
const std::string char_name = character["name"];
const int slot = character["slot"];
const int base_level = character["base_level"];
const int job_level = character["job_level"];
const int hair_style = character["hair_style"];
const int hair_color = character["hair_color"];
//TODO: Clothing/Style Randomizer hier einbauen
//Decode Job Value (is given as the ID not the number behind the ID for readability)
const std::string job_id = character["start_job"];
const int start_job = convert_jobid_to_number(job_id.c_str());
if (start_job == -1) {
const int start_job = JOB_NOVICE;
}
std::shared_ptr<bot_job_info> job = bot_job_db.find(start_job);
if (base_level > bot_job_db.get_maxBaseLv(start_job) or job_level > bot_job_db.get_maxJobLv(start_job))
{
//ShowNotice("Error in bot data. Inplausible baselevel (max=%s) or joblevel (max=%s) (account: %s, charname: $s, baselevel: %d, joblevel: %d)\n", bot_job_db.get_maxBaseLv(start_job), bot_job_db.get_maxJobLv(start_job), userid, char_name, base_level, job_level);
ShowNotice("Unplausible Base-level/Job-level");
continue; // Inplausible data
}
// Use job_id as reference for stat_builds
const std::string build_profile = character["char_build_profile"];
// Prep with default
int str = 1;
int agi = 1;
int vit = 1;
int int_ = 1;
int dex = 1;
int luk = 1;
if (build_profile == "CUSTOM") {
// When a CUSTOM build is entered, stats key must be present
if (!character.contains("stats")) {
// character params in json are malformed. Stats for CUSTOM build are missing
ShowNotice("Error: Could not create character %s . Stats are missing.", char_name);
continue;
}
const auto& char_stats = character["stats"];
str = char_stats["str"];
agi = char_stats["agi"];
vit = char_stats["vit"];
int_ = char_stats["int"];
dex = char_stats["dex"];
luk = char_stats["luk"];
}
else {
// Open the JSON file
std::string job_profile_file_path = "conf/bot_profiles/";
job_profile_file_path += job_id.c_str();
job_profile_file_path += "_profiles.json";
std::ifstream stat_profile_file(job_profile_file_path);
if (!stat_profile_file.is_open()) {
ShowError("Failed to open %s\n", job_profile_file_path.c_str());
return false;
}
// Parse the JSON file
json json_stat_profiles;
try {
stat_profile_file >> json_stat_profiles;
}
catch (json::parse_error& e) {
ShowError("Error parsing %s for job: %s\n", job_profile_file_path.c_str(), job_id.c_str());
return false;
}
// Close file after done with it
stat_profile_file.close();
// Read profile data and convert to stats
if (!json_stat_profiles.contains(build_profile)) {
ShowError("Error build profile %s does not exists. Skipping char...\n", build_profile.c_str());
return false;
}
const auto& stats = json_stat_profiles[build_profile];
str = stats["str"];
agi = stats["agi"];
vit = stats["vit"];
int_ = stats["int"];
dex = stats["dex"];
luk = stats["luk"];
}
// DEBUG: Show character details
//ShowStatus("Creating character: name=%s, slot=%d, start_job=%d\n", char_name.c_str(), slot, start_job);
// Create the character for this account
if (!brokk_create_character(account_id, char_name.c_str(), slot, base_level, job_level, str, agi, vit, int_, dex, luk, hair_style, hair_color, start_job, sex, true))
{
ShowError("Failed to create charakter (char_name: %s)\n", char_name);
continue; // Skip to the next charakter
}
}
}
else {
ShowError("No 'characters' array found for bot (userid: %s)\n", userid);
}
}
// Other initialization logic
setup_periodic_cleanup_timer(); // Set up the cleanup timer
ShowStatus("The brokk-server is " CL_GREEN "ready" CL_RESET " (Server is listening on the port %u).\n\n", brokk_config.brokk_port);
//brokk_log(0, "brokk server", 100, "brokk server started");
return true;
}

@ -30,41 +30,4 @@ namespace rathena {
};
}
}
struct Brokk_Config {
uint32 brokk_ip; /// the address to bind to
uint16 brokk_port; /// the port to bind to
bool log_brokk; /// whether to log brokk server actions or not
char date_format[32]; /// date format used in messages
bool console; /// console input system enabled?
char brokkconf_name[256]; /// name of main config file
char msgconf_name[256]; /// name of msg_conf config file
bool destroy_all_on_shutdown; /// cleanup all bots on shutdown?
uint32 allowed_bot_creations; /// number of allowed bot creations in quick succession
uint8 acc_name_min_length; /// minimum account name length
uint8 password_min_length; /// minimum password length
int time_allowed; /// registration interval in seconds
bool use_web_auth_token; /// Enable web authentication token system
int disable_webtoken_delay; /// delay disabling web token after char logs off in milliseconds
#ifdef VIP_ENABLE
#endif
};
extern struct Brokk_Config brokk_config;
// Bot Account creation
int brokk_mmo_auth_new(const char* userid, const char* pass, const char sex, const char* last_ip, const bool auto_generated, int creator_id, const char* creator_name);
int brokk_mmo_auth_delete_str(const char* userid);
int brokk_mmo_auth_delete_num(uint32 account_id);
bool brokk_config_read(const char* cfgName, bool normal);
void brokk_set_defaults();
void setup_periodic_cleanup_timer();
void bot_account_cleanup();
bool brokk_initialize_dbs();
#endif /* BROKK_HPP */

File diff suppressed because it is too large Load Diff

@ -1,369 +0,0 @@
// 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 CHAR_CREATION_HELPERS_HPP
#define CHAR_CREATION_HELPERS_HPP
#include <string>
#include <cstdint>
#include <common/cbasetypes.hpp>
#include <common/database.hpp>
#include <common/mmo.hpp> // JOB_*, MAX_FAME_LIST, struct fame_list, struct mmo_charstatus
#include "charakters.hpp"
/*
* Constants for yaml dbs
*/
#define MAX_LEVEL 275
/// Enum of Player's Parameter
enum bot_params {
BOT_PARAM_STR = 0,
BOT_PARAM_AGI,
BOT_PARAM_VIT,
BOT_PARAM_INT,
BOT_PARAM_DEX,
BOT_PARAM_LUK,
BOT_PARAM_POW,
BOT_PARAM_STA,
BOT_PARAM_WIS,
BOT_PARAM_SPL,
BOT_PARAM_CON,
BOT_PARAM_CRT,
BOT_PARAM_MAX
};
static const char* bot_parameter_names[BOT_PARAM_MAX] = {
"Str",
"Agi",
"Vit",
"Int",
"Dex",
"Luk",
"Pow",
"Sta",
"Wis",
"Spl",
"Con",
"Crt"
};
//Total number of classes (for data storage)
#define CLASS_COUNT (JOB_MAX - JOB_NOVICE_HIGH + JOB_MAX_BASIC)
enum bot_weapon_type : uint8 {
BOT_W_FIST, //Bare hands
BOT_W_DAGGER, //1
BOT_W_1HSWORD, //2
BOT_W_2HSWORD, //3
BOT_W_1HSPEAR, //4
BOT_W_2HSPEAR, //5
BOT_W_1HAXE, //6
BOT_W_2HAXE, //7
BOT_W_MACE, //8
BOT_W_2HMACE, //9 (unused)
BOT_W_STAFF, //10
BOT_W_BOW, //11
BOT_W_KNUCKLE, //12
BOT_W_MUSICAL, //13
BOT_W_WHIP, //14
BOT_W_BOOK, //15
BOT_W_KATAR, //16
BOT_W_REVOLVER, //17
BOT_W_RIFLE, //18
BOT_W_GATLING, //19
BOT_W_SHOTGUN, //20
BOT_W_GRENADE, //21
BOT_W_HUUMA, //22
BOT_W_2HSTAFF, //23
BOT_MAX_WEAPON_TYPE,
// dual-wield constants
BOT_W_DOUBLE_DD, // 2 daggers
BOT_W_DOUBLE_SS, // 2 swords
BOT_W_DOUBLE_AA, // 2 axes
BOT_W_DOUBLE_DS, // dagger + sword
BOT_W_DOUBLE_DA, // dagger + axe
BOT_W_DOUBLE_SA, // sword + axe
BOT_MAX_WEAPON_TYPE_ALL,
BOT_W_SHIELD = BOT_MAX_WEAPON_TYPE,
};
#define WEAPON_TYPE_ALL ((1<<MAX_WEAPON_TYPE)-1)
//Checks if the given class value corresponds to a player class. [Skotlex]
//JOB_NOVICE isn't checked for class_ is supposed to be unsigned
#define pcdb_checkid_sub(class_) ( \
( (class_) < JOB_MAX_BASIC ) || \
( (class_) >= JOB_NOVICE_HIGH && (class_) <= JOB_DARK_COLLECTOR ) || \
( (class_) >= JOB_RUNE_KNIGHT && (class_) <= JOB_MECHANIC_T2 ) || \
( (class_) >= JOB_BABY_RUNE_KNIGHT && (class_) <= JOB_BABY_MECHANIC2 ) || \
( (class_) >= JOB_SUPER_NOVICE_E && (class_) <= JOB_SUPER_BABY_E ) || \
( (class_) >= JOB_KAGEROU && (class_) <= JOB_OBORO ) || \
(class_) == JOB_REBELLION || (class_) == JOB_SUMMONER || \
(class_) == JOB_BABY_SUMMONER || \
( (class_) >= JOB_BABY_NINJA && (class_) <= JOB_BABY_REBELLION ) || \
( (class_) >= JOB_BABY_STAR_GLADIATOR2 && (class_) <= JOB_BABY_STAR_EMPEROR2 ) || \
( (class_) >= JOB_DRAGON_KNIGHT && (class_) <= JOB_TROUVERE ) || \
( (class_) >= JOB_WINDHAWK2 && (class_) <= JOB_IMPERIAL_GUARD2 ) || \
( (class_) >= JOB_SKY_EMPEROR && (class_) <= JOB_SPIRIT_HANDLER ) || \
(class_) == JOB_SKY_EMPEROR2 \
)
#define pcdb_checkid(class_) pcdb_checkid_sub((unsigned int)class_)
//These marks the "level" of the job.
#define JOBL_2_1 0x100 //256
#define JOBL_2_2 0x200 //512
#define JOBL_2 0x300 //768
#define JOBL_UPPER 0x1000 //4096
#define JOBL_BABY 0x2000 //8192
#define JOBL_THIRD 0x4000 //16384
#define JOBL_FOURTH 0x8000 //32768
//First Jobs
//Note the oddity of the novice:
enum bot_mapid : uint64{
//Novice And 1-1 Jobs
BOT_MAPID_NOVICE = 0x0,
BOT_MAPID_SWORDMAN,
BOT_MAPID_MAGE,
BOT_MAPID_ARCHER,
BOT_MAPID_ACOLYTE,
BOT_MAPID_MERCHANT,
BOT_MAPID_THIEF,
BOT_MAPID_TAEKWON,
BOT_MAPID_WEDDING,
BOT_MAPID_GUNSLINGER,
BOT_MAPID_NINJA,
BOT_MAPID_XMAS,
BOT_MAPID_SUMMER,
BOT_MAPID_HANBOK,
BOT_MAPID_GANGSI,
BOT_MAPID_OKTOBERFEST,
BOT_MAPID_SUMMONER,
BOT_MAPID_SUMMER2,
//2-1 Jobs
BOT_MAPID_SUPER_NOVICE = JOBL_2_1|BOT_MAPID_NOVICE,
BOT_MAPID_KNIGHT,
BOT_MAPID_WIZARD,
BOT_MAPID_HUNTER,
BOT_MAPID_PRIEST,
BOT_MAPID_BLACKSMITH,
BOT_MAPID_ASSASSIN,
BOT_MAPID_STAR_GLADIATOR,
BOT_MAPID_REBELLION = JOBL_2_1| BOT_MAPID_GUNSLINGER,
BOT_MAPID_KAGEROUOBORO,
BOT_MAPID_DEATH_KNIGHT = JOBL_2_1|BOT_MAPID_GANGSI,
//2-2 Jobs
BOT_MAPID_CRUSADER = JOBL_2_2|BOT_MAPID_SWORDMAN,
BOT_MAPID_SAGE,
BOT_MAPID_BARDDANCER,
BOT_MAPID_MONK,
BOT_MAPID_ALCHEMIST,
BOT_MAPID_ROGUE,
BOT_MAPID_SOUL_LINKER,
BOT_MAPID_DARK_COLLECTOR = JOBL_2_2|BOT_MAPID_GANGSI,
//Trans Novice And Trans 1-1 Jobs
BOT_MAPID_NOVICE_HIGH = JOBL_UPPER|BOT_MAPID_NOVICE,
BOT_MAPID_SWORDMAN_HIGH,
BOT_MAPID_MAGE_HIGH,
BOT_MAPID_ARCHER_HIGH,
BOT_MAPID_ACOLYTE_HIGH,
BOT_MAPID_MERCHANT_HIGH,
BOT_MAPID_THIEF_HIGH,
//Trans 2-1 Jobs
BOT_MAPID_LORD_KNIGHT = JOBL_UPPER|BOT_MAPID_KNIGHT,
BOT_MAPID_HIGH_WIZARD,
BOT_MAPID_SNIPER,
BOT_MAPID_HIGH_PRIEST,
BOT_MAPID_WHITESMITH,
BOT_MAPID_ASSASSIN_CROSS,
//Trans 2-2 Jobs
BOT_MAPID_PALADIN = JOBL_UPPER|BOT_MAPID_CRUSADER,
BOT_MAPID_PROFESSOR,
BOT_MAPID_CLOWNGYPSY,
BOT_MAPID_CHAMPION,
BOT_MAPID_CREATOR,
BOT_MAPID_STALKER,
//Baby Novice And Baby 1-1 Jobs
BOT_MAPID_BABY = JOBL_BABY|BOT_MAPID_NOVICE,
BOT_MAPID_BABY_SWORDMAN,
BOT_MAPID_BABY_MAGE,
BOT_MAPID_BABY_ARCHER,
BOT_MAPID_BABY_ACOLYTE,
BOT_MAPID_BABY_MERCHANT,
BOT_MAPID_BABY_THIEF,
BOT_MAPID_BABY_TAEKWON,
BOT_MAPID_BABY_GUNSLINGER = JOBL_BABY|BOT_MAPID_GUNSLINGER,
BOT_MAPID_BABY_NINJA,
BOT_MAPID_BABY_SUMMONER = JOBL_BABY|BOT_MAPID_SUMMONER,
//Baby 2-1 Jobs
BOT_MAPID_SUPER_BABY = JOBL_BABY|BOT_MAPID_SUPER_NOVICE,
BOT_MAPID_BABY_KNIGHT,
BOT_MAPID_BABY_WIZARD,
BOT_MAPID_BABY_HUNTER,
BOT_MAPID_BABY_PRIEST,
BOT_MAPID_BABY_BLACKSMITH,
BOT_MAPID_BABY_ASSASSIN,
BOT_MAPID_BABY_STAR_GLADIATOR,
BOT_MAPID_BABY_REBELLION = JOBL_BABY|BOT_MAPID_REBELLION,
BOT_MAPID_BABY_KAGEROUOBORO,
//Baby 2-2 Jobs
BOT_MAPID_BABY_CRUSADER = JOBL_BABY|BOT_MAPID_CRUSADER,
BOT_MAPID_BABY_SAGE,
BOT_MAPID_BABY_BARDDANCER,
BOT_MAPID_BABY_MONK,
BOT_MAPID_BABY_ALCHEMIST,
BOT_MAPID_BABY_ROGUE,
BOT_MAPID_BABY_SOUL_LINKER,
//3-1 Jobs
BOT_MAPID_SUPER_NOVICE_E = JOBL_THIRD|BOT_MAPID_SUPER_NOVICE,
BOT_MAPID_RUNE_KNIGHT,
BOT_MAPID_WARLOCK,
BOT_MAPID_RANGER,
BOT_MAPID_ARCH_BISHOP,
BOT_MAPID_MECHANIC,
BOT_MAPID_GUILLOTINE_CROSS,
BOT_MAPID_STAR_EMPEROR,
//3-2 Jobs
BOT_MAPID_ROYAL_GUARD = JOBL_THIRD|BOT_MAPID_CRUSADER,
BOT_MAPID_SORCERER,
BOT_MAPID_MINSTRELWANDERER,
BOT_MAPID_SURA,
BOT_MAPID_GENETIC,
BOT_MAPID_SHADOW_CHASER,
BOT_MAPID_SOUL_REAPER,
//Trans 3-1 Jobs
BOT_MAPID_RUNE_KNIGHT_T = JOBL_THIRD|BOT_MAPID_LORD_KNIGHT,
BOT_MAPID_WARLOCK_T,
BOT_MAPID_RANGER_T,
BOT_MAPID_ARCH_BISHOP_T,
BOT_MAPID_MECHANIC_T,
BOT_MAPID_GUILLOTINE_CROSS_T,
//Trans 3-2 Jobs
BOT_MAPID_ROYAL_GUARD_T = JOBL_THIRD|BOT_MAPID_PALADIN,
BOT_MAPID_SORCERER_T,
BOT_MAPID_MINSTRELWANDERER_T,
BOT_MAPID_SURA_T,
BOT_MAPID_GENETIC_T,
BOT_MAPID_SHADOW_CHASER_T,
//Baby 3-1 Jobs
BOT_MAPID_SUPER_BABY_E = JOBL_THIRD|BOT_MAPID_SUPER_BABY,
BOT_MAPID_BABY_RUNE_KNIGHT,
BOT_MAPID_BABY_WARLOCK,
BOT_MAPID_BABY_RANGER,
BOT_MAPID_BABY_ARCH_BISHOP,
BOT_MAPID_BABY_MECHANIC,
BOT_MAPID_BABY_GUILLOTINE_CROSS,
BOT_MAPID_BABY_STAR_EMPEROR,
//Baby 3-2 Jobs
BOT_MAPID_BABY_ROYAL_GUARD = JOBL_THIRD|BOT_MAPID_BABY_CRUSADER,
BOT_MAPID_BABY_SORCERER,
BOT_MAPID_BABY_MINSTRELWANDERER,
BOT_MAPID_BABY_SURA,
BOT_MAPID_BABY_GENETIC,
BOT_MAPID_BABY_SHADOW_CHASER,
BOT_MAPID_BABY_SOUL_REAPER,
//4-1 Jobs
BOT_MAPID_HYPER_NOVICE = JOBL_FOURTH|JOBL_THIRD|JOBL_UPPER|BOT_MAPID_SUPER_NOVICE,
BOT_MAPID_DRAGON_KNIGHT,
BOT_MAPID_ARCH_MAGE,
BOT_MAPID_WINDHAWK,
BOT_MAPID_CARDINAL,
BOT_MAPID_MEISTER,
BOT_MAPID_SHADOW_CROSS,
BOT_MAPID_SKY_EMPEROR,
BOT_MAPID_NIGHT_WATCH = JOBL_FOURTH|JOBL_THIRD|JOBL_UPPER|BOT_MAPID_REBELLION,
BOT_MAPID_SHINKIRO_SHIRANUI,
BOT_MAPID_SPIRIT_HANDLER = JOBL_FOURTH|JOBL_THIRD|JOBL_UPPER|JOBL_2_1|BOT_MAPID_SUMMONER,
//4-2 Jobs
BOT_MAPID_IMPERIAL_GUARD = JOBL_FOURTH|JOBL_THIRD|JOBL_UPPER|BOT_MAPID_CRUSADER,
BOT_MAPID_ELEMENTAL_MASTER,
BOT_MAPID_TROUBADOURTROUVERE,
BOT_MAPID_INQUISITOR,
BOT_MAPID_BIOLO,
BOT_MAPID_ABYSS_CHASER,
BOT_MAPID_SOUL_ASCETIC,
// Additional constants
BOT_MAPID_ALL = UINT64_MAX
};
//for filtering and quick checking.
#define MAPID_BASEMASK 0x00ff
#define MAPID_UPPERMASK 0x0fff
#define MAPID_THIRDMASK (JOBL_THIRD|MAPID_UPPERMASK)
#define MAPID_FOURTHMASK (JOBL_FOURTH|MAPID_THIRDMASK|JOBL_UPPER)
/*
* Yaml DBs and their containers
*/
struct bot_statpoint_entry {
uint16 level;
uint32 statpoints;
uint32 traitpoints;
};
class BotStatPointDatabase : public TypesafeCachedYamlDatabase<uint16, bot_statpoint_entry> {
public:
BotStatPointDatabase() : TypesafeCachedYamlDatabase("STATPOINT_DB", 2, 1) {
}
const std::string getDefaultLocation() override;
uint64 parseBodyNode(const ryml::NodeRef& node) override;
void loadingFinished() override;
// Additional
uint32 load_max_status_points_of_level(uint16 level);
uint32 get_table_point(uint16 level);
uint32 load_max_trait_points_of_level(uint16 level);
uint32 get_trait_table_point(uint16 level);
};
extern BotStatPointDatabase bot_statpoint_db;
struct bot_job_info {
std::vector<uint32> base_hp, base_sp, base_ap; //Storage for the first calculation with hp/sp/ap factor and multiplicator
uint32 hp_factor, hp_increase, sp_increase, max_weight_base;
std::vector<std::array<uint16, BOT_PARAM_MAX>> job_bonus;
std::vector<int16> aspd_base;
t_exp base_exp[MAX_LEVEL], job_exp[MAX_LEVEL];
uint16 max_base_level, max_job_level;
uint16 max_param[BOT_PARAM_MAX];
struct s_job_noenter_map {
uint32 zone;
uint8 group_lv;
} noenter_map;
};
class BotJobDatabase : public TypesafeCachedYamlDatabase<uint16, bot_job_info> {
public:
BotJobDatabase() : TypesafeCachedYamlDatabase("JOB_STATS", 2) {
}
const std::string getDefaultLocation() override;
uint64 parseBodyNode(const ryml::NodeRef& node) override;
void loadingFinished() override;
// Additional
uint32 get_maxBaseLv(uint16 job_id);
uint32 get_maxJobLv(uint16 job_id);
t_exp get_baseExp(uint16 job_id, uint32 level);
t_exp get_jobExp(uint16 job_id, uint32 level);
int32 get_maxWeight(uint16 job_id);
};
extern BotJobDatabase bot_job_db;
/*
* Tool Functions
*/
void bot_read_yaml_dbs(void);
#endif /* CHAR_CREATION_HELPERS_HPP */

File diff suppressed because it is too large Load Diff

@ -1,216 +0,0 @@
// 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 CHARAKTERS_HPP
#define CHARAKTERS_HPP
#include <common/cbasetypes.hpp>
#include <common/mmo.hpp> // ACCOUNT_REG2_NUM, WEB_AUTH_TOKEN_LENGTH
#include <config/core.hpp>
typedef struct CharDB CharDB;
typedef struct CharakterDBIterator CharakterDBIterator;
// standard engines
CharDB* char_db_sql(void);
struct mmo_charakter {
uint32 char_id; // Primary key
uint32 account_id; // ID of corresponding login account
uint8 char_num; // Slot number of the character
char name[NAME_LENGTH]; // Name of the character, limited to 30 chars
uint16 class_; // Class ID of the character
uint16 base_level; // Base level of the character
uint16 job_level; // Job level of the character
uint64 base_exp; // Base experience points
uint64 job_exp; // Job experience points
uint32 zeny; // Amount of zeny
uint16 str; // Strength stat
uint16 agi; // Agility stat
uint16 vit; // Vitality stat
uint16 int_; // Intelligence stat
uint16 dex; // Dexterity stat
uint16 luk; // Luck stat
// Additional stats
uint16 pow; // Power stat
uint16 sta; // Stamina stat
uint16 wis; // Wisdom stat
uint16 spl; // Spirit stat
uint16 con; // Concentration stat
uint16 crt; // Critical stat
uint32 max_hp; // Maximum HP
uint32 hp; // Current HP
uint32 max_sp; // Maximum SP
uint32 sp; // Current SP
uint32 max_ap; // Maximum AP (if applicable)
uint32 ap; // Current AP (if applicable)
uint32 status_point; // Available status points
uint32 skill_point; // Available skill points
uint32 trait_point; // Available trait points
int32 option; // Character options
int8 karma; // Karma points (signed, -128 to 127)
int16 manner; // Manner points (could be negative, small int)
uint32 party_id; // ID of the party the character is in
uint32 guild_id; // ID of the guild the character is in
uint32 pet_id; // ID of the pet (if any)
uint32 homun_id; // ID of the homunculus (if any)
uint32 elemental_id; // ID of the elemental (if any)
uint8 hair; // Hair style
uint16 hair_color; // Hair color
uint16 clothes_color; // Clothes color
uint16 body; // Body ID
uint16 weapon; // Weapon ID
uint16 shield; // Shield ID
uint16 head_top; // Head top item
uint16 head_mid; // Head middle item
uint16 head_bottom; // Head bottom item
uint16 robe; // Robe item
char last_map[MAP_NAME_LENGTH]; // Last known map the character was on
uint16 last_x; // Last X position on the map
uint16 last_y; // Last Y position on the map
uint32 last_instanceid; // ID of the last instance the character was in
char save_map[MAP_NAME_LENGTH]; // Save map (respawn location)
uint16 save_x; // Save X position
uint16 save_y; // Save Y position
uint32 partner_id; // ID of the partner (e.g., for marriage)
uint8 online; // Is the character online or not?
uint32 father; // Father character ID
uint32 mother; // Mother character ID
uint32 child; // Child character ID
uint32 fame; // Fame points
uint16 rename; // Rename status
uint32 delete_date; // Scheduled deletion time (timestamp)
uint32 moves; // Character moves (could represent migration)
uint32 unban_time; // Time until unbanned (timestamp)
int8 font; // Font used by the character
uint32 uniqueitem_counter; // Counter for unique items
char sex; // 'M' or 'F' for gender
int8 hotkey_rowshift; // Hotkey row shift setting
int8 hotkey_rowshift2; // Additional hotkey row shift setting
uint32 clan_id; // Clan ID
char last_login[24]; // date+time of last successful login
uint32 title_id; // Character title ID
uint8 show_equip; // Show equipment flag
uint16 inventory_slots; // Available inventory slots
uint8 body_direction; // Direction the body is facing
uint8 disable_call; // Disable call flag (e.g., for party/guild recall)
// Add other custom fields here if needed for future expansions
};
struct CharakterDBIterator {
/// Destroys this iterator, releasing all allocated memory (including itself).
///
/// @param self Iterator
void (*destroy)(CharakterDBIterator* self);
/// Fetches the next charakter in the database.
/// Fills charakter with the charakter data.
/// @param self Iterator
/// @param character data
/// @return true if successful
bool (*next)(CharakterDBIterator* self, struct mmo_charakter* charakter);
};
struct CharDB {
/// Initializes this database, making it ready for use.
/// Call this after setting the properties.
///
/// @param self Database
/// @return true if successful
bool (*init)(CharDB* self);
/// Destroys this database, releasing all allocated memory (including itself).
///
/// @param self Database
void (*destroy)(CharDB* 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)(CharDB* 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)(CharDB* self, const char* key, const char* value);
/// Creates a new charakter in this database.
///
/// @param self Database
/// @param charakter Charakter data
/// @return true if successful
bool (*create)(CharDB* self, struct mmo_charakter* charakter);
/// Removes an charakter from this database.
///
/// @param self Database
/// @param char_id Charakter id
/// @return true if successful
bool (*remove)(CharDB* self, const uint32 char_id);
/// Modifies the data of an existing charakter.
/// Uses charakter->char_id to identify the charakter.
///
/// @param self Database
/// @param charakter Char data
/// @return true if successful
bool (*save)(CharDB* self, const struct mmo_charakter* charakter);
/// Finds an charakter with char_id and copies it to charakter.
///
/// @param self Database
/// @param charakter Pointer that receives the char data
/// @param char_id Target charakter id
/// @return true if successful
bool (*load_num)(CharDB* self, struct mmo_charakter* charakter, const uint32 char_id);
/// Finds an charakter with charakter_name and copies it to charakter.
///
/// @param self Database
/// @param charakter Pointer that receives the charakter data
/// @param char_name Target Charakter Name
/// @return true if successful
bool (*load_str)(CharDB* self, struct mmo_charakter* charakter, const char* id);
/// Returns a new forward iterator.
///
/// @param self Database
/// @return Iterator
CharakterDBIterator* (*iterator)(CharDB* self);
};
//void mmo_send_global_accreg(BotAccountDB* self, int fd, uint32 account_id, uint32 char_id);
//void mmo_save_global_accreg(BotAccountDB* self, int fd, uint32 account_id, uint32 char_id);
int convert_jobid_to_number(const char* job_id);
int convert_weaponid_to_number(const char* weapon_id);
#endif /* CHARAKTERS_HPP */

@ -1,825 +0,0 @@
// 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 "bot_account.hpp"
#include <algorithm> //min / max
#include <cstdlib>
#include <cstring>
#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 "brokk.hpp" // brokk_config
/// global defines
/// internal structure
typedef struct BotAccountDB_SQL {
BotAccountDB vtable; // public interface
Sql* bot_accounts; // SQL handle bot accounts 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 bot_account_db[32];
char login_account_db[32];
char global_acc_reg_num_table[32];
char global_acc_reg_str_table[32];
} BotAccountDB_SQL;
/// internal structure
typedef struct BotAccountDBIterator_SQL {
BotAccountDBIterator vtable; // public interface
BotAccountDB_SQL* db;
int last_bot_account_id;
} BotAccountDBIterator_SQL;
/// internal functions
static bool bot_account_db_sql_init(BotAccountDB* self);
static void bot_account_db_sql_destroy(BotAccountDB* self);
static bool bot_account_db_sql_get_property(BotAccountDB* self, const char* key, char* buf, size_t buflen);
static bool bot_account_db_sql_set_property(BotAccountDB* self, const char* option, const char* value);
static bool bot_account_db_sql_create(BotAccountDB* self, struct mmo_bot_account* bot_acc);
static bool bot_account_db_sql_remove(BotAccountDB* self, const uint32 account_id);
static bool bot_account_db_sql_save(BotAccountDB* self, const struct mmo_bot_account* bot_acc, bool refresh_token);
static bool bot_account_db_sql_load_num(BotAccountDB* self, struct mmo_bot_account* bot_acc, const uint32 account_id);
static bool bot_account_db_sql_load_str(BotAccountDB* self, struct mmo_bot_account* bot_acc, const char* creator_name);
static BotAccountDBIterator* bot_account_db_sql_iterator(BotAccountDB* self);
static void bot_account_db_sql_iter_destroy(BotAccountDBIterator* self);
static bool bot_account_db_sql_iter_next(BotAccountDBIterator* self, struct mmo_bot_account* bot_acc);
static bool mmo_bot_auth_fromsql(BotAccountDB_SQL* db, struct mmo_bot_account* bot_acc, uint32 account_id);
static bool mmo_bot_auth_tosql(BotAccountDB_SQL* db, const struct mmo_bot_account* bot_acc, bool is_new, bool refresh_token);
/// public constructor
BotAccountDB* bot_account_db_sql(void) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)aCalloc(1, sizeof(BotAccountDB_SQL));
new(db) BotAccountDB_SQL();
// set up the vtable
db->vtable.init = &bot_account_db_sql_init;
db->vtable.destroy = &bot_account_db_sql_destroy;
db->vtable.get_property = &bot_account_db_sql_get_property;
db->vtable.set_property = &bot_account_db_sql_set_property;
db->vtable.save = &bot_account_db_sql_save;
db->vtable.create = &bot_account_db_sql_create;
db->vtable.remove = &bot_account_db_sql_remove;
db->vtable.load_num = &bot_account_db_sql_load_num;
db->vtable.load_str = &bot_account_db_sql_load_str;
db->vtable.iterator = &bot_account_db_sql_iterator;
// initialize to default values
db->bot_accounts = nullptr;
// other settings
db->case_sensitive = false;
safestrncpy(db->bot_account_db, "bot_accounts", sizeof(db->bot_account_db));
safestrncpy(db->login_account_db, "login", sizeof(db->login_account_db));
//safestrncpy(db->global_acc_reg_num_table, "global_acc_reg_num", sizeof(db->global_acc_reg_num_table));
//safestrncpy(db->global_acc_reg_str_table, "global_acc_reg_str", sizeof(db->global_acc_reg_str_table));
return &db->vtable;
}
/* ------------------------------------------------------------------------- */
/**
* Establish the database connection.
* @param self: pointer to db
*/
static bool bot_account_db_sql_init(BotAccountDB* self) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
Sql* sql_handle;
db->bot_accounts = Sql_Malloc();
sql_handle = db->bot_accounts;
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_accounts);
db->bot_accounts = nullptr;
return false;
}
if (!db->codepage.empty() && SQL_ERROR == Sql_SetEncoding(sql_handle, db->codepage.c_str()))
Sql_ShowDebug(sql_handle);
//self->remove_webtokens( self );
return true;
}
/**
* Destroy the database and close the connection to it.
* @param self: pointer to db
*/
static void bot_account_db_sql_destroy(BotAccountDB* self){
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
//if( SQL_ERROR == Sql_Query( db->bot_accounts, "UPDATE `%s` SET `web_auth_token` = NULL", db->bot_account_db ) ){
// Sql_ShowDebug( db->accounts );
//}
Sql_Free(db->bot_accounts);
db->bot_accounts = nullptr;
db->~BotAccountDB_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_account_db_sql_get_property(BotAccountDB* self, const char* key, char* buf, size_t buflen)
{
BotAccountDB_SQL* db = (BotAccountDB_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, "bot_account_db") == 0 )
safesnprintf(buf, buflen, "%s", db->bot_account_db);
//else
//if( strcmpi(key, "global_acc_reg_str_table") == 0 )
// safesnprintf(buf, buflen, "%s", db->global_acc_reg_str_table);
//else
//if( strcmpi(key, "global_acc_reg_num_table") == 0 )
// safesnprintf(buf, buflen, "%s", db->global_acc_reg_num_table);
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_account_db_sql_set_property(BotAccountDB* self, const char* key, const char* value) {
BotAccountDB_SQL* db = (BotAccountDB_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, "bot_account_db") == 0 )
safestrncpy(db->bot_account_db, value, sizeof(db->bot_account_db));
else
if (strcmpi(key, "login_account_db") == 0)
safestrncpy(db->login_account_db, value, sizeof(db->login_account_db));
//else
//if( strcmpi(key, "global_acc_reg_str_table") == 0 )
// safestrncpy(db->global_acc_reg_str_table, value, sizeof(db->global_acc_reg_str_table));
//else
//if( strcmpi(key, "global_acc_reg_num_table") == 0 )
// safestrncpy(db->global_acc_reg_num_table, value, sizeof(db->global_acc_reg_num_table));
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 account entry.
* acc->account_id = -1 means auto_decidet by server
* bot_acc->account_id is always matching its login table counter part,
* and its value will be written to bot_acc->account_id if everything succeeds.
* @param self: pointer to db
* @param acc: pointer of mmo_bot_account to save
* @return true if successful, false if something has failed
*/
static bool bot_account_db_sql_create(BotAccountDB* self, struct mmo_bot_account* bot_acc) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
Sql* sql_handle_bot = db->bot_accounts;
// validate account id to assign
uint32 account_id;
account_id = bot_acc->account_id;
// zero value is prohibited
if( account_id == 0 )
return false;
// absolute maximum
if( account_id > END_ACCOUNT_NUM )
return false;
// insert the data into the database
bot_acc->account_id = account_id;
return mmo_bot_auth_tosql(db, bot_acc, true, false);
}
/**
* Delete an existing bot account entry and its regs.
* @param self: pointer to db
* @param account_id: id of user account
* @return true if successful, false if something has failed
*/
static bool bot_account_db_sql_remove(BotAccountDB* self, const uint32 account_id) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
Sql* sql_handle = db->bot_accounts;
bool result = false;
if( SQL_SUCCESS != Sql_QueryStr(sql_handle, "START TRANSACTION")
|| SQL_SUCCESS != Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = %d", db->bot_account_db, account_id)
)
Sql_ShowDebug(sql_handle);
else
result = true;
result &= ( SQL_SUCCESS == Sql_QueryStr(sql_handle, (result == true) ? "COMMIT" : "ROLLBACK") );
return result;
}
/**
* Update an existing bot_account with the new data provided.
* @param self: pointer to db
* @param bot_acc: pointer of mmo_bot_account to save
* @return true if successful, false if something has failed
*/
static bool bot_account_db_sql_save(BotAccountDB* self, const struct mmo_bot_account* bot_acc, bool refresh_token) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
return mmo_bot_auth_tosql(db, bot_acc, false, refresh_token);
}
/**
* Retrieve data from db and store it in the provided data structure.
* Filled data structure is done by delegation to mmo_bot_auth_fromsql.
* @param self: pointer to db
* @param bot_acc: pointer of mmo_bot_account to fill
* @param account_id: id of user account
* @return true if successful, false if something has failed
*/
static bool bot_account_db_sql_load_num(BotAccountDB* self, struct mmo_bot_account* bot_acc, const uint32 account_id) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
return mmo_bot_auth_fromsql(db, bot_acc, account_id);
}
/**
* Retrieve data from db and store it in the provided data structure.
* Doesn't actually retrieve data yet: escapes and checks userid, then transforms it to accid for fetching.
* Filled data structure is done by delegation to bot_account_db_sql_load_num.
* This function basicly fetches account_ids of all bots of an account
* @param self: pointer to db
* @param bot_acc: pointer of mmo_bot_account to fill
* @param userid: name of user account
* @return true if successful, false if something has failed
*/
static bool bot_account_db_sql_load_str(BotAccountDB* self, struct mmo_bot_account* bot_acc, const char* userid) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
Sql* sql_handle = db->bot_accounts;
char esc_userid[2*NAME_LENGTH+1];
uint32 account_id;
char* data;
Sql_EscapeString(sql_handle, esc_userid, userid);
// get the list of account IDs for this userid
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `userid`= %s '%s'",
db->login_account_db, (db->case_sensitive ? "BINARY" : ""), esc_userid) )
{
Sql_ShowDebug(sql_handle);
return false;
}
if( Sql_NumRows(sql_handle) > 1 )
{// serious problem - duplicit account
ShowError("bot_account_db_sql_load_str: multiple accounts found when retrieving data for account '%s'!\n", userid);
Sql_FreeResult(sql_handle);
return false;
}
if( SQL_SUCCESS != Sql_NextRow(sql_handle) )
{// no such entry
Sql_FreeResult(sql_handle);
return false;
}
//TODO FIXME this will only get the first found bot to a creator (Currently not a problem, because only one bot per account)
Sql_GetData(sql_handle, 0, &data, nullptr);
account_id = atoi(data);
return bot_account_db_sql_load_num(self, bot_acc, account_id);
}
/**
* Create a new forward iterator.
* @param self: pointer to db iterator
* @return a new db iterator
*/
static BotAccountDBIterator* bot_account_db_sql_iterator(BotAccountDB* self) {
BotAccountDB_SQL* db = (BotAccountDB_SQL*)self;
BotAccountDBIterator_SQL* iter = (BotAccountDBIterator_SQL*)aCalloc(1, sizeof(BotAccountDBIterator_SQL));
// set up the vtable
iter->vtable.destroy = &bot_account_db_sql_iter_destroy;
iter->vtable.next = &bot_account_db_sql_iter_next;
// fill data
iter->db = db;
iter->last_bot_account_id = -1;
return &iter->vtable;
}
/**
* Destroys this iterator, releasing all allocated memory (including itself).
* @param self: pointer to db iterator
*/
static void bot_account_db_sql_iter_destroy(BotAccountDBIterator* self) {
BotAccountDBIterator_SQL* iter = (BotAccountDBIterator_SQL*)self;
aFree(iter);
}
/**
* Fetches the next account in the database.
* @param self: pointer to db iterator
* @param bot_acc: pointer of mmo_bot_account to fill
* @return true if next account found and filled, false if something has failed
*/
static bool bot_account_db_sql_iter_next(BotAccountDBIterator* self, struct mmo_bot_account* bot_acc) {
BotAccountDBIterator_SQL* iter = (BotAccountDBIterator_SQL*)self;
BotAccountDB_SQL* db = iter->db;
Sql* sql_handle = db->bot_accounts;
char* data;
// get next account ID
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `account_id` > '%d' ORDER BY `account_id` ASC LIMIT 1",
db->bot_account_db, iter->last_bot_account_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 account data
uint32 account_id;
account_id = atoi(data);
if( mmo_bot_auth_fromsql(db, bot_acc, account_id) )
{
iter->last_bot_account_id = account_id;
Sql_FreeResult(sql_handle);
return true;
}
}
Sql_FreeResult(sql_handle);
return false;
}
/**
* Fetch a struct mmo_bot_account from sql.
* @param db: pointer to db
* @param acc: pointer of mmo_bot_account to fill
* @param account_id: id of user account to take data from
* @return true if successful, false if something has failed
*/
static bool mmo_bot_auth_fromsql(BotAccountDB_SQL* db, struct mmo_bot_account* bot_acc, uint32 account_id) {
Sql* sql_handle = db->bot_accounts;
char* data;
// retrieve login entry for the specified account
if( SQL_ERROR == Sql_Query(sql_handle,
#ifdef VIP_ENABLE
"SELECT `account_id`,`is_bot`,`creator_id`,`creator_name` FROM `%s` WHERE `account_id` = %d",
#else
"SELECT `account_id`,`is_bot`,`creator_id`,`creator_name` FROM `%s` WHERE `account_id` = %d",
#endif
db->bot_account_db, account_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); bot_acc->account_id = atoi(data);
Sql_GetData(sql_handle, 1, &data, nullptr); bot_acc->is_bot = (uint8)atoi(data);
Sql_GetData(sql_handle, 2, &data, nullptr); bot_acc->creator_id = atoi(data);
Sql_GetData(sql_handle, 3, &data, nullptr); safestrncpy(bot_acc->creator_name, data, sizeof(bot_acc->creator_name));
//ifdef VIP_ENABLE
//endif
Sql_FreeResult(sql_handle);
return true;
}
/**
* Save a struct mmo_bot_account in sql.
* @param db: pointer to db
* @param bot_acc: pointer of mmo_account to save
* @param is_new: if it's a new entry or should we update
* @return true if successful, false if something has failed
*/
static bool mmo_bot_auth_tosql(BotAccountDB_SQL* db, const struct mmo_bot_account* bot_acc, bool is_new, bool refresh_token) {
Sql* sql_handle = db->bot_accounts;
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` (`account_id`, `is_bot`, `creator_id`, `creator_name`) VALUES (?, ?, ?, ?)",
#else
"INSERT INTO `%s` (`account_id`, `is_bot`, `creator_id`, `creator_name`) VALUES (?, ?, ?, ?)",
#endif
db->bot_account_db)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, (void*)&bot_acc->account_id, sizeof(bot_acc->account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_SHORT, (void*)&bot_acc->is_bot, sizeof(bot_acc->is_bot))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_INT, (void*)&bot_acc->creator_id, sizeof(bot_acc->creator_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_STRING, (void*)&bot_acc->creator_name, strlen(bot_acc->creator_name))
//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)
if( SQL_SUCCESS != SqlStmt_Prepare(stmt,
#ifdef VIP_ENABLE
"UPDATE `%s` SET `is_bot`=? WHERE `account_id` = '%d'",
#else
"UPDATE `%s` SET `is_bot`=? WHERE `account_id` = '%d'",
#endif
db->bot_account_db, bot_acc->account_id)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void*)bot_acc->is_bot, sizeof(bot_acc->is_bot))
#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;
}
/*
void mmo_save_global_accreg(AccountDB* self, int fd, uint32 account_id, uint32 char_id) {
Sql* sql_handle = ((AccountDB_SQL*)self)->accounts;
AccountDB_SQL* db = (AccountDB_SQL*)self;
uint16 count = RFIFOW(fd, 12);
if (count) {
int cursor = 14, i;
char key[32], sval[254], esc_key[32*2+1], esc_sval[254*2+1];
for (i = 0; i < count; i++) {
uint32 index;
safestrncpy(key, RFIFOCP(fd, cursor + 1), RFIFOB(fd, cursor));
Sql_EscapeString(sql_handle, esc_key, key);
cursor += RFIFOB(fd, cursor) + 1;
index = RFIFOL(fd, cursor);
cursor += 4;
switch (RFIFOB(fd, cursor++)) {
// int
case 0:
if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%" PRIu32 "','%s','%" PRIu32 "','%" PRId64 "')", db->global_acc_reg_num_table, account_id, esc_key, index, RFIFOQ(fd, cursor)) )
Sql_ShowDebug(sql_handle);
cursor += 8;
break;
case 1:
if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%" PRIu32 "' AND `key` = '%s' AND `index` = '%" PRIu32 "' LIMIT 1", db->global_acc_reg_num_table, account_id, esc_key, index) )
Sql_ShowDebug(sql_handle);
break;
// str
case 2:
safestrncpy(sval, RFIFOCP(fd, cursor + 1), RFIFOB(fd, cursor));
cursor += RFIFOB(fd, cursor) + 1;
Sql_EscapeString(sql_handle, esc_sval, sval);
if( SQL_ERROR == Sql_Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%" PRIu32 "','%s','%" PRIu32 "','%s')", db->global_acc_reg_str_table, account_id, esc_key, index, esc_sval) )
Sql_ShowDebug(sql_handle);
break;
case 3:
if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%" PRIu32 "' AND `key` = '%s' AND `index` = '%" PRIu32 "' LIMIT 1", db->global_acc_reg_str_table, account_id, esc_key, index) )
Sql_ShowDebug(sql_handle);
break;
default:
ShowError("mmo_save_global_accreg: unknown type %d\n",RFIFOB(fd, cursor - 1));
return;
}
}
}
}*/
/*
void mmo_send_global_accreg(AccountDB* self, int fd, uint32 account_id, uint32 char_id) {
Sql* sql_handle = ((AccountDB_SQL*)self)->accounts;
AccountDB_SQL* db = (AccountDB_SQL*)self;
char* data;
int16 plen = 0;
size_t len;
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%" PRIu32 "'", db->global_acc_reg_str_table, account_id) )
Sql_ShowDebug(sql_handle);
WFIFOHEAD(fd, 60000 + 300);
WFIFOW(fd, 0) = 0x2726;
// 0x2 = length, set prior to being sent
WFIFOL(fd, 4) = account_id;
WFIFOL(fd, 8) = char_id;
WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
WFIFOB(fd, 13) = 1; // is string type
WFIFOW(fd, 14) = 0; // count
plen = 16;
//
// * Vessel!
// *
// * str type
// * { keyLength(B), key(<keyLength>), index(L), valLength(B), val(<valLength>) }
//
while ( SQL_SUCCESS == Sql_NextRow(sql_handle) ) {
Sql_GetData(sql_handle, 0, &data, nullptr);
len = strlen(data)+1;
WFIFOB(fd, plen) = (unsigned char)len; // won't be higher; the column size is 32
plen += 1;
safestrncpy(WFIFOCP(fd,plen), data, len);
plen += static_cast<decltype(plen)>( len );
Sql_GetData(sql_handle, 1, &data, nullptr);
WFIFOL(fd, plen) = (uint32)atol(data);
plen += 4;
Sql_GetData(sql_handle, 2, &data, nullptr);
len = strlen(data)+1;
WFIFOB(fd, plen) = (unsigned char)len; // won't be higher; the column size is 254
plen += 1;
safestrncpy(WFIFOCP(fd,plen), data, len);
plen += static_cast<decltype(plen)>( len );
WFIFOW(fd, 14) += 1;
if( plen > 60000 ) {
WFIFOW(fd, 2) = plen;
WFIFOSET(fd, plen);
// prepare follow up
WFIFOHEAD(fd, 60000 + 300);
WFIFOW(fd, 0) = 0x2726;
// 0x2 = length, set prior to being sent
WFIFOL(fd, 4) = account_id;
WFIFOL(fd, 8) = char_id;
WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
WFIFOB(fd, 13) = 1; // is string type
WFIFOW(fd, 14) = 0; // count
plen = 16;
}
}
WFIFOW(fd, 2) = plen;
WFIFOSET(fd, plen);
Sql_FreeResult(sql_handle);
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%" PRIu32 "'", db->global_acc_reg_num_table, account_id) )
Sql_ShowDebug(sql_handle);
WFIFOHEAD(fd, 60000 + 300);
WFIFOW(fd, 0) = 0x2726;
// 0x2 = length, set prior to being sent
WFIFOL(fd, 4) = account_id;
WFIFOL(fd, 8) = char_id;
WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
WFIFOB(fd, 13) = 0; // is int type
WFIFOW(fd, 14) = 0; // count
plen = 16;
//
// * Vessel!
// *
// * int type
// * { keyLength(B), key(<keyLength>), index(L), value(L) }
//
while ( SQL_SUCCESS == Sql_NextRow(sql_handle) ) {
Sql_GetData(sql_handle, 0, &data, nullptr);
len = strlen(data)+1;
WFIFOB(fd, plen) = (unsigned char)len; // won't be higher; the column size is 32
plen += 1;
safestrncpy(WFIFOCP(fd,plen), data, len);
plen += static_cast<decltype(plen)>( len );
Sql_GetData(sql_handle, 1, &data, nullptr);
WFIFOL(fd, plen) = (uint32)atol(data);
plen += 4;
Sql_GetData(sql_handle, 2, &data, nullptr);
WFIFOQ(fd, plen) = strtoll(data,nullptr,10);
plen += 8;
WFIFOW(fd, 14) += 1;
if( plen > 60000 ) {
WFIFOW(fd, 2) = plen;
WFIFOSET(fd, plen);
// prepare follow up
WFIFOHEAD(fd, 60000 + 300);
WFIFOW(fd, 0) = 0x2726;
// 0x2 = length, set prior to being sent
WFIFOL(fd, 4) = account_id;
WFIFOL(fd, 8) = char_id;
WFIFOB(fd, 12) = 0; // var type (only set when all vars have been sent, regardless of type)
WFIFOB(fd, 13) = 0; // is int type
WFIFOW(fd, 14) = 0; // count
plen = 16;
}
}
WFIFOB(fd, 12) = 1;
WFIFOW(fd, 2) = plen;
WFIFOSET(fd, plen);
Sql_FreeResult(sql_handle);
}*/
/*
bool account_db_sql_enable_webtoken( AccountDB* self, const uint32 account_id ){
AccountDB_SQL* db = (AccountDB_SQL*)self;
if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token_enabled` = '1' WHERE `account_id` = '%u'", db->account_db, account_id ) ){
Sql_ShowDebug( db->accounts );
return false;
}
return true;
}*/
/**
* Timered function to disable webtoken for user
* If the user is online, then they must have logged since we started the timer.
* In that case, do nothing. The new authtoken must be valid.
* @param tid: timer id
* @param tick: tick of execution
* @param id: user account id
* @param data: BotAccountDB // because we don't use singleton???
* @return :0
TIMER_FUNC(account_disable_webtoken_timer){
const struct online_login_data* p = login_get_online_user(id);
AccountDB_SQL* db = reinterpret_cast<AccountDB_SQL*>(data);
if (p == nullptr) {
ShowInfo("Web Auth Token for account %d was disabled\n", id);
if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token_enabled` = '0' WHERE `account_id` = '%u'", db->account_db, id ) ){
Sql_ShowDebug( db->accounts );
return 0;
}
}
return 0;
}
*/
/*
bool account_db_sql_disable_webtoken( AccountDB* self, const uint32 account_id ){
AccountDB_SQL* db = (AccountDB_SQL*)self;
//add_timer(gettick() + brokk_config.disable_webtoken_delay, account_disable_webtoken_timer, account_id, reinterpret_cast<intptr_t>(db));
return true;
}*/
/*
bool account_db_sql_remove_webtokens( AccountDB* self ){
AccountDB_SQL* db = (AccountDB_SQL*)self;
if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token` = NULL, `web_auth_token_enabled` = '0'", db->account_db ) ){
Sql_ShowDebug( db->accounts );
return false;
}
return true;
}*/

@ -1,676 +0,0 @@
// 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 CONFIG_HELPERS_HPP
#define CONFIG_HELPERS_HPP
/*
#include <string>
#include <cstdint>
#include <common/cbasetypes.hpp>
#include <common/database.hpp>
*/
/*Inter Configs
struct inter_conf {
uint32 start_status_points;
bool emblem_woe_change;
uint32 emblem_transparency_limit;
};
extern struct inter_conf inter_config;
*/
//Brokk Configs
/*
struct Brokk_Config {
uint32 brokk_ip; /// the address to bind to
uint16 brokk_port; /// the port to bind to
bool log_brokk; /// whether to log brokk server actions or not
char date_format[32]; /// date format used in messages
bool console; /// console input system enabled?
char brokkconf_name[256]; /// name of main config file
char msgconf_name[256]; /// name of msg_conf config file
bool destroy_all_on_shutdown; /// cleanup all bots on shutdown?
uint32 allowed_bot_creations; /// number of allowed bot creations in quick succession
uint8 acc_name_min_length; /// minimum account name length
uint8 password_min_length; /// minimum password length
int time_allowed; /// registration interval in seconds
bool use_web_auth_token; /// Enable web authentication token system
int disable_webtoken_delay; /// delay disabling web token after char logs off in milliseconds
#ifdef VIP_ENABLE
#endif
};
extern struct Brokk_Config brokk_config;
*/
bool config_read(const char* cfgName, bool normal);
void set_defaults();
//Battle Configs
#define MIN_HAIR_STYLE battle_config.min_hair_style
#define MAX_HAIR_STYLE battle_config.max_hair_style
#define MIN_HAIR_COLOR battle_config.min_hair_color
#define MAX_HAIR_COLOR battle_config.max_hair_color
#define MIN_CLOTH_COLOR battle_config.min_cloth_color
#define MAX_CLOTH_COLOR battle_config.max_cloth_color
#define MIN_BODY_STYLE battle_config.min_body_style
#define MAX_BODY_STYLE battle_config.max_body_style
/*
struct Battle_Config
{
int warp_point_debug;
int enable_critical;
int mob_critical_rate;
int critical_rate;
int enable_baseatk, enable_baseatk_renewal;
int enable_perfect_flee;
int cast_rate, delay_rate;
int delay_dependon_dex, delay_dependon_agi;
int sdelay_attack_enable;
int left_cardfix_to_right;
int cardfix_monster_physical;
int skill_add_range;
int skill_out_range_consume;
int skill_amotion_leniency;
int skillrange_by_distance; //[Skotlex]
int use_weapon_skill_range; //[Skotlex]
int pc_damage_delay_rate;
int defnotenemy;
int vs_traps_bctall;
int traps_setting;
int summon_flora; //[Skotlex]
int clear_unit_ondeath; //[Skotlex]
int clear_unit_onwarp; //[Skotlex]
int random_monster_checklv;
int attr_recover;
int item_auto_get;
int flooritem_lifetime;
int item_first_get_time;
int item_second_get_time;
int item_third_get_time;
int mvp_item_first_get_time;
int mvp_item_second_get_time;
int mvp_item_third_get_time;
int base_exp_rate, job_exp_rate;
int drop_rate0item;
int death_penalty_type;
int death_penalty_base, death_penalty_job;
int pvp_exp; // [MouseJstr]
int gtb_sc_immunity;
int zeny_penalty;
int restart_hp_rate;
int restart_sp_rate;
int mvp_exp_rate;
int mvp_hp_rate;
int monster_hp_rate;
int monster_max_aspd;
int view_range_rate;
int chase_range_rate;
int atc_spawn_quantity_limit;
int atc_slave_clone_limit;
int partial_name_scan;
int skillfree;
int skillup_limit;
int wp_rate;
int pp_rate;
int monster_active_enable;
int monster_damage_delay_rate;
int monster_loot_type;
int mob_skill_rate; //[Skotlex]
int mob_skill_delay; //[Skotlex]
int mob_count_rate;
int no_spawn_on_player; //[Skotlex]
int force_random_spawn; //[Skotlex]
int mob_spawn_delay, plant_spawn_delay, boss_spawn_delay; // [Skotlex]
int slaves_inherit_mode;
int slaves_inherit_speed;
int summons_trigger_autospells;
int pc_walk_delay_rate; //Adjusts can't walk delay after being hit for players. [Skotlex]
int walk_delay_rate; //Adjusts can't walk delay after being hit. [Skotlex]
int multihit_delay; //Adjusts can't walk delay per hit on multi-hitting skills. [Skotlex]
int quest_skill_learn;
int quest_skill_reset;
int basic_skill_check;
int guild_emperium_check;
int guild_exp_limit;
int guild_max_castles;
int guild_skill_relog_delay;
int guild_skill_relog_type;
int emergency_call;
int guild_aura;
int pc_invincible_time;
int pet_catch_rate;
int pet_rename;
int pet_friendly_rate;
int pet_hungry_delay_rate;
int pet_hungry_friendly_decrease;
int pet_status_support;
int pet_attack_support;
int pet_damage_support;
int pet_support_min_friendly; //[Skotlex]
int pet_support_rate;
int pet_attack_exp_to_master;
int pet_attack_exp_rate;
int pet_lv_rate; //[Skotlex]
int pet_max_stats; //[Skotlex]
int pet_max_atk1; //[Skotlex]
int pet_max_atk2; //[Skotlex]
int pet_no_gvg; //Disables pets in gvg. [Skotlex]
int pet_equip_required;
int pet_unequip_destroy;
int pet_master_dead;
int skill_min_damage;
int finger_offensive_type;
int heal_exp;
int max_heal_lv;
int max_heal; //Mitternacht
int resurrection_exp;
int shop_exp;
int combo_delay_rate;
int item_check;
int item_use_interval; //[Skotlex]
int cashfood_use_interval;
int wedding_modifydisplay;
int wedding_ignorepalette; //[Skotlex]
int xmas_ignorepalette; // [Valaris]
int summer_ignorepalette; // [Zephyrus]
int hanbok_ignorepalette;
int oktoberfest_ignorepalette;
int natural_healhp_interval;
int natural_healsp_interval;
int natural_heal_skill_interval;
int natural_heal_weight_rate;
int natural_heal_weight_rate_renewal;
int arrow_decrement;
int ammo_unequip;
int ammo_check_weapon;
int max_aspd;
int max_walk_speed; //Maximum walking speed after buffs [Skotlex]
int max_hp_lv99;
int max_hp_lv150;
int max_hp;
int max_sp;
int max_lv, aura_lv;
int max_parameter, max_baby_parameter;
int max_cart_weight;
int skill_log;
int battle_log;
int etc_log;
int save_clothcolor;
int undead_detect_type;
int auto_counter_type;
int min_hitrate; //[Skotlex]
int max_hitrate; //[Skotlex]
int agi_penalty_target;
int agi_penalty_type;
int agi_penalty_count;
int agi_penalty_num;
int vit_penalty_target;
int vit_penalty_type;
int vit_penalty_count;
int vit_penalty_num;
int weapon_defense_type;
int magic_defense_type;
int skill_reiteration;
int skill_nofootset;
int pc_cloak_check_type;
int monster_cloak_check_type;
int estimation_type;
int gvg_short_damage_rate;
int gvg_long_damage_rate;
int gvg_weapon_damage_rate;
int gvg_magic_damage_rate;
int gvg_misc_damage_rate;
int gvg_flee_penalty;
int pk_short_damage_rate;
int pk_long_damage_rate;
int pk_weapon_damage_rate;
int pk_magic_damage_rate;
int pk_misc_damage_rate;
int mob_changetarget_byskill;
int attack_direction_change;
int land_skill_limit;
int monster_class_change_recover;
int produce_item_name_input;
int display_skill_fail;
int chat_warpportal;
int mob_warp;
int dead_branch_active;
int vending_max_value;
int vending_over_max;
int vending_tax;
int vending_tax_min;
int show_steal_in_same_party;
int party_share_type;
int party_hp_mode;
int party_show_share_picker;
int show_picker_item_type;
int attack_attr_none;
int item_rate_mvp, item_rate_common, item_rate_common_boss, item_rate_card, item_rate_card_boss,
item_rate_equip, item_rate_equip_boss, item_rate_heal, item_rate_heal_boss, item_rate_use,
item_rate_use_boss, item_rate_treasure, item_rate_adddrop;
int item_rate_common_mvp, item_rate_heal_mvp, item_rate_use_mvp, item_rate_equip_mvp, item_rate_card_mvp;
int logarithmic_drops;
int item_drop_common_min, item_drop_common_max; // Added by TyrNemesis^
int item_drop_card_min, item_drop_card_max;
int item_drop_equip_min, item_drop_equip_max;
int item_drop_mvp_min, item_drop_mvp_max; // End Addition
int item_drop_mvp_mode; //rAthena addition [Playtester]
int item_drop_heal_min, item_drop_heal_max; // Added by Valatris
int item_drop_use_min, item_drop_use_max; //End
int item_drop_treasure_min, item_drop_treasure_max; //by [Skotlex]
int item_drop_adddrop_min, item_drop_adddrop_max; //[Skotlex]
int prevent_logout; // Added by RoVeRT
int prevent_logout_trigger;
int land_protector_behavior;
int npc_emotion_behavior;
int alchemist_summon_reward; // [Valaris]
int drops_by_luk;
int drops_by_luk2;
int equip_natural_break_rate; //Base Natural break rate for attacks.
int equip_self_break_rate; //Natural & Penalty skills break rate
int equip_skill_break_rate; //Offensive skills break rate
int multi_level_up;
int multi_level_up_base;
int multi_level_up_job;
int max_exp_gain_rate; //Max amount of exp bar % you can get in one go.
int pk_mode;
int pk_mode_mes;
int pk_level_range;
int manner_system; // end additions [Valaris]
int show_mob_info;
int gx_allhit;
int gx_disptype;
int devotion_level_difference;
int player_skill_partner_check;
int invite_request_check;
int skill_removetrap_type;
int disp_experience;
int disp_zeny;
int backstab_bow_penalty;
int hp_rate;
int sp_rate;
int bone_drop;
int buyer_name;
int dancing_weaponswitch_fix;
// eAthena additions
int night_at_start; // added by [Yor]
int day_duration; // added by [Yor]
int night_duration; // added by [Yor]
int ban_hack_trade; // added by [Yor]
int min_hair_style; // added by [MouseJstr]
int max_hair_style; // added by [MouseJstr]
int min_hair_color; // added by [MouseJstr]
int max_hair_color; // added by [MouseJstr]
int min_cloth_color; // added by [MouseJstr]
int max_cloth_color; // added by [MouseJstr]
int pet_hair_style; // added by [Skotlex]
int castrate_dex_scale; // added by [MouseJstr]
int area_size; // added by [MouseJstr]
int max_def, over_def_bonus; //added by [Skotlex]
int zeny_from_mobs; // [Valaris]
int mobs_level_up; // [Valaris]
int mobs_level_up_exp_rate; // [Valaris]
int pk_min_level; // [celest]
int skill_steal_max_tries; //max steal skill tries on a mob. if 0, then w/o limit [Lupus]
int skill_steal_random_options;
int motd_type; // [celest]
int finding_ore_rate; // orn
int exp_calc_type;
int exp_bonus_attacker;
int exp_bonus_max_attacker;
int min_skill_delay_limit;
int default_walk_delay;
int no_skill_delay;
int attack_walk_delay;
int require_glory_guild;
int idle_no_share;
int party_update_interval;
int party_even_share_bonus;
int delay_battle_damage;
int hide_woe_damage;
int display_version;
int display_hallucination; // [Skotlex]
int use_statpoint_table; // [Skotlex]
int debuff_on_logout; // Removes a few "official" negative Scs on logout. [Skotlex]
int mob_ai; //Configures various mob_ai settings to make them smarter or dumber(official). [Skotlex]
int hom_setting; //Configures various homunc settings which make them behave unlike normal characters.. [Skotlex]
int dynamic_mobs; // Dynamic Mobs [Wizputer] - battle_athena flag implemented by [random]
int mob_remove_damaged; // Dynamic Mobs - Remove mobs even if damaged [Wizputer]
int mob_remove_delay; // Dynamic Mobs - delay before removing mobs from a map [Skotlex]
int mob_active_time; //Duration through which mobs execute their Hard AI after players leave their area of sight.
int boss_active_time;
int show_hp_sp_drain, show_hp_sp_gain; //[Skotlex]
int mob_npc_event_type; //Determines on who the npc_event is executed. [Skotlex]
int character_size; // if riders have size=2, and baby class riders size=1 [Lupus]
int mob_max_skilllvl; // Max possible skill level [Lupus]
int rare_drop_announce; // chance <= to show rare drops global announces
int drop_rate_cap; // Drop rate can't be raised above this amount by drop bonus items
int drop_rate_cap_vip;
int retaliate_to_master; //Whether when a mob is attacked by another mob, it will retaliate versus the mob or the mob's master. [Skotlex]
int duel_allow_pvp; // [LuzZza]
int duel_allow_gvg; // [LuzZza]
int duel_allow_teleport; // [LuzZza]
int duel_autoleave_when_die; // [LuzZza]
int duel_time_interval; // [LuzZza]
int duel_only_on_same_map; // [Toms]
int skip_teleport_lv1_menu; // possibility to disable (skip) Teleport Lv1 menu, that have only two lines `Random` and `Cancel` [LuzZza]
int allow_skill_without_day; // [Komurka]
int allow_es_magic_pc; // [Skotlex]
int skill_wall_check; // [Skotlex]
int official_cell_stack_limit; // [Playtester]
int custom_cell_stack_limit; // [Skotlex]
int skill_caster_check; // [Skotlex]
int sc_castcancel; // [Skotlex]
int pc_sc_def_rate; // [Skotlex]
int mob_sc_def_rate;
int pc_max_sc_def;
int mob_max_sc_def;
int sg_angel_skill_ratio;
int sg_miracle_skill_ratio;
int sg_miracle_skill_duration;
int autospell_stacking; //Enables autospell cards to stack. [Skotlex]
int override_mob_names; //Enables overriding spawn mob names with the mob_db names. [Skotlex]
int min_chat_delay; //Minimum time between client messages. [Skotlex]
int friend_auto_add; //When accepting friends, both get friended. [Skotlex]
int hvan_explosion_intimate; // fix [albator]
int hom_rename;
int homunculus_show_growth; //[orn]
int homunculus_friendly_rate;
int quest_exp_rate;
int autotrade_mapflag;
int at_timeout;
int homunculus_autoloot;
int idle_no_autoloot;
int max_guild_alliance;
int ksprotection;
int auction_feeperhour;
int auction_maximumprice;
int homunculus_auto_vapor; //Keep Homunculus from Vaporizing when master dies. [L0ne_W0lf]
int display_status_timers; //Show or hide skill buff/delay timers in recent clients [Sara]
int skill_add_heal_rate; //skills that bHealPower has effect on [Inkfish]
int eq_single_target_reflectable;
int invincible_nodamage;
int mob_slave_keep_target;
int autospell_check_range; //Enable range check for autospell bonus. [L0ne_W0lf]
int knockback_left;
int client_reshuffle_dice; // Reshuffle /dice
int client_sort_storage;
int feature_buying_store;
int feature_search_stores;
int searchstore_querydelay;
int searchstore_maxresults;
int display_party_name;
int cashshop_show_points;
int mail_show_status;
int client_limit_unit_lv;
int hom_max_level;
int hom_S_max_level;
int hom_S_growth_level;
// [BattleGround Settings]
int bg_update_interval;
int bg_short_damage_rate;
int bg_long_damage_rate;
int bg_weapon_damage_rate;
int bg_magic_damage_rate;
int bg_misc_damage_rate;
int bg_flee_penalty;
// rAthena
int max_third_parameter;
int max_baby_third_parameter;
int max_trans_parameter;
int max_third_trans_parameter;
int max_extended_parameter;
int max_summoner_parameter;
int max_fourth_parameter;
int max_third_aspd;
int max_summoner_aspd;
int vcast_stat_scale;
int mvp_tomb_enabled;
int mvp_tomb_delay;
int atcommand_suggestions_enabled;
int min_npc_vendchat_distance;
int atcommand_mobinfo_type;
int mob_size_influence; // Enable modifications on earned experience, drop rates and monster status depending on monster size. [mkbu95]
int skill_trap_type;
int allow_consume_restricted_item;
int allow_equip_restricted_item;
int max_walk_path;
int item_enabled_npc;
int item_onfloor; // Whether to drop an undroppable item on the map or destroy it if inventory is full.
int bowling_bash_area;
int drop_rateincrease;
int feature_auction;
int feature_banking;
int vip_storage_increase;
int vip_base_exp_increase;
int vip_job_exp_increase;
int vip_zeny_penalty;
int vip_bm_increase;
int vip_drop_increase;
int vip_gemstone;
int vip_exp_penalty_base;
int vip_exp_penalty_job;
int vip_disp_rate;
int mon_trans_disable_in_gvg;
int discount_item_point_shop;
int update_enemy_position;
int devotion_rdamage;
int feature_itemlink;
int feature_mesitemlink;
int feature_mesitemlink_brackets;
int feature_mesitemlink_dbname;
// autotrade persistency
int feature_autotrade;
int feature_autotrade_direction;
int feature_autotrade_head_direction;
int feature_autotrade_sit;
int feature_autotrade_open_delay;
// Fame points
int fame_taekwon_mission;
int fame_refine_lv1;
int fame_refine_lv2;
int fame_refine_lv3;
int fame_forge;
int fame_pharmacy_3;
int fame_pharmacy_5;
int fame_pharmacy_7;
int fame_pharmacy_10;
int disp_servervip_msg;
int warg_can_falcon;
int path_blown_halt;
int rental_mount_speed_boost;
int warp_suggestions_enabled;
int taekwon_mission_mobname;
int teleport_on_portal;
int cart_revo_knockback;
int guild_notice_changemap;
int transcendent_status_points;
int taekwon_ranker_min_lv;
int revive_onwarp;
int mail_delay;
int autotrade_monsterignore;
int idletime_option;
int spawn_direction;
int arrow_shower_knockback;
int devotion_rdamage_skill_only;
int max_extended_aspd;
int mob_chase_refresh; //How often a monster should refresh its chase [Playtester]
int mob_icewall_walk_block; //How a normal monster should be trapped in icewall [Playtester]
int boss_icewall_walk_block; //How a boss monster should be trapped in icewall [Playtester]
int snap_dodge; // Enable or disable dodging damage snapping away [csnv]
int stormgust_knockback;
int default_fixed_castrate;
int default_bind_on_equip;
int pet_ignore_infinite_def; // Makes fixed damage of petskillattack2 ignores infinite defense
int homunculus_evo_intimacy_need;
int homunculus_evo_intimacy_reset;
int monster_loot_search_type;
int feature_roulette;
int feature_roulette_bonus_reward;
int monster_hp_bars_info;
int min_body_style;
int max_body_style;
int save_body_style;
int mob_eye_range_bonus; //Vulture's Eye and Snake's Eye range bonus
int mob_stuck_warning; //Show warning if a monster is stuck too long
int skill_eightpath_algorithm; //Official path algorithm
int skill_eightpath_same_cell;
int death_penalty_maxlv;
int exp_cost_redemptio;
int exp_cost_redemptio_limit;
int mvp_exp_reward_message;
int can_damage_skill; //Which BL types can damage traps
int atcommand_levelup_events;
int atcommand_disable_npc;
int block_account_in_same_party;
int tarotcard_equal_chance; //Official or equal chance for each card
int change_party_leader_samemap;
int dispel_song; //Can songs be dispelled?
int guild_maprespawn_clones; // Should clones be killed by maprespawnguildid?
int hide_fav_sell;
int mail_daily_count;
int mail_zeny_fee;
int mail_attachment_price;
int mail_attachment_weight;
int banana_bomb_duration;
int guild_leaderchange_delay;
int guild_leaderchange_woe;
int guild_alliance_onlygm;
int feature_achievement;
int allow_bound_sell;
int autoloot_adjust;
int feature_petevolution;
int feature_pet_autofeed;
int feature_pet_autofeed_rate;
int pet_autofeed_always;
int broadcast_hide_name;
int skill_drop_items_full;
int switch_remove_edp;
int feature_homunculus_autofeed;
int feature_homunculus_autofeed_rate;
int summoner_race;
int summoner_size;
int homunculus_autofeed_always;
int feature_attendance;
int feature_privateairship;
int rental_transaction;
int min_shop_buy;
int min_shop_sell;
int feature_equipswitch;
int pet_walk_speed;
int blacksmith_fame_refine_threshold;
int mob_nopc_idleskill_rate;
int mob_nopc_move_rate;
int boss_nopc_idleskill_rate;
int boss_nopc_move_rate;
int hom_idle_no_share;
int idletime_hom_option;
int devotion_standup_fix;
int feature_bgqueue;
int bgqueue_nowarp_mapflag;
int homunculus_exp_gain;
int rental_item_novalue;
int ping_timer_interval;
int ping_time;
int show_skill_scale;
int achievement_mob_share;
int slave_stick_with_master;
int at_logout_event;
int homunculus_starving_rate;
int homunculus_starving_delay;
int drop_connection_on_quit;
int mob_spawn_variance;
int mercenary_autoloot;
int mer_idle_no_share;
int idletime_mer_option;
int feature_refineui;
int rndopt_drop_pillar;
int pet_legacy_formula;
int pet_distance_check;
int pet_hide_check;
int instance_block_leave;
int instance_block_leaderchange;
int instance_block_invite;
int instance_block_expulsion;
// 4th Jobs Stuff
int trait_points_job_change;
int use_traitpoint_table;
int max_trait_parameter;
int max_res_mres_ignored;
int max_ap;
int ap_rate;
int restart_ap_rate;
int loose_ap_on_death;
int loose_ap_on_map;
int keep_ap_on_logout;
int attack_machine_level_difference;
int feature_barter;
int feature_barter_extended;
int break_mob_equip;
int macro_detection_retry;
int macro_detection_timeout;
int macro_detection_punishment;
int macro_detection_punishment_time;
int feature_dynamicnpc_timeout;
int feature_dynamicnpc_rangex;
int feature_dynamicnpc_rangey;
int feature_dynamicnpc_direction;
int mob_respawn_time;
int mob_unlock_time;
int map_edge_size;
int randomize_center_cell;
int feature_stylist;
int feature_banking_state_enforce;
int instance_allow_reconnect;
int synchronize_damage;
int item_stacking;
int hom_delay_reset_vaporize;
int hom_delay_reset_warp;
#include <custom/battle_config_struct.inc>
};
extern struct Battle_Config battle_config;
*/
#endif /* CONFIG_HELPERS_HPP */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,44 +0,0 @@
[
{
"userid": "BrokkBot1",
"pass": "password",
"sex": "M",
"last_ip": "127.0.0.1",
"characters": [
{
"name": "BrokkKnight",
"str": 9,
"agi": 9,
"vit": 9,
"int": 1,
"dex": 1,
"luk": 1,
"slot": 0,
"hair_style": 1,
"hair_color": 0,
"start_job": 1
}
]
},
{
"userid": "BrokkBot2",
"pass": "password",
"sex": "F",
"last_ip": "127.0.0.1",
"characters": [
{
"name": "BrokkDancer",
"str": 1,
"agi": 9,
"vit": 1,
"int": 9,
"dex": 9,
"luk": 1,
"slot": 0,
"hair_style": 2,
"hair_color": 1,
"start_job": 2
}
]
}
]

@ -24,90 +24,6 @@
using namespace rathena;
using namespace rathena::server_sindri;
struct Sindri_Config sindri_config; /// Configuration of sindri-server
int sindri_fd; // sindr server file descriptor socket
/**
* Reading main configuration file.
* @param cfgName: Name of the configuration (could be fullpath)
* @param normal: Config read normally when server started
* @return True:success, Fals:failure (file not found|readable)
*/
bool sindri_config_read(const char* cfgName, bool normal) {
char line[1024], w1[32], w2[1024];
FILE* fp = fopen(cfgName, "r");
if (fp == nullptr) {
ShowError("Configuration file (%s) not found.\n", cfgName);
return false;
}
while (fgets(line, sizeof(line), fp)) {
if (line[0] == '/' && line[1] == '/')
continue;
if (sscanf(line, "%31[^:]: %1023[^\r\n]", w1, w2) < 2)
continue;
// Config that loaded only when server started, not by reloading config file
if (normal) {
if (!strcmpi(w1, "bind_ip")) {
sindri_config.sindri_ip = host2ip(w2);
if (sindri_config.sindri_ip) {
char ip_str[16];
ShowStatus("Sindri server binding IP address : %s -> %s\n", w2, ip2str(sindri_config.sindri_ip, ip_str));
}
}
else if (!strcmpi(w1, "sindri_port"))
sindri_config.sindri_port = (uint16)atoi(w2);
else if (!strcmpi(w1, "sindriconsole"))
sindri_config.console = (bool)config_switch(w2);
}
if (!strcmpi(w1, "timestamp_format"))
safestrncpy(timestamp_format, w2, 20);
else if (strcmpi(w1, "db_path") == 0)
safestrncpy(db_path, w2, ARRAYLENGTH(db_path));
else if (!strcmpi(w1, "stdout_with_ansisequence"))
stdout_with_ansisequence = config_switch(w2);
else if (!strcmpi(w1, "console_silent")) {
msg_silent = atoi(w2);
if (msg_silent) /* only bother if we actually have this enabled */
ShowInfo("Console Silent Setting: %d\n", atoi(w2));
}
else if (strcmpi(w1, "console_msg_log") == 0)
console_msg_log = atoi(w2);
else if (strcmpi(w1, "console_log_filepath") == 0)
safestrncpy(console_log_filepath, w2, sizeof(console_log_filepath));
else if (!strcmpi(w1, "log_sindri"))
sindri_config.log_sindri = (bool)config_switch(w2);
else if (!strcmpi(w1, "date_format"))
safestrncpy(sindri_config.date_format, w2, sizeof(sindri_config.date_format));
else if (!strcmpi(w1, "import"))
sindri_config_read(w2, normal);
else {// try the account engines
if (!normal)
continue;
}
}
fclose(fp);
ShowInfo("Finished reading %s.\n", cfgName);
return true;
}
/**
* Init sindri-serv default configuration.
*/
void sindri_set_defaults() {
sindri_config.sindri_ip = INADDR_ANY;
sindri_config.sindri_port = 7600;
sindri_config.log_sindri = true;
safestrncpy(sindri_config.date_format, "%Y-%m-%d %H:%M:%S", sizeof(sindri_config.date_format));
sindri_config.console = false;
//other default conf
safestrncpy(sindri_config.sindriconf_name, "conf/sindri_athena.conf", sizeof(sindri_config.sindriconf_name));
safestrncpy(sindri_config.msgconf_name, "conf/msg_conf/sindri_msg.conf", sizeof(sindri_config.msgconf_name));
}
void SindriServer::finalize() {
@ -121,26 +37,6 @@ void SindriServer::handle_shutdown() {
}
bool SindriServer::initialize(int argc, char* argv[]) {
// Init default value
safestrncpy(console_log_filepath, "./log/sindri-msg_log.log", sizeof(console_log_filepath));
// read sindri-server configuration
sindri_set_defaults();
sindri_config_read(sindri_config.sindriconf_name, true);
// initialize logging
//if (sindri_config.log_sindri)
// sindrilog_init();
// server port open & binding
if ((sindri_fd = make_listen_bind(sindri_config.sindri_ip, sindri_config.sindri_port)) == -1) {
ShowFatalError("Failed to bind to port '" CL_WHITE "%d" CL_RESET "'\n", sindri_config.sindri_port);
return false;
}
ShowStatus("The sindri-server is " CL_GREEN "ready" CL_RESET " (Server is listening on the port %u).\n\n", sindri_config.sindri_port);
//sindri_log(0, "sindri server", 100, "sindri server started");
return true;
}

@ -30,22 +30,4 @@ namespace rathena {
};
}
}
struct Sindri_Config {
uint32 sindri_ip; /// the address to bind to
uint16 sindri_port; /// the port to bind to
bool log_sindri; /// whether to log brokk server actions or not
char date_format[32]; /// date format used in messages
bool console; /// console input system enabled?
char sindriconf_name[256]; /// name of main config file
char msgconf_name[256]; /// name of msg_conf config file
#ifdef VIP_ENABLE
#endif
};
extern struct Sindri_Config sindri_config;
#endif /* SINDRI_HPP */

@ -466,7 +466,7 @@ int Core::start( int argc, char **argv ){
return EXIT_SUCCESS;
}
int Core::start_sah_edition(int argc, char** argv) {
int Core::start_botserver(int argc, char** argv) {
// Same as start but slightly modified for the bot servers
if (this->get_status() != e_core_status::NOT_STARTED) {
ShowFatalError("Core was already started and cannot be started again!\n");

@ -90,7 +90,7 @@ namespace rathena{
void signal_crash();
void signal_shutdown();
int start( int argc, char* argv[] );
int start_sah_edition(int argc, char* argv[]);
int start_botserver(int argc, char* argv[]);
};
}
}
@ -110,7 +110,7 @@ template <typename T> int main_bot_core(int argc, char* argv[]) {
global_core = &server;
return server.start_sah_edition(argc, argv);
return server.start_botserver(argc, argv);
}
#endif /* CORE_HPP */

@ -10317,7 +10317,6 @@ bool pc_setparam(map_session_data *sd,int64 type,int64 val_tmp)
sd->status.zeny = cap_value(val, 0, MAX_ZENY);
break;
case SP_BASEEXP:
//TODO Branch off if char is bot -> No XP should be gained
val_tmp = cap_value(val_tmp, 0, pc_is_maxbaselv(sd) ? MAX_LEVEL_BASE_EXP : MAX_EXP);
if (val_tmp < sd->status.base_exp) // Lost
pc_lostexp(sd, sd->status.base_exp - val_tmp, 0);
@ -10325,7 +10324,6 @@ bool pc_setparam(map_session_data *sd,int64 type,int64 val_tmp)
pc_gainexp(sd, nullptr, val_tmp - sd->status.base_exp, 0, 2);
return true;
case SP_JOBEXP:
//TODO Branch off if char is bot -> No XP should be gained
val_tmp = cap_value(val_tmp, 0, pc_is_maxjoblv(sd) ? MAX_LEVEL_JOB_EXP : MAX_EXP);
if (val_tmp < sd->status.job_exp) // Lost
pc_lostexp(sd, 0, sd->status.job_exp - val_tmp);

Loading…
Cancel
Save