summaryrefslogtreecommitdiff
path: root/src/char/char.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/char/char.c')
-rw-r--r--src/char/char.c1726
1 files changed, 1055 insertions, 671 deletions
diff --git a/src/char/char.c b/src/char/char.c
index d05d13d4b..069ccc481 100644
--- a/src/char/char.c
+++ b/src/char/char.c
@@ -2,7 +2,7 @@
* This file is part of Hercules.
* http://herc.ws - http://github.com/HerculesWS/Hercules
*
- * Copyright (C) 2012-2015 Hercules Dev Team
+ * Copyright (C) 2012-2016 Hercules Dev Team
* Copyright (C) Athena Dev Teams
*
* Hercules is free software: you can redistribute it and/or modify
@@ -42,6 +42,7 @@
#include "common/HPM.h"
#include "common/cbasetypes.h"
+#include "common/conf.h"
#include "common/console.h"
#include "common/core.h"
#include "common/db.h"
@@ -61,8 +62,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
-#ifndef WIN32
-# include <unistd.h>
+#include <sys/stat.h> // stat()
+
+#if MAX_MAP_SERVERS > 1
+# ifdef _MSC_VER
+# pragma message("WARNING: your settings allow more than one map server to connect, this is deprecated dangerous feature USE IT AT YOUR OWN RISK")
+# else
+# warning your settings allow more than one map server to connect, this is deprecated dangerous feature USE IT AT YOUR OWN RISK
+# endif
#endif
// private declarations
@@ -105,9 +112,6 @@ char char_reg_num_db[32] = "char_reg_num_db";
struct char_interface char_s;
struct char_interface *chr;
-// show loading/saving messages
-int save_log = 1;
-
char db_path[1024] = "db";
char wisp_server_name[NAME_LENGTH] = "Server";
@@ -118,7 +122,7 @@ char char_ip_str[128];
char bind_ip_str[128];
uint32 bind_ip = INADDR_ANY;
int char_maintenance_min_group_id = 0;
-bool char_new = true;
+bool enable_char_creation = true; ///< Whether to allow character creation.
bool name_ignoring_case = false; // Allow or not identical name for characters but with a different case by [Yor]
int char_name_option = 0; // Option to know which letters/symbols are authorized in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor]
@@ -126,19 +130,24 @@ char unknown_char_name[NAME_LENGTH] = "Unknown"; // Name to use when the request
#define TRIM_CHARS "\255\xA0\032\t\x0A\x0D " //The following characters are trimmed regardless because they cause confusion and problems on the servers. [Skotlex]
char char_name_letters[1024] = ""; // list of letters/symbols allowed (or not) in a character name. by [Yor]
-int char_del_level = 0; //From which level u can delete character [Lupus]
+int char_del_level = 0; ///< From which level you can delete character [Lupus]
int char_del_delay = 86400;
-
-int log_char = 1; // logging char or not [devil]
-int log_inter = 1; // logging inter or not [devil]
-
-int char_aegis_delete = 0; // Verify if char is in guild/party or char and reacts as Aegis does (doesn't allow deletion), see chr->delete2_req for more information
+bool char_aegis_delete = false; ///< Verify if char is in guild/party or char and reacts as Aegis does (disallow deletion), @see chr->delete2_req.
int max_connect_user = -1;
int gm_allow_group = -1;
int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
int start_zeny = 0;
-int start_items[MAX_START_ITEMS*3];
+
+/// Start items for new characters
+struct start_item_s {
+ int id;
+ int amount;
+ int loc;
+ bool stackable;
+};
+VECTOR_DECL(struct start_item_s) start_items;
+
int guild_exp_rate = 100;
//Custom limits for the fame lists. [Skotlex]
@@ -165,7 +174,7 @@ unsigned short skillid2idx[MAX_SKILL_ID];
//-----------------------------------------------------
#define AUTH_TIMEOUT 30000
-static DBMap* auth_db; // int account_id -> struct char_auth_node*
+static struct DBMap *auth_db; // int account_id -> struct char_auth_node*
//-----------------------------------------------------
// Online User Database
@@ -174,7 +183,7 @@ static DBMap* auth_db; // int account_id -> struct char_auth_node*
/**
* @see DBCreateData
*/
-static DBData char_create_online_char_data(DBKey key, va_list args)
+static struct DBData char_create_online_char_data(union DBKey key, va_list args)
{
struct online_char_data* character;
CREATE(character, struct online_char_data, 1);
@@ -278,8 +287,10 @@ void char_set_char_offline(int char_id, int account_id)
}
else
{
- struct mmo_charstatus* cp = (struct mmo_charstatus*)idb_get(chr->char_db_,char_id);
+ struct mmo_charstatus* cp = (struct mmo_charstatus*) idb_get(chr->char_db_,char_id);
+
inter_guild->CharOffline(char_id, cp?cp->guild_id:-1);
+
if (cp)
idb_remove(chr->char_db_,char_id);
@@ -316,7 +327,7 @@ void char_set_char_offline(int char_id, int account_id)
/**
* @see DBApply
*/
-static int char_db_setoffline(DBKey key, DBData *data, va_list ap)
+static int char_db_setoffline(union DBKey key, struct DBData *data, va_list ap)
{
struct online_char_data* character = (struct online_char_data*)DB->data2ptr(data);
int server_id = va_arg(ap, int);
@@ -336,7 +347,7 @@ static int char_db_setoffline(DBKey key, DBData *data, va_list ap)
/**
* @see DBApply
*/
-static int char_db_kickoffline(DBKey key, DBData *data, va_list ap)
+static int char_db_kickoffline(union DBKey key, struct DBData *data, va_list ap)
{
struct online_char_data* character = (struct online_char_data*)DB->data2ptr(data);
int server_id = va_arg(ap, int);
@@ -391,7 +402,7 @@ void char_set_all_offline_sql(void)
/**
* @see DBCreateData
*/
-static DBData char_create_charstatus(DBKey key, va_list args)
+static struct DBData char_create_charstatus(union DBKey key, va_list args)
{
struct mmo_charstatus *cp;
cp = (struct mmo_charstatus *) aCalloc(1,sizeof(struct mmo_charstatus));
@@ -419,7 +430,7 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p)
//map inventory data
if( memcmp(p->inventory, cp->inventory, sizeof(p->inventory)) ) {
- if (!chr->inventory_to_sql(p->inventory, MAX_INVENTORY, p->char_id))
+ if (!chr->memitemdata_to_sql(p->inventory, MAX_INVENTORY, p->char_id, TABLE_INVENTORY))
strcat(save_status, " inventory");
else
errors++;
@@ -433,14 +444,6 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p)
errors++;
}
- //map storage data
- if( memcmp(p->storage.items, cp->storage.items, sizeof(p->storage.items)) ) {
- if (!chr->memitemdata_to_sql(p->storage.items, MAX_STORAGE, p->account_id, TABLE_STORAGE))
- strcat(save_status, " storage");
- else
- errors++;
- }
-
if (
(p->base_exp != cp->base_exp) || (p->base_level != cp->base_level) ||
(p->job_level != cp->job_level) || (p->job_exp != cp->job_exp) ||
@@ -473,7 +476,7 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p)
"`base_exp`='%u', `job_exp`='%u', `zeny`='%d',"
"`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d',"
"`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d',"
- "`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d',`elemental_id`='%d',"
+ "`option`='%u',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d',`elemental_id`='%d',"
"`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
"`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d', `rename`='%d',"
"`delete_date`='%lu',`robe`='%d',`slotchange`='%d', `char_opt`='%u', `font`='%u', `uniqueitem_counter` ='%u',"
@@ -510,7 +513,7 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p)
if (
(p->hair != cp->hair) || (p->hair_color != cp->hair_color) ||
(p->clothes_color != cp->clothes_color) || (p->body != cp->body) ||
- (p->class_ != cp->class_) ||
+ (p->class != cp->class) ||
(p->partner_id != cp->partner_id) || (p->father != cp->father) ||
(p->mother != cp->mother) || (p->child != cp->child) ||
(p->karma != cp->karma) || (p->manner != cp->manner) ||
@@ -522,7 +525,7 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p)
"`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d',"
"`karma`='%d', `manner`='%d', `fame`='%d'"
" WHERE `account_id`='%d' AND `char_id` = '%d'",
- char_db, p->class_,
+ char_db, p->class,
p->hair, p->hair_color, p->clothes_color, p->body,
p->partner_id, p->father, p->mother, p->child,
p->karma, p->manner, p->fame,
@@ -685,60 +688,73 @@ int char_mmo_char_tosql(int char_id, struct mmo_charstatus* p)
#endif
StrBuf->Destroy(&buf);
- if (save_status[0]!='\0' && save_log)
+ if (chr->show_save_log && save_status[0] != '\0')
ShowInfo("Saved char %d - %s:%s.\n", char_id, p->name, save_status);
if (!errors)
memcpy(cp, p, sizeof(struct mmo_charstatus));
return 0;
}
-/// Saves an array of 'item' entries into the specified table.
+/**
+ * Saves an array of 'item' entries into the specified table.
+ *
+ * @param items The items array.
+ * @param max The array size.
+ * @param id The character/account/guild ID (depending on tableswitch).
+ * @param tableswitch The type of table (@see enum inventory_table_type).
+ * @return Error code.
+ * @retval 0 in case of success.
+ */
int char_memitemdata_to_sql(const struct item items[], int max, int id, int tableswitch)
{
StringBuf buf;
- SqlStmt* stmt;
- int i;
- int j;
- const char* tablename;
- const char* selectoption;
- struct item item; // temp storage variable
- bool* flag; // bit array for inventory matching
+ struct SqlStmt *stmt = NULL;
+ int i, j;
+ const char *tablename = NULL;
+ const char *selectoption = NULL;
+ bool has_favorite = false;
+ struct item item = { 0 }; // temp storage variable
+ bool *flag = NULL; // bit array for inventory matching
bool found;
int errors = 0;
+ nullpo_ret(items);
+
switch (tableswitch) {
- case TABLE_INVENTORY: tablename = inventory_db; selectoption = "char_id"; break;
+ case TABLE_INVENTORY: tablename = inventory_db; selectoption = "char_id"; has_favorite = true; break;
case TABLE_CART: tablename = cart_db; selectoption = "char_id"; break;
case TABLE_STORAGE: tablename = storage_db; selectoption = "account_id"; break;
case TABLE_GUILD_STORAGE: tablename = guild_storage_db; selectoption = "guild_id"; break;
default:
ShowError("Invalid table name!\n");
- return 1;
+ Assert_retr(1, tableswitch);
}
-
// The following code compares inventory with current database values
// and performs modification/deletion/insertion only on relevant rows.
// This approach is more complicated than a trivial delete&insert, but
// it significantly reduces cpu load on the database server.
StrBuf->Init(&buf);
- StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`");
- for( j = 0; j < MAX_SLOTS; ++j )
+ StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`");
+ for (j = 0; j < MAX_SLOTS; ++j)
StrBuf->Printf(&buf, ", `card%d`", j);
+ for (j = 0; j < MAX_ITEM_OPTIONS; ++j)
+ StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", j, j);
+
+ if (has_favorite)
+ StrBuf->AppendStr(&buf, ", `favorite`");
StrBuf->Printf(&buf, " FROM `%s` WHERE `%s`='%d'", tablename, selectoption, id);
stmt = SQL->StmtMalloc(inter->sql_handle);
- if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf))
- || SQL_ERROR == SQL->StmtExecute(stmt) )
- {
+ if (SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf))
+ || SQL_ERROR == SQL->StmtExecute(stmt)) {
SqlStmt_ShowDebug(stmt);
SQL->StmtFree(stmt);
StrBuf->Destroy(&buf);
return 1;
}
- memset(&item, 0, sizeof(item));
SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &item.id, 0, NULL, NULL);
SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &item.nameid, 0, NULL, NULL);
SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &item.amount, 0, NULL, NULL);
@@ -748,30 +764,39 @@ int char_memitemdata_to_sql(const struct item items[], int max, int id, int tabl
SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL);
SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL);
SQL->StmtBindColumn(stmt, 8, SQLDT_UCHAR, &item.bound, 0, NULL, NULL);
- for( j = 0; j < MAX_SLOTS; ++j )
- SQL->StmtBindColumn(stmt, 9+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
+ SQL->StmtBindColumn(stmt, 9, SQLDT_UINT64, &item.unique_id, 0, NULL, NULL);
+ for (j = 0; j < MAX_SLOTS; ++j)
+ SQL->StmtBindColumn(stmt, 10 + j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
+ for (j = 0; j < MAX_ITEM_OPTIONS; ++j) {
+ SQL->StmtBindColumn(stmt, 10 + MAX_SLOTS + j * 2, SQLDT_INT16, &item.option[j].index, 0, NULL, NULL);
+ SQL->StmtBindColumn(stmt, 11 + MAX_SLOTS + j * 2, SQLDT_INT16, &item.option[j].value, 0, NULL, NULL);
+ }
+ if (has_favorite)
+ SQL->StmtBindColumn(stmt, 10 + MAX_SLOTS + MAX_ITEM_OPTIONS * 2, SQLDT_UCHAR, &item.favorite, 0, NULL, NULL);
// bit array indicating which inventory items have already been matched
- flag = (bool*) aCalloc(max, sizeof(bool));
+ flag = aCalloc(max, sizeof(bool));
- while( SQL_SUCCESS == SQL->StmtNextRow(stmt) )
- {
+ while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) {
found = false;
// search for the presence of the item in the char's inventory
- for( i = 0; i < max; ++i )
- {
+ for (i = 0; i < max; ++i) {
// skip empty and already matched entries
- if( items[i].nameid == 0 || flag[i] )
+ if (items[i].nameid == 0 || flag[i])
continue;
- if( items[i].nameid == item.nameid
+ if (items[i].nameid == item.nameid
+ && items[i].unique_id == item.unique_id
&& items[i].card[0] == item.card[0]
&& items[i].card[2] == item.card[2]
&& items[i].card[3] == item.card[3]
) {
- //They are the same item.
- ARR_FIND( 0, MAX_SLOTS, j, items[i].card[j] != item.card[j] );
- if( j == MAX_SLOTS
+ int k = 0;
+ // They are the same item.
+ ARR_FIND(0, MAX_SLOTS, j, items[i].card[j] != item.card[j]);
+ ARR_FIND(0, MAX_ITEM_OPTIONS, k, items[i].option[k].index != item.option[k].index || items[i].option[k].value != item.option[k].value);
+
+ if (j == MAX_SLOTS && k == MAX_ITEM_OPTIONS
&& items[i].amount == item.amount
&& items[i].equip == item.equip
&& items[i].identify == item.identify
@@ -779,19 +804,23 @@ int char_memitemdata_to_sql(const struct item items[], int max, int id, int tabl
&& items[i].attribute == item.attribute
&& items[i].expire_time == item.expire_time
&& items[i].bound == item.bound
+ && (!has_favorite || items[i].favorite == item.favorite)
) {
; //Do nothing.
} else {
// update all fields.
StrBuf->Clear(&buf);
- StrBuf->Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `bound`='%d'",
+ StrBuf->Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%u', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `bound`='%d'",
tablename, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].bound);
- for( j = 0; j < MAX_SLOTS; ++j )
- StrBuf->Printf(&buf, ", `card%d`=%d", j, items[i].card[j]);
+ for (j = 0; j < MAX_SLOTS; ++j)
+ StrBuf->Printf(&buf, ", `card%d`='%d'", j, items[i].card[j]);
+ for (j = 0; j < MAX_ITEM_OPTIONS; ++j)
+ StrBuf->Printf(&buf, ", `opt_idx%d`='%d', `opt_val%d`='%d'", j, items[i].option[j].index, j, items[i].option[j].value);
+ if (has_favorite)
+ StrBuf->Printf(&buf, ", `favorite`='%d'", items[i].favorite);
StrBuf->Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id);
- if( SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) )
- {
+ if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) {
Sql_ShowDebug(inter->sql_handle);
errors++;
}
@@ -801,10 +830,9 @@ int char_memitemdata_to_sql(const struct item items[], int max, int id, int tabl
break; //skip to next item in the db.
}
}
- if( !found )
- {// Item not present in inventory, remove it.
- if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE from `%s` where `id`='%d' LIMIT 1", tablename, item.id) )
- {
+ if (!found) {
+ // Item not present in inventory, remove it.
+ if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE from `%s` where `id`='%d' LIMIT 1", tablename, item.id)) {
Sql_ShowDebug(inter->sql_handle);
errors++;
}
@@ -814,173 +842,38 @@ int char_memitemdata_to_sql(const struct item items[], int max, int id, int tabl
StrBuf->Clear(&buf);
StrBuf->Printf(&buf, "INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`", tablename, selectoption);
- for( j = 0; j < MAX_SLOTS; ++j )
+ for (j = 0; j < MAX_SLOTS; ++j)
StrBuf->Printf(&buf, ", `card%d`", j);
+ for (j = 0; j < MAX_ITEM_OPTIONS; ++j)
+ StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", j, j);
+ if (has_favorite)
+ StrBuf->AppendStr(&buf, ", `favorite`");
StrBuf->AppendStr(&buf, ") VALUES ");
found = false;
// insert non-matched items into the db as new items
- for( i = 0; i < max; ++i )
- {
+ for (i = 0; i < max; ++i) {
// skip empty and already matched entries
- if( items[i].nameid == 0 || flag[i] )
+ if (items[i].nameid == 0 || flag[i])
continue;
- if( found )
+ if (found)
StrBuf->AppendStr(&buf, ",");
else
found = true;
- StrBuf->Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'",
+ StrBuf->Printf(&buf, "('%d', '%d', '%d', '%u', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'",
id, items[i].nameid, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].bound, items[i].unique_id);
- for( j = 0; j < MAX_SLOTS; ++j )
+ for (j = 0; j < MAX_SLOTS; ++j)
StrBuf->Printf(&buf, ", '%d'", items[i].card[j]);
+ for (j = 0; j < MAX_ITEM_OPTIONS; ++j)
+ StrBuf->Printf(&buf, ", '%d', '%d'", items[i].option[j].index, items[i].option[j].value);
+ if (has_favorite)
+ StrBuf->Printf(&buf, ", '%d'", items[i].favorite);
StrBuf->AppendStr(&buf, ")");
}
- if( found && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) )
- {
- Sql_ShowDebug(inter->sql_handle);
- errors++;
- }
-
- StrBuf->Destroy(&buf);
- aFree(flag);
-
- return errors;
-}
-/* pretty much a copy of chr->memitemdata_to_sql except it handles inventory_db exclusively,
- * - this is required because inventory db is the only one with the 'favorite' column. */
-int char_inventory_to_sql(const struct item items[], int max, int id) {
- StringBuf buf;
- SqlStmt* stmt;
- int i;
- int j;
- struct item item; // temp storage variable
- bool* flag; // bit array for inventory matching
- bool found;
- int errors = 0;
-
- nullpo_ret(items);
-
- // The following code compares inventory with current database values
- // and performs modification/deletion/insertion only on relevant rows.
- // This approach is more complicated than a trivial delete&insert, but
- // it significantly reduces cpu load on the database server.
-
- StrBuf->Init(&buf);
- StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`");
- for( j = 0; j < MAX_SLOTS; ++j )
- StrBuf->Printf(&buf, ", `card%d`", j);
- StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`='%d'", inventory_db, id);
-
- stmt = SQL->StmtMalloc(inter->sql_handle);
- if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf))
- || SQL_ERROR == SQL->StmtExecute(stmt) )
- {
- SqlStmt_ShowDebug(stmt);
- SQL->StmtFree(stmt);
- StrBuf->Destroy(&buf);
- return 1;
- }
-
- memset(&item, 0, sizeof(item));
- SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &item.id, 0, NULL, NULL);
- SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &item.nameid, 0, NULL, NULL);
- SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &item.amount, 0, NULL, NULL);
- SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &item.equip, 0, NULL, NULL);
- SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &item.identify, 0, NULL, NULL);
- SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL);
- SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL);
- SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL);
- SQL->StmtBindColumn(stmt, 8, SQLDT_CHAR, &item.favorite, 0, NULL, NULL);
- SQL->StmtBindColumn(stmt, 9, SQLDT_UCHAR, &item.bound, 0, NULL, NULL);
- for( j = 0; j < MAX_SLOTS; ++j )
- SQL->StmtBindColumn(stmt, 10+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
-
- // bit array indicating which inventory items have already been matched
- flag = (bool*) aCalloc(max, sizeof(bool));
-
- while( SQL_SUCCESS == SQL->StmtNextRow(stmt) ) {
- found = false;
- // search for the presence of the item in the char's inventory
- for( i = 0; i < max; ++i ) {
- // skip empty and already matched entries
- if( items[i].nameid == 0 || flag[i] )
- continue;
-
- if( items[i].nameid == item.nameid
- && items[i].card[0] == item.card[0]
- && items[i].card[2] == item.card[2]
- && items[i].card[3] == item.card[3]
- ) {
- //They are the same item.
- ARR_FIND( 0, MAX_SLOTS, j, items[i].card[j] != item.card[j] );
- if( j == MAX_SLOTS
- && items[i].amount == item.amount
- && items[i].equip == item.equip
- && items[i].identify == item.identify
- && items[i].refine == item.refine
- && items[i].attribute == item.attribute
- && items[i].expire_time == item.expire_time
- && items[i].favorite == item.favorite
- && items[i].bound == item.bound
- ) {
- ; //Do nothing.
- } else {
- // update all fields.
- StrBuf->Clear(&buf);
- StrBuf->Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `favorite`='%d', `bound`='%d'",
- inventory_db, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite, items[i].bound);
- for( j = 0; j < MAX_SLOTS; ++j )
- StrBuf->Printf(&buf, ", `card%d`=%d", j, items[i].card[j]);
- StrBuf->Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id);
-
- if( SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) ) {
- Sql_ShowDebug(inter->sql_handle);
- errors++;
- }
- }
-
- found = flag[i] = true; //Item dealt with,
- break; //skip to next item in the db.
- }
- }
- if( !found ) {// Item not present in inventory, remove it.
- if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE from `%s` where `id`='%d' LIMIT 1", inventory_db, item.id) ) {
- Sql_ShowDebug(inter->sql_handle);
- errors++;
- }
- }
- }
- SQL->StmtFree(stmt);
-
- StrBuf->Clear(&buf);
- StrBuf->Printf(&buf, "INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`, `unique_id`", inventory_db);
- for( j = 0; j < MAX_SLOTS; ++j )
- StrBuf->Printf(&buf, ", `card%d`", j);
- StrBuf->AppendStr(&buf, ") VALUES ");
-
- found = false;
- // insert non-matched items into the db as new items
- for( i = 0; i < max; ++i ) {
- // skip empty and already matched entries
- if( items[i].nameid == 0 || flag[i] )
- continue;
-
- if( found )
- StrBuf->AppendStr(&buf, ",");
- else
- found = true;
-
- StrBuf->Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%d', '%d', '%"PRIu64"'",
- id, items[i].nameid, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite, items[i].bound, items[i].unique_id);
- for( j = 0; j < MAX_SLOTS; ++j )
- StrBuf->Printf(&buf, ", '%d'", items[i].card[j]);
- StrBuf->AppendStr(&buf, ")");
- }
-
- if( found && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf)) ) {
+ if (found && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) {
Sql_ShowDebug(inter->sql_handle);
errors++;
}
@@ -1044,7 +937,7 @@ int char_mmo_gender(const struct char_session_data *sd, const struct mmo_charsta
// Loads the basic character rooster for the given account. Returns total buffer used.
int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf)
{
- SqlStmt* stmt;
+ struct SqlStmt *stmt;
struct mmo_charstatus p;
int j = 0, i;
char last_map[MAP_NAME_LENGTH_EXT];
@@ -1078,9 +971,9 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p.char_id, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_UCHAR, &p.slot, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_STRING, &p.name, sizeof(p.name), NULL, NULL)
- || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_SHORT, &p.class_, 0, NULL, NULL)
- || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_UINT, &p.base_level, 0, NULL, NULL)
- || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_UINT, &p.job_level, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_SHORT, &p.class, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_INT, &p.base_level, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_INT, &p.job_level, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_UINT, &p.base_exp, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &p.job_exp, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_INT, &p.zeny, 0, NULL, NULL)
@@ -1094,8 +987,8 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 16, SQLDT_INT, &p.hp, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 17, SQLDT_INT, &p.max_sp, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 18, SQLDT_INT, &p.sp, 0, NULL, NULL)
- || SQL_ERROR == SQL->StmtBindColumn(stmt, 19, SQLDT_UINT, &p.status_point, 0, NULL, NULL)
- || SQL_ERROR == SQL->StmtBindColumn(stmt, 20, SQLDT_UINT, &p.skill_point, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 19, SQLDT_INT, &p.status_point, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 20, SQLDT_INT, &p.skill_point, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 21, SQLDT_UINT, &p.option, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 22, SQLDT_UCHAR, &p.karma, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 23, SQLDT_SHORT, &p.manner, 0, NULL, NULL)
@@ -1144,7 +1037,7 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every
char t_msg[128] = "";
struct mmo_charstatus* cp;
StringBuf buf;
- SqlStmt* stmt;
+ struct SqlStmt *stmt;
char last_map[MAP_NAME_LENGTH_EXT];
char save_map[MAP_NAME_LENGTH_EXT];
char point_map[MAP_NAME_LENGTH_EXT];
@@ -1164,7 +1057,8 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every
memset(p, 0, sizeof(struct mmo_charstatus));
- if (save_log) ShowInfo("Char load request (%d)\n", char_id);
+ if (chr->show_save_log)
+ ShowInfo("Char load request (%d)\n", char_id);
stmt = SQL->StmtMalloc(inter->sql_handle);
if( stmt == NULL )
@@ -1188,9 +1082,9 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &p->account_id, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_UCHAR, &p->slot, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_STRING, &p->name, sizeof(p->name), NULL, NULL)
- || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_SHORT, &p->class_, 0, NULL, NULL)
- || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_UINT, &p->base_level, 0, NULL, NULL)
- || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_UINT, &p->job_level, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_SHORT, &p->class, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_INT, &p->base_level, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_INT, &p->job_level, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &p->base_exp, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_UINT, &p->job_exp, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_INT, &p->zeny, 0, NULL, NULL)
@@ -1204,8 +1098,8 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 17, SQLDT_INT, &p->hp, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 18, SQLDT_INT, &p->max_sp, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 19, SQLDT_INT, &p->sp, 0, NULL, NULL)
- || SQL_ERROR == SQL->StmtBindColumn(stmt, 20, SQLDT_UINT, &p->status_point, 0, NULL, NULL)
- || SQL_ERROR == SQL->StmtBindColumn(stmt, 21, SQLDT_UINT, &p->skill_point, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 20, SQLDT_INT, &p->status_point, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 21, SQLDT_INT, &p->skill_point, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 22, SQLDT_UINT, &p->option, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 23, SQLDT_UCHAR, &p->karma, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 24, SQLDT_SHORT, &p->manner, 0, NULL, NULL)
@@ -1304,8 +1198,10 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every
//`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `expire_time`, `favorite`, `bound`, `unique_id`)
StrBuf->Init(&buf);
StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`, `unique_id`");
- for( i = 0; i < MAX_SLOTS; ++i )
+ for (i = 0; i < MAX_SLOTS; ++i)
StrBuf->Printf(&buf, ", `card%d`", i);
+ for (i = 0; i < MAX_ITEM_OPTIONS; ++i)
+ StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", i, i);
StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", inventory_db, MAX_INVENTORY);
memset(&tmp_item, 0, sizeof(tmp_item));
@@ -1325,8 +1221,14 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_UINT64, &tmp_item.unique_id, 0, NULL, NULL)
)
SqlStmt_ShowDebug(stmt);
- for( i = 0; i < MAX_SLOTS; ++i )
- if( SQL_ERROR == SQL->StmtBindColumn(stmt, 11+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
+ /* Card Slots */
+ for (i = 0; i < MAX_SLOTS; ++i)
+ if (SQL_ERROR == SQL->StmtBindColumn(stmt, 11 + i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL))
+ SqlStmt_ShowDebug(stmt);
+ /* Item Options */
+ for (i = 0; i < MAX_ITEM_OPTIONS; i++)
+ if (SQL_ERROR == SQL->StmtBindColumn(stmt, 11 + MAX_SLOTS + i * 2, SQLDT_INT16, &tmp_item.option[i].index, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 12 + MAX_SLOTS + i * 2, SQLDT_INT16, &tmp_item.option[i].value, 0, NULL, NULL))
SqlStmt_ShowDebug(stmt);
for( i = 0; i < MAX_INVENTORY && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i )
@@ -1338,8 +1240,10 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every
//`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, expire_time`, `bound`, `unique_id`)
StrBuf->Clear(&buf);
StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`");
- for( j = 0; j < MAX_SLOTS; ++j )
+ for (j = 0; j < MAX_SLOTS; ++j)
StrBuf->Printf(&buf, ", `card%d`", j);
+ for (j = 0; j < MAX_ITEM_OPTIONS; ++j)
+ StrBuf->Printf(&buf, ", `opt_idx%d`, `opt_val%d`", j, j);
StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", cart_db, MAX_CART);
memset(&tmp_item, 0, sizeof(tmp_item));
@@ -1359,18 +1263,21 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every
) {
SqlStmt_ShowDebug(stmt);
}
- for( i = 0; i < MAX_SLOTS; ++i )
- if( SQL_ERROR == SQL->StmtBindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
+ /* Card Slots */
+ for (i = 0; i < MAX_SLOTS; ++i)
+ if( SQL_ERROR == SQL->StmtBindColumn(stmt, 10 + i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
+ SqlStmt_ShowDebug(stmt);
+ /* Item Options */
+ for (i = 0; i < MAX_ITEM_OPTIONS; ++i)
+ if (SQL_ERROR == SQL->StmtBindColumn(stmt, 10 + MAX_SLOTS + i * 2, SQLDT_INT16, &tmp_item.option[i].index, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 11 + MAX_SLOTS + i * 2, SQLDT_INT16, &tmp_item.option[i].value, 0, NULL, NULL))
SqlStmt_ShowDebug(stmt);
for( i = 0; i < MAX_CART && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i )
memcpy(&p->cart[i], &tmp_item, sizeof(tmp_item));
+
strcat(t_msg, " cart");
- //read storage
- inter_storage->fromsql(p->account_id, &p->storage);
- strcat(t_msg, " storage");
-
//read skill
//`skill` (`char_id`, `id`, `lv`)
memset(&tmp_skill, 0, sizeof(tmp_skill));
@@ -1457,7 +1364,8 @@ int char_mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_every
if( SQL_SUCCESS == SQL->StmtNextRow(stmt) )
strcat(t_msg, " accdata");
- if (save_log) ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfully!
+ if (chr->show_save_log)
+ ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, t_msg); //ok. all data load successfully!
SQL->StmtFree(stmt);
StrBuf->Destroy(&buf);
@@ -1584,12 +1492,11 @@ int char_rename_char_sql(struct char_session_data *sd, int char_id)
memset(sd->new_name,0,sizeof(sd->new_name));
// log change
- if( log_char )
- {
- if( SQL_ERROR == SQL->Query(inter->sql_handle,
- "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)"
- "VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '0', '0', '0', '0', '0', '0', '0', '0')",
- charlog_db, "change char name", sd->account_id, char_dat.char_id, char_dat.slot, esc_name) )
+ if (chr->enable_logs) {
+ if (SQL_ERROR == SQL->Query(inter->sql_handle,
+ "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)"
+ "VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '0', '0', '0', '0', '0', '0', '0', '0')",
+ charlog_db, "change char name", sd->account_id, char_dat.char_id, char_dat.slot, esc_name))
Sql_ShowDebug(inter->sql_handle);
}
@@ -1660,10 +1567,11 @@ int char_check_char_name(char * name, char * esc_name)
* -5: 'Symbols in Character Names are forbidden'
* char_id: Success
**/
-int char_make_new_char_sql(struct char_session_data* sd, char* name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style) {
+int char_make_new_char_sql(struct char_session_data *sd, const char *name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style, int16 starting_class, uint8 sex)
+{
char name[NAME_LENGTH];
char esc_name[NAME_LENGTH*2+1];
- int char_id, flag, k, l;
+ int char_id, flag, i;
nullpo_retr(-2, sd);
nullpo_retr(-2, name_);
@@ -1672,9 +1580,17 @@ int char_make_new_char_sql(struct char_session_data* sd, char* name_, int str, i
SQL->EscapeStringLen(inter->sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
flag = chr->check_char_name(name,esc_name);
- if( flag < 0 )
+ if (flag < 0)
return flag;
+ switch (starting_class) {
+ case JOB_SUMMONER:
+ case JOB_NOVICE:
+ break;
+ default:
+ return -2; // Char Creation Denied
+ }
+
//check other inputs
#if PACKETVER >= 20120307
if(slot < 0 || slot >= sd->char_slots)
@@ -1694,24 +1610,24 @@ int char_make_new_char_sql(struct char_session_data* sd, char* name_, int str, i
if( sd->found_char[slot] != -1 )
return -2; /* character account limit exceeded */
+
#if PACKETVER >= 20120307
- //Insert the new char entry to the database
- if( SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`account_id`, `char_num`, `name`, `zeny`, `status_point`,`str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`,"
- "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`) VALUES ("
- "'%d', '%d', '%s', '%d', '%d','%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d')",
- char_db, sd->account_id , slot, esc_name, start_zeny, 48, str, agi, vit, int_, dex, luk,
+ // Insert the new char entry to the database
+ if (SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`account_id`, `char_num`, `name`, `class`, `zeny`, `status_point`,`str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`,"
+ "`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`, `sex`) VALUES ("
+ "'%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d', '%c')",
+ char_db, sd->account_id , slot, esc_name, starting_class, start_zeny, 48, str, agi, vit, int_, dex, luk,
(40 * (100 + vit)/100) , (40 * (100 + vit)/100 ), (11 * (100 + int_)/100), (11 * (100 + int_)/100), hair_style, hair_color,
- mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y) )
- {
- Sql_ShowDebug(inter->sql_handle);
- return -2; //No, stop the procedure!
+ mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y, sex)) {
+ Sql_ShowDebug(inter->sql_handle);
+ return -2; //No, stop the procedure!
}
#else
//Insert the new char entry to the database
- if( SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`account_id`, `char_num`, `name`, `zeny`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`,"
+ if( SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`account_id`, `char_num`, `name`, `class`, `zeny`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`,"
"`max_sp`, `sp`, `hair`, `hair_color`, `last_map`, `last_x`, `last_y`, `save_map`, `save_x`, `save_y`) VALUES ("
- "'%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d')",
- char_db, sd->account_id , slot, esc_name, start_zeny, str, agi, vit, int_, dex, luk,
+ "'%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d','%d', '%d','%d', '%d', '%s', '%d', '%d', '%s', '%d', '%d')",
+ char_db, sd->account_id , slot, esc_name, starting_class, start_zeny, str, agi, vit, int_, dex, luk,
(40 * (100 + vit)/100) , (40 * (100 + vit)/100 ), (11 * (100 + int_)/100), (11 * (100 + int_)/100), hair_style, hair_color,
mapindex_id2name(start_point.map), start_point.x, start_point.y, mapindex_id2name(start_point.map), start_point.x, start_point.y) )
{
@@ -1726,38 +1642,35 @@ int char_make_new_char_sql(struct char_session_data* sd, char* name_, int str, i
return -2;
// Validation success, log result
- if (log_char) {
- if( SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)"
- "VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
- charlog_db, "make new char", sd->account_id, char_id, slot, esc_name, str, agi, vit, int_, dex, luk, hair_style, hair_color) )
+ if (chr->enable_logs) {
+ if (SQL_ERROR == SQL->Query(inter->sql_handle,
+ "INSERT INTO `%s` (`time`, `char_msg`, `account_id`, `char_id`, `char_num`, `class`, `name`, `str`, `agi`, `vit`, `int`, `dex`, `luk`, `hair`, `hair_color`)"
+ "VALUES (NOW(), '%s', '%d', '%d', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
+ charlog_db, "make new char", sd->account_id, char_id, slot, starting_class, esc_name, str, agi, vit, int_, dex, luk, hair_style, hair_color))
Sql_ShowDebug(inter->sql_handle);
}
//Give the char the default items
- for (k = 0; k < ARRAYLENGTH(start_items) && start_items[k] != 0; k += 3) {
- // FIXME: How to define if an item is stackable without having to lookup itemdb? [panikon]
- if( start_items[k+2] == 1 )
- {
- if( SQL_ERROR == SQL->Query(inter->sql_handle,
- "INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `identify`) VALUES ('%d', '%d', '%d', '%d')",
- inventory_db, char_id, start_items[k], start_items[k + 1], 1) )
- Sql_ShowDebug(inter->sql_handle);
- }
- else if( start_items[k+2] == 0 )
- {
+ for (i = 0; i < VECTOR_LENGTH(start_items); i++) {
+ struct start_item_s *item = &VECTOR_INDEX(start_items, i);
+ if (item->stackable) {
+ if (SQL_ERROR == SQL->Query(inter->sql_handle,
+ "INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `identify`) VALUES ('%d', '%d', '%d', '%d')",
+ inventory_db, char_id, item->id, item->amount, 1))
+ Sql_ShowDebug(inter->sql_handle);
+ } else {
// Non-stackable items should have their own entries (issue: 7279)
- for( l = 0; l < start_items[k+1]; l++ )
- {
- if( SQL_ERROR == SQL->Query(inter->sql_handle,
- "INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `identify`) VALUES ('%d', '%d', '%d', '%d')",
- inventory_db, char_id, start_items[k], 1, 1)
- )
+ int l, loc = item->loc;
+ for (l = 0; l < item->amount; l++) {
+ if (SQL_ERROR == SQL->Query(inter->sql_handle,
+ "INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `equip`, `identify`) VALUES ('%d', '%d', '%d', '%d', '%d')",
+ inventory_db, char_id, item->id, 1, loc, 1))
Sql_ShowDebug(inter->sql_handle);
}
}
}
- ShowInfo("Created char: account: %d, char: %d, slot: %d, name: %s\n", sd->account_id, char_id, slot, name);
+ ShowInfo("Created char: account: %d, char: %d, slot: %d, name: %s, sex: %c\n", sd->account_id, char_id, slot, name, sex);
return char_id;
}
@@ -1924,13 +1837,13 @@ int char_delete_char_sql(int char_id)
#endif
/* delete character */
- if( SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", char_db, char_id) )
+ if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", char_db, char_id)) {
Sql_ShowDebug(inter->sql_handle);
- else if( log_char ) {
- if( SQL_ERROR == SQL->Query(inter->sql_handle,
- "INSERT INTO `%s`(`time`, `account_id`, `char_id`, `char_num`, `char_msg`, `name`)"
- " VALUES (NOW(), '%d', '%d', '%d', 'Deleted character', '%s')",
- charlog_db, account_id, char_id, 0, esc_name) )
+ } else if (chr->enable_logs) {
+ if (SQL_ERROR == SQL->Query(inter->sql_handle,
+ "INSERT INTO `%s`(`time`, `account_id`, `char_id`, `char_num`, `char_msg`, `name`)"
+ " VALUES (NOW(), '%d', '%d', '%d', 'Deleted character', '%s')",
+ charlog_db, account_id, char_id, 0, esc_name))
Sql_ShowDebug(inter->sql_handle);
}
@@ -2000,7 +1913,7 @@ int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) {
WBUFW(buf,46) = min(p->sp, INT16_MAX);
WBUFW(buf,48) = min(p->max_sp, INT16_MAX);
WBUFW(buf,50) = DEFAULT_WALK_SPEED; // p->speed;
- WBUFW(buf,52) = p->class_;
+ WBUFW(buf,52) = p->class;
WBUFW(buf,54) = p->hair;
#if PACKETVER >= 20141022
WBUFW(buf,56) = p->body;
@@ -2010,7 +1923,7 @@ int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) {
//When the weapon is sent and your option is riding, the client crashes on login!?
// FIXME[Haru]: is OPTION_HANBOK intended to be part of this list? And if it is, should the list also include other OPTION_ costumes?
- WBUFW(buf,56) = p->option&(OPTION_RIDING|OPTION_DRAGON|OPTION_WUG|OPTION_WUGRIDER|OPTION_MADOGEAR|OPTION_HANBOK) ? 0 : p->weapon;
+ WBUFW(buf,56) = (p->option&(OPTION_RIDING|OPTION_DRAGON|OPTION_WUG|OPTION_WUGRIDER|OPTION_MADOGEAR|OPTION_HANBOK)) ? 0 : p->weapon;
WBUFW(buf,58) = p->base_level;
WBUFW(buf,60) = min(p->skill_point, INT16_MAX);
@@ -2033,7 +1946,7 @@ int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) {
offset += 2;
#endif
#if (PACKETVER >= 20100720 && PACKETVER <= 20100727) || PACKETVER >= 20100803
- mapindex->getmapname_ext(mapindex_id2name(p->last_point.map), (char*)WBUFP(buf,108));
+ mapindex->getmapname_ext(mapindex_id2name(p->last_point.map), WBUFP(buf,108));
offset += MAP_NAME_LENGTH_EXT;
#endif
#if PACKETVER >= 20100803
@@ -2063,11 +1976,15 @@ int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) {
}
/* Made Possible by Yommy~! <3 */
-void char_mmo_char_send099d(int fd, struct char_session_data *sd) {
+void char_mmo_char_send099d(int fd, struct char_session_data *sd)
+{
+// support added for client between 20121010 and 20130320
+#if PACKETVER > 20120418
WFIFOHEAD(fd,4 + (MAX_CHARS*MAX_CHAR_BUF));
WFIFOW(fd,0) = 0x99d;
WFIFOW(fd,2) = chr->mmo_chars_fromsql(sd, WFIFOP(fd,4)) + 4;
WFIFOSET(fd,WFIFOW(fd,2));
+#endif
}
/* Sends character ban list */
@@ -2087,7 +2004,7 @@ void char_mmo_char_send_ban_list(int fd, struct char_session_data *sd) {
for(i = 0, c = 0; i < MAX_CHARS; i++) {
if( sd->unban_time[i] ) {
- timestamp2string((char*)WFIFOP(fd,8 + (28*c)), 20, sd->unban_time[i], "%Y-%m-%d %H:%M:%S");
+ timestamp2string(WFIFOP(fd,8 + (28*c)), 20, sd->unban_time[i], "%Y-%m-%d %H:%M:%S");
if( sd->unban_time[i] > now )
WFIFOL(fd, 4 + (24*c)) = sd->found_char[i];
@@ -2135,7 +2052,7 @@ int char_mmo_char_send_characters(int fd, struct char_session_data* sd)
#if PACKETVER >= 20100413
offset += 3;
#endif
- if (save_log)
+ if (chr->show_save_log)
ShowInfo("Loading Char Data ("CL_BOLD"%d"CL_RESET")\n",sd->account_id);
j = 24 + offset; // offset
@@ -2301,7 +2218,7 @@ int char_parse_fromlogin_connection_state(int fd)
ShowError("Can not connect to login-server.\n");
ShowError("The server communication passwords (default s1/p1) are probably invalid.\n");
ShowError("Also, please make sure your login db has the correct communication username/passwords and the gender of the account is S.\n");
- ShowError("The communication passwords are set in /conf/map-server.conf and /conf/char-server.conf\n");
+ ShowError("The communication passwords are set in /conf/map-server.conf and /conf/char/char-server.conf\n");
sockt->eof(fd);
return 1;
} else {
@@ -2380,8 +2297,8 @@ void char_parse_fromlogin_account_data(int fd)
sd->char_slots = MAX_CHARS;/* cap to maximum */
} else if ( sd->char_slots <= 0 )/* no value aka 0 in sql */
sd->char_slots = MAX_CHARS;/* cap to maximum */
- safestrncpy(sd->birthdate, (const char*)RFIFOP(fd,52), sizeof(sd->birthdate));
- safestrncpy(sd->pincode, (const char*)RFIFOP(fd,63), sizeof(sd->pincode));
+ safestrncpy(sd->birthdate, RFIFOP(fd,52), sizeof(sd->birthdate));
+ safestrncpy(sd->pincode, RFIFOP(fd,63), sizeof(sd->pincode));
sd->pincode_change = RFIFOL(fd,68);
// continued from chr->auth_ok...
if( (max_connect_user == 0 && sd->group_id != gm_allow_group) ||
@@ -2432,33 +2349,33 @@ void char_changesex(int account_id, int sex)
* @param sex The new sex (SEX_MALE or SEX_FEMALE).
* @param acc The character's account ID.
* @param char_id The character ID.
- * @param class_ The character's current job class.
+ * @param class The character's current job class.
* @param guild_id The character's guild ID.
*/
-void char_change_sex_sub(int sex, int acc, int char_id, int class_, int guild_id)
+void char_change_sex_sub(int sex, int acc, int char_id, int class, int guild_id)
{
// job modification
- if (class_ == JOB_BARD || class_ == JOB_DANCER)
- class_ = (sex == SEX_MALE ? JOB_BARD : JOB_DANCER);
- else if (class_ == JOB_CLOWN || class_ == JOB_GYPSY)
- class_ = (sex == SEX_MALE ? JOB_CLOWN : JOB_GYPSY);
- else if (class_ == JOB_BABY_BARD || class_ == JOB_BABY_DANCER)
- class_ = (sex == SEX_MALE ? JOB_BABY_BARD : JOB_BABY_DANCER);
- else if (class_ == JOB_MINSTREL || class_ == JOB_WANDERER)
- class_ = (sex == SEX_MALE ? JOB_MINSTREL : JOB_WANDERER);
- else if (class_ == JOB_MINSTREL_T || class_ == JOB_WANDERER_T)
- class_ = (sex == SEX_MALE ? JOB_MINSTREL_T : JOB_WANDERER_T);
- else if (class_ == JOB_BABY_MINSTREL || class_ == JOB_BABY_WANDERER)
- class_ = (sex == SEX_MALE ? JOB_BABY_MINSTREL : JOB_BABY_WANDERER);
- else if (class_ == JOB_KAGEROU || class_ == JOB_OBORO)
- class_ = (sex == SEX_MALE ? JOB_KAGEROU : JOB_OBORO);
+ if (class == JOB_BARD || class == JOB_DANCER)
+ class = (sex == SEX_MALE ? JOB_BARD : JOB_DANCER);
+ else if (class == JOB_CLOWN || class == JOB_GYPSY)
+ class = (sex == SEX_MALE ? JOB_CLOWN : JOB_GYPSY);
+ else if (class == JOB_BABY_BARD || class == JOB_BABY_DANCER)
+ class = (sex == SEX_MALE ? JOB_BABY_BARD : JOB_BABY_DANCER);
+ else if (class == JOB_MINSTREL || class == JOB_WANDERER)
+ class = (sex == SEX_MALE ? JOB_MINSTREL : JOB_WANDERER);
+ else if (class == JOB_MINSTREL_T || class == JOB_WANDERER_T)
+ class = (sex == SEX_MALE ? JOB_MINSTREL_T : JOB_WANDERER_T);
+ else if (class == JOB_BABY_MINSTREL || class == JOB_BABY_WANDERER)
+ class = (sex == SEX_MALE ? JOB_BABY_MINSTREL : JOB_BABY_WANDERER);
+ else if (class == JOB_KAGEROU || class == JOB_OBORO)
+ class = (sex == SEX_MALE ? JOB_KAGEROU : JOB_OBORO);
if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `equip`='0' WHERE `char_id`='%d'", inventory_db, char_id))
Sql_ShowDebug(inter->sql_handle);
if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `class`='%d', `weapon`='0', `shield`='0', "
"`head_top`='0', `head_mid`='0', `head_bottom`='0' WHERE `char_id`='%d'",
- char_db, class_, char_id))
+ char_db, class, char_id))
Sql_ShowDebug(inter->sql_handle);
if (guild_id) // If there is a guild, update the guild_member data [Skotlex]
inter_guild->sex_changed(guild_id, acc, char_id, sex);
@@ -2466,10 +2383,10 @@ void char_change_sex_sub(int sex, int acc, int char_id, int class_, int guild_id
int char_parse_fromlogin_changesex_reply(int fd)
{
- int char_id = 0, class_ = 0, guild_id = 0;
+ int char_id = 0, class = 0, guild_id = 0;
int i;
struct char_auth_node *node;
- SqlStmt *stmt;
+ struct SqlStmt *stmt;
int acc = RFIFOL(fd,2);
int sex = RFIFOB(fd,6);
@@ -2495,11 +2412,11 @@ int char_parse_fromlogin_changesex_reply(int fd)
SQL->StmtFree(stmt);
}
SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &char_id, 0, NULL, NULL);
- SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &class_, 0, NULL, NULL);
+ SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &class, 0, NULL, NULL);
SQL->StmtBindColumn(stmt, 2, SQLDT_INT, &guild_id, 0, NULL, NULL);
for (i = 0; i < MAX_CHARS && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i) {
- char_change_sex_sub(sex, acc, char_id, class_, guild_id);
+ char_change_sex_sub(sex, acc, char_id, class, guild_id);
}
SQL->StmtFree(stmt);
@@ -2608,9 +2525,8 @@ void char_parse_fromlogin_accinfo2_failed(int fd)
void char_parse_fromlogin_accinfo2_ok(int fd)
{
mapif->parse_accinfo2(true, RFIFOL(fd,167), RFIFOL(fd,171), RFIFOL(fd,175), RFIFOL(fd,179),
- (char*)RFIFOP(fd,2), (char*)RFIFOP(fd,26), (char*)RFIFOP(fd,59),
- (char*)RFIFOP(fd,99), (char*)RFIFOP(fd,119), (char*)RFIFOP(fd,151),
- (char*)RFIFOP(fd,156), RFIFOL(fd,115), RFIFOL(fd,143), RFIFOL(fd,147));
+ RFIFOP(fd,2), RFIFOP(fd,26), RFIFOP(fd,59), RFIFOP(fd,99), RFIFOP(fd,119),
+ RFIFOP(fd,151), RFIFOP(fd,156), RFIFOL(fd,115), RFIFOL(fd,143), RFIFOL(fd,147));
RFIFOSKIP(fd,183);
}
@@ -2788,7 +2704,7 @@ void char_global_accreg_to_login_add (const char *key, unsigned int index, intpt
WFIFOB(chr->login_fd, nlen) = (unsigned char)len;/* won't be higher; the column size is 32 */
nlen += 1;
- safestrncpy((char*)WFIFOP(chr->login_fd,nlen), key, len);
+ safestrncpy(WFIFOP(chr->login_fd,nlen), key, len);
nlen += len;
WFIFOL(chr->login_fd, nlen) = index;
@@ -2805,7 +2721,7 @@ void char_global_accreg_to_login_add (const char *key, unsigned int index, intpt
WFIFOB(chr->login_fd, nlen) = (unsigned char)len;/* won't be higher; the column size is 254 */
nlen += 1;
- safestrncpy((char*)WFIFOP(chr->login_fd,nlen), sval, len);
+ safestrncpy(WFIFOP(chr->login_fd,nlen), sval, len);
nlen += len;
}
} else {
@@ -3096,7 +3012,7 @@ void char_parse_frommap_map_names(int fd, int id)
VECTOR_PUSH(chr->server[id].maps, RFIFOW(fd,i));
}
- ShowStatus("Map-Server %d connected: %d maps, from IP %d.%d.%d.%d port %d.\n",
+ ShowStatus("Map-Server %d connected: %d maps, from IP %u.%u.%u.%u port %d.\n",
id, (int)VECTOR_LENGTH(chr->server[id].maps), CONVIP(chr->server[id].ip), chr->server[id].port);
ShowStatus("Map-server %d loading complete.\n", id);
@@ -3292,7 +3208,7 @@ void char_parse_frommap_char_select_req(int fd)
}
}
-void char_change_map_server_ack(int fd, uint8 *data, bool ok)
+void char_change_map_server_ack(int fd, const uint8 *data, bool ok)
{
WFIFOHEAD(fd,30);
WFIFOW(fd,0) = 0x2b06;
@@ -3371,7 +3287,7 @@ void char_char_name_ack(int fd, int char_id)
WFIFOHEAD(fd,30);
WFIFOW(fd,0) = 0x2b09;
WFIFOL(fd,2) = char_id;
- chr->loadName(char_id, (char*)WFIFOP(fd,6));
+ chr->loadName(char_id, WFIFOP(fd,6));
WFIFOSET(fd,30);
}
@@ -3406,7 +3322,7 @@ void char_ban(int account_id, int char_id, time_t *unban_time, short year, short
{
time_t timestamp;
struct tm *tmtime;
- SqlStmt* stmt = SQL->StmtMalloc(inter->sql_handle);
+ struct SqlStmt *stmt = SQL->StmtMalloc(inter->sql_handle);
nullpo_retv(unban_time);
@@ -3427,8 +3343,8 @@ void char_ban(int account_id, int char_id, time_t *unban_time, short year, short
if( SQL_SUCCESS != SQL->StmtPrepare(stmt,
"UPDATE `%s` SET `unban_time` = ? WHERE `char_id` = ? LIMIT 1",
char_db)
- || SQL_SUCCESS != SQL->StmtBindParam(stmt, 0, SQLDT_LONG, (void*)&timestamp, sizeof(timestamp))
- || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_INT, (void*)&char_id, sizeof(char_id))
+ || SQL_SUCCESS != SQL->StmtBindParam(stmt, 0, SQLDT_LONG, &timestamp, sizeof(timestamp))
+ || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_INT, &char_id, sizeof(char_id))
|| SQL_SUCCESS != SQL->StmtExecute(stmt)
) {
SqlStmt_ShowDebug(stmt);
@@ -3460,7 +3376,7 @@ void char_ask_name_ack(int fd, int acc, const char* name, int type, int result)
WFIFOHEAD(fd,34);
WFIFOW(fd, 0) = 0x2b0f;
WFIFOL(fd, 2) = acc;
- safestrncpy((char*)WFIFOP(fd,6), name, NAME_LENGTH);
+ safestrncpy(WFIFOP(fd,6), name, NAME_LENGTH);
WFIFOW(fd,30) = type;
WFIFOW(fd,32) = result;
WFIFOSET(fd,34);
@@ -3478,7 +3394,7 @@ void char_ask_name_ack(int fd, int acc, const char* name, int type, int result)
*/
int char_changecharsex(int char_id, int sex)
{
- int class_ = 0, guild_id = 0, account_id = 0;
+ int class = 0, guild_id = 0, account_id = 0;
char *data;
// get character data
@@ -3491,7 +3407,7 @@ int char_changecharsex(int char_id, int sex)
return 1;
}
SQL->GetData(inter->sql_handle, 0, &data, NULL); account_id = atoi(data);
- SQL->GetData(inter->sql_handle, 1, &data, NULL); class_ = atoi(data);
+ SQL->GetData(inter->sql_handle, 1, &data, NULL); class = atoi(data);
SQL->GetData(inter->sql_handle, 2, &data, NULL); guild_id = atoi(data);
SQL->FreeResult(inter->sql_handle);
@@ -3499,7 +3415,7 @@ int char_changecharsex(int char_id, int sex)
Sql_ShowDebug(inter->sql_handle);
return 1;
}
- char_change_sex_sub(sex, account_id, char_id, class_, guild_id);
+ char_change_sex_sub(sex, account_id, char_id, class, guild_id);
// disconnect player if online on char-server
chr->disconnect_player(account_id);
@@ -3515,7 +3431,7 @@ void char_parse_frommap_change_account(int fd)
char esc_name[NAME_LENGTH*2+1];
int acc = RFIFOL(fd,2); // account_id of who ask (-1 if server itself made this request)
- const char* name = (char*)RFIFOP(fd,6); // name of the target character
+ const char *name = RFIFOP(fd,6); // name of the target character
int type = RFIFOW(fd,30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5 changesex, 6 charban, 7 charunban
short year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
int sex = SEX_MALE;
@@ -3658,7 +3574,7 @@ void char_parse_frommap_ragsrvinfo(int fd)
SQL->EscapeString(inter->sql_handle, esc_server_name, chr->server_name);
- if( SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` SET `index`='%d',`name`='%s',`exp`='%d',`jexp`='%d',`drop`='%d'",
+ if( SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` SET `index`='%d',`name`='%s',`exp`='%u',`jexp`='%u',`drop`='%u'",
ragsrvinfo_db, fd, esc_server_name, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)) )
{
Sql_ShowDebug(inter->sql_handle);
@@ -3834,7 +3750,7 @@ void char_parse_frommap_auth_request(int fd, int id)
void char_parse_frommap_update_ip(int fd, int id)
{
chr->server[id].ip = ntohl(RFIFOL(fd, 2));
- ShowInfo("Updated IP address of map-server #%d to %d.%d.%d.%d.\n", id, CONVIP(chr->server[id].ip));
+ ShowInfo("Updated IP address of map-server #%d to %u.%u.%u.%u.\n", id, CONVIP(chr->server[id].ip));
RFIFOSKIP(fd,6);
}
@@ -3858,17 +3774,13 @@ void char_parse_frommap_request_stats_report(int fd)
WFIFOHEAD(sfd, RFIFOW(fd,2) );
- memcpy((char*)WFIFOP(sfd,0), (char*)RFIFOP(fd, 0), RFIFOW(fd,2));
+ memcpy(WFIFOP(sfd,0), RFIFOP(fd, 0), RFIFOW(fd,2));
WFIFOSET(sfd, RFIFOW(fd,2) );
do {
sockt->flush(sfd);
-#ifdef WIN32
- Sleep(1);
-#else
- sleep(1);
-#endif
+ HSleep(1);
} while( !sockt->session[sfd]->flag.eof && sockt->session[sfd]->wdata_size );
sockt->close(sfd);
@@ -3887,9 +3799,11 @@ void char_parse_frommap_scdata_update(int fd)
int val4 = RFIFOL(fd, 24);
short type = RFIFOW(fd, 10);
- if( SQL_ERROR == SQL->Query(inter->sql_handle, "REPLACE INTO `%s` (`account_id`,`char_id`,`type`,`tick`,`val1`,`val2`,`val3`,`val4`) VALUES ('%d','%d','%d',-1,'%d','%d','%d','%d')",
- scdata_db, account_id, char_id, type, val1, val2, val3, val4) )
- {
+ if (SQL_ERROR == SQL->Query(inter->sql_handle, "REPLACE INTO `%s`"
+ " (`account_id`,`char_id`,`type`,`tick`,`val1`,`val2`,`val3`,`val4`)"
+ " VALUES ('%d','%d','%d','%d','%d','%d','%d','%d')",
+ scdata_db, account_id, char_id, type, INFINITE_DURATION, val1, val2, val3, val4)
+ ) {
Sql_ShowDebug(inter->sql_handle);
}
RFIFOSKIP(fd, 28);
@@ -3930,8 +3844,11 @@ int char_parse_frommap(int fd)
int packet_id = RFIFOW(fd,0);
if (VECTOR_LENGTH(HPM->packets[hpParse_FromMap]) > 0) {
int result = HPM->parse_packets(fd,packet_id,hpParse_FromMap);
- if (result == 1)
+ if (result == 1) {
+ if (sockt->session[fd] == NULL)
+ return 0;
continue;
+ }
if (result == 2)
return 0;
}
@@ -4345,7 +4262,7 @@ static void char_delete2_accept(int fd, struct char_session_data* sd)
{// CH: <0829>.W <char id>.L <birth date:YYMMDD>.6B
char birthdate[8+1];
int char_id, i;
- unsigned int base_level;
+ int base_level;
char* data;
time_t delete_date;
@@ -4379,7 +4296,7 @@ static void char_delete2_accept(int fd, struct char_session_data* sd)
return;
}
- SQL->GetData(inter->sql_handle, 0, &data, NULL); base_level = (unsigned int)strtoul(data, NULL, 10);
+ SQL->GetData(inter->sql_handle, 0, &data, NULL); base_level = atoi(data);
SQL->GetData(inter->sql_handle, 1, &data, NULL); delete_date = strtoul(data, NULL, 10);
if( !delete_date || delete_date>time(NULL) )
@@ -4394,8 +4311,8 @@ static void char_delete2_accept(int fd, struct char_session_data* sd)
return;
}
- if( ( char_del_level > 0 && base_level >= (unsigned int)char_del_level ) || ( char_del_level < 0 && base_level <= (unsigned int)(-char_del_level) ) )
- {// character level config restriction
+ if ((char_del_level > 0 && base_level >= char_del_level) || (char_del_level < 0 && base_level <= -char_del_level)) {
+ // character level config restriction
chr->delete2_accept_ack(fd, char_id, 2); // 2: Due to system settings can not be deleted
return;
}
@@ -4457,7 +4374,7 @@ void char_parse_char_connect(int fd, struct char_session_data* sd, uint32 ipl)
RFIFOSKIP(fd,17);
- ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", account_id, login_id1, login_id2);
+ ShowInfo("request connect - account_id:%d/login_id1:%u/login_id2:%u\n", account_id, login_id1, login_id2);
if (sd) {
//Received again auth packet for already authenticated account?? Discard it.
@@ -4515,14 +4432,21 @@ void char_parse_char_connect(int fd, struct char_session_data* sd, uint32 ipl)
void char_send_map_info(int fd, int i, uint32 subnet_map_ip, struct mmo_charstatus *cd)
{
+#if PACKETVER < 20170329
+ const int cmd = 0x71;
+ const int len = 28;
+#else
+ const int cmd = 0xac5;
+ const int len = 156;
+#endif
nullpo_retv(cd);
- WFIFOHEAD(fd,28);
- WFIFOW(fd,0) = 0x71;
- WFIFOL(fd,2) = cd->char_id;
- mapindex->getmapname_ext(mapindex_id2name(cd->last_point.map), (char*)WFIFOP(fd,6));
- WFIFOL(fd,22) = htonl((subnet_map_ip) ? subnet_map_ip : chr->server[i].ip);
- WFIFOW(fd,26) = sockt->ntows(htons(chr->server[i].port)); // [!] LE byte order here [!]
- WFIFOSET(fd,28);
+ WFIFOHEAD(fd, len);
+ WFIFOW(fd, 0) = cmd;
+ WFIFOL(fd, 2) = cd->char_id;
+ mapindex->getmapname_ext(mapindex_id2name(cd->last_point.map), WFIFOP(fd, 6));
+ WFIFOL(fd, 22) = htonl((subnet_map_ip) ? subnet_map_ip : chr->server[i].ip);
+ WFIFOW(fd, 26) = sockt->ntows(htons(chr->server[i].port)); // [!] LE byte order here [!]
+ WFIFOSET(fd, len);
}
void char_send_wait_char_server(int fd)
@@ -4530,7 +4454,7 @@ void char_send_wait_char_server(int fd)
WFIFOHEAD(fd, 24);
WFIFOW(fd, 0) = 0x840;
WFIFOW(fd, 2) = 24;
- safestrncpy((char*)WFIFOP(fd,4), "0", 20);/* we can't send empty (otherwise the list will pop up) */
+ safestrncpy(WFIFOP(fd,4), "0", 20);/* we can't send empty (otherwise the list will pop up) */
WFIFOSET(fd, 24);
}
@@ -4636,13 +4560,13 @@ void char_parse_char_select(int fd, struct char_session_data* sd, uint32 ipl)
if( cd->sex == 99 )
cd->sex = sd->sex;
- if (log_char) {
+ if (chr->enable_logs) {
char esc_name[NAME_LENGTH*2+1];
// FIXME: Why are we re-escaping the name if it was already escaped in rename/make_new_char? [Panikon]
SQL->EscapeStringLen(inter->sql_handle, esc_name, char_dat.name, strnlen(char_dat.name, NAME_LENGTH));
- if( SQL_ERROR == SQL->Query(inter->sql_handle,
- "INSERT INTO `%s`(`time`, `account_id`, `char_id`, `char_num`, `name`) VALUES (NOW(), '%d', '%d', '%d', '%s')",
- charlog_db, sd->account_id, cd->char_id, slot, esc_name) )
+ if (SQL_ERROR == SQL->Query(inter->sql_handle,
+ "INSERT INTO `%s`(`time`, `account_id`, `char_id`, `char_num`, `name`) VALUES (NOW(), '%d', '%d', '%d', '%s')",
+ charlog_db, sd->account_id, cd->char_id, slot, esc_name))
Sql_ShowDebug(inter->sql_handle);
}
ShowInfo("Selected char: (Account %d: %d - %s)\n", sd->account_id, slot, char_dat.name);
@@ -4735,15 +4659,31 @@ void char_parse_char_create_new_char(int fd, struct char_session_data* sd) __att
void char_parse_char_create_new_char(int fd, struct char_session_data* sd)
{
int result;
- if( !char_new ) {
+ if (!enable_char_creation) {
//turn character creation on/off [Kevin]
result = -2;
} else {
- #if PACKETVER >= 20120307
- result = chr->make_new_char_sql(sd, (char*)RFIFOP(fd,2), 1, 1, 1, 1, 1, 1, RFIFOB(fd,26),RFIFOW(fd,27),RFIFOW(fd,29));
- #else
- result = chr->make_new_char_sql(sd, (char*)RFIFOP(fd,2),RFIFOB(fd,26),RFIFOB(fd,27),RFIFOB(fd,28),RFIFOB(fd,29),RFIFOB(fd,30),RFIFOB(fd,31),RFIFOB(fd,32),RFIFOW(fd,33),RFIFOW(fd,35));
- #endif
+#if PACKETVER >= 20151001
+ uint8 sex = RFIFOB(fd, 35);
+
+ switch (sex) {
+ case SEX_FEMALE:
+ sex = 'F';
+ break;
+ case SEX_MALE:
+ sex = 'M';
+ break;
+ default:
+ chr->creation_failed(fd, -2); // Char Creation Denied
+ RFIFOSKIP(fd, 36);
+ return;
+ }
+ result = chr->make_new_char_sql(sd, RFIFOP(fd, 2), 1, 1, 1, 1, 1, 1, RFIFOB(fd, 26), RFIFOW(fd, 27), RFIFOW(fd, 29), RFIFOW(fd, 31), sex);
+#elif PACKETVER >= 20120307
+ result = chr->make_new_char_sql(sd, RFIFOP(fd, 2), 1, 1, 1, 1, 1, 1, RFIFOB(fd, 26), RFIFOW(fd, 27), RFIFOW(fd, 29), JOB_NOVICE, 'U');
+#else
+ result = chr->make_new_char_sql(sd, RFIFOP(fd, 2), RFIFOB(fd, 26), RFIFOB(fd, 27), RFIFOB(fd, 28), RFIFOB(fd, 29), RFIFOB(fd, 30), RFIFOB(fd, 31), RFIFOB(fd, 32), RFIFOW(fd, 33), RFIFOW(fd, 35), JOB_NOVICE, 'U');
+#endif
}
//'Charname already exists' (-1), 'Char creation denied' (-2) and 'You are underaged' (-3)
@@ -4758,11 +4698,13 @@ void char_parse_char_create_new_char(int fd, struct char_session_data* sd)
// add new entry to the chars list
sd->found_char[char_dat.slot] = result; // the char_id of the new char
}
- #if PACKETVER >= 20120307
- RFIFOSKIP(fd,31);
- #else
- RFIFOSKIP(fd,37);
- #endif
+#if PACKETVER >= 20151001
+ RFIFOSKIP(fd, 36);
+#elif PACKETVER >= 20120307
+ RFIFOSKIP(fd, 31);
+#else
+ RFIFOSKIP(fd, 37);
+#endif
}
// flag:
@@ -4856,7 +4798,7 @@ void char_parse_char_rename_char(int fd, struct char_session_data* sd)
int i, cid =RFIFOL(fd,2);
char name[NAME_LENGTH];
char esc_name[NAME_LENGTH*2+1];
- safestrncpy(name, (char *)RFIFOP(fd,6), NAME_LENGTH);
+ safestrncpy(name, RFIFOP(fd,6), NAME_LENGTH);
RFIFOSKIP(fd,30);
ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
@@ -4881,7 +4823,7 @@ void char_parse_char_rename_char2(int fd, struct char_session_data* sd)
int i, aid = RFIFOL(fd,2), cid =RFIFOL(fd,6);
char name[NAME_LENGTH];
char esc_name[NAME_LENGTH*2+1];
- safestrncpy(name, (char *)RFIFOP(fd,10), NAME_LENGTH);
+ safestrncpy(name, RFIFOP(fd,10), NAME_LENGTH);
RFIFOSKIP(fd,34);
if( aid != sd->account_id )
@@ -4978,11 +4920,10 @@ void char_login_map_server_ack(int fd, uint8 flag)
void char_parse_char_login_map_server(int fd, uint32 ipl)
{
- char* l_user = (char*)RFIFOP(fd,2);
- char* l_pass = (char*)RFIFOP(fd,26);
+ char l_user[24], l_pass[24];
int i;
- l_user[23] = '\0';
- l_pass[23] = '\0';
+ safestrncpy(l_user, RFIFOP(fd,2), 24);
+ safestrncpy(l_pass, RFIFOP(fd,26), 24);
ARR_FIND( 0, ARRAYLENGTH(chr->server), i, chr->server[i].fd <= 0 );
if (core->runflag != CHARSERVER_ST_RUNNING ||
@@ -5141,17 +5082,22 @@ int char_parse_char(int fd)
break;
// create new char
- #if PACKETVER >= 20120307
+#if PACKETVER >= 20151001
+ // S 0a39 <name>.24B <slot>.B <hair color>.W <hair style>.W <starting job class ID>.W <Unknown>.(W or 2 B's)??? <sex>.B
+ case 0xa39:
+ {
+ FIFOSD_CHECK(36);
+#elif PACKETVER >= 20120307
// S 0970 <name>.24B <slot>.B <hair color>.W <hair style>.W
case 0x970:
{
FIFOSD_CHECK(31);
- #else
+#else
// S 0067 <name>.24B <str>.B <agi>.B <vit>.B <int>.B <dex>.B <luk>.B <slot>.B <hair color>.W <hair style>.W
case 0x67:
{
FIFOSD_CHECK(37);
- #endif
+#endif
chr->parse_char_create_new_char(fd, sd);
}
@@ -5293,7 +5239,7 @@ int char_parse_char(int fd)
return 0;
}
-int mapif_sendall(unsigned char *buf, unsigned int len)
+int mapif_sendall(const unsigned char *buf, unsigned int len)
{
int i, c;
@@ -5381,7 +5327,7 @@ int char_broadcast_user_count(int tid, int64 tick, int id, intptr_t data) {
* Load this character's account id into the 'online accounts' packet
* @see DBApply
*/
-static int char_send_accounts_tologin_sub(DBKey key, DBData *data, va_list ap)
+static int char_send_accounts_tologin_sub(union DBKey key, struct DBData *data, va_list ap)
{
struct online_char_data* character = DB->data2ptr(data);
int* i = va_arg(ap, int*);
@@ -5450,7 +5396,7 @@ static int char_waiting_disconnect(int tid, int64 tick, int id, intptr_t data) {
/**
* @see DBApply
*/
-static int char_online_data_cleanup_sub(DBKey key, DBData *data, va_list ap)
+static int char_online_data_cleanup_sub(union DBKey key, struct DBData *data, va_list ap)
{
struct online_char_data *character= DB->data2ptr(data);
nullpo_ret(character);
@@ -5469,304 +5415,683 @@ static int char_online_data_cleanup(int tid, int64 tick, int id, intptr_t data)
return 0;
}
-void char_sql_config_read(const char* cfgName)
+/**
+ * Reads the 'inter_configuration' config file and initializes required variables.
+ *
+ * @param filename Path to configuration file
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_sql_config_read(const char *filename, bool imported)
{
- char line[1024], w1[1024], w2[1024];
- FILE* fp;
+ struct config_t config;
+ const struct config_setting_t *setting = NULL;
+ const char *import = NULL;
+ bool retval = true;
- if ((fp = fopen(cfgName, "r")) == NULL) {
- ShowError("File not found: %s\n", cfgName);
- return;
+ nullpo_retr(false, filename);
+
+ if (!libconfig->load_file(&config, filename))
+ return false; // Error message is already shown by libconfig->load_file
+
+ if ((setting = libconfig->lookup(&config, "inter_configuration/database_names")) == NULL) {
+ libconfig->destroy(&config);
+ if (imported)
+ return true;
+ ShowError("sql_config_read: inter_configuration/database_names was not found in %s!\n", filename);
+ return false;
+ }
+ libconfig->setting_lookup_mutable_string(setting, "char_db", char_db, sizeof(char_db));
+ libconfig->setting_lookup_mutable_string(setting, "interlog_db", interlog_db, sizeof(interlog_db));
+ libconfig->setting_lookup_mutable_string(setting, "ragsrvinfo_db", ragsrvinfo_db, sizeof(ragsrvinfo_db));
+
+ if (!chr->sql_config_read_registry(filename, &config, imported))
+ retval = false;
+ if (!chr->sql_config_read_pc(filename, &config, imported))
+ retval = false;
+ if (!chr->sql_config_read_guild(filename, &config, imported))
+ retval = false;
+
+ ShowInfo("Done reading %s.\n", filename);
+ // import should overwrite any previous configuration, so it should be called last
+ if (libconfig->lookup_string(&config, "import", &import) == CONFIG_TRUE) {
+ if (strcmp(import, filename) == 0 || strcmp(import, chr->SQL_CONF_NAME) == 0) {
+ ShowWarning("sql_config_read: Loop detected in %s! Skipping 'import'...\n", filename);
+ } else {
+ if (!chr->sql_config_read(import, true))
+ retval = false;
+ }
}
- while(fgets(line, sizeof(line), fp))
- {
- if(line[0] == '/' && line[1] == '/')
- continue;
+ if (!HPM->parse_conf(&config, filename, HPCT_CHAR_INTER, imported))
+ retval = false;
- if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2)
- continue;
+ libconfig->destroy(&config);
+ return retval;
+}
- if(!strcmpi(w1,"char_db"))
- safestrncpy(char_db, w2, sizeof(char_db));
- else if(!strcmpi(w1,"scdata_db"))
- safestrncpy(scdata_db, w2, sizeof(scdata_db));
- else if(!strcmpi(w1,"cart_db"))
- safestrncpy(cart_db, w2, sizeof(cart_db));
- else if(!strcmpi(w1,"inventory_db"))
- safestrncpy(inventory_db, w2, sizeof(inventory_db));
- else if(!strcmpi(w1,"charlog_db"))
- safestrncpy(charlog_db, w2, sizeof(charlog_db));
- else if(!strcmpi(w1,"storage_db"))
- safestrncpy(storage_db, w2, sizeof(storage_db));
- else if(!strcmpi(w1,"skill_db"))
- safestrncpy(skill_db, w2, sizeof(skill_db));
- else if(!strcmpi(w1,"interlog_db"))
- safestrncpy(interlog_db, w2, sizeof(interlog_db));
- else if(!strcmpi(w1,"memo_db"))
- safestrncpy(memo_db, w2, sizeof(memo_db));
- else if(!strcmpi(w1,"guild_db"))
- safestrncpy(guild_db, w2, sizeof(guild_db));
- else if(!strcmpi(w1,"guild_alliance_db"))
- safestrncpy(guild_alliance_db, w2, sizeof(guild_alliance_db));
- else if(!strcmpi(w1,"guild_castle_db"))
- safestrncpy(guild_castle_db, w2, sizeof(guild_castle_db));
- else if(!strcmpi(w1,"guild_expulsion_db"))
- safestrncpy(guild_expulsion_db, w2, sizeof(guild_expulsion_db));
- else if(!strcmpi(w1,"guild_member_db"))
- safestrncpy(guild_member_db, w2, sizeof(guild_member_db));
- else if(!strcmpi(w1,"guild_skill_db"))
- safestrncpy(guild_skill_db, w2, sizeof(guild_skill_db));
- else if(!strcmpi(w1,"guild_position_db"))
- safestrncpy(guild_position_db, w2, sizeof(guild_position_db));
- else if(!strcmpi(w1,"guild_storage_db"))
- safestrncpy(guild_storage_db, w2, sizeof(guild_storage_db));
- else if(!strcmpi(w1,"party_db"))
- safestrncpy(party_db, w2, sizeof(party_db));
- else if(!strcmpi(w1,"pet_db"))
- safestrncpy(pet_db, w2, sizeof(pet_db));
- else if(!strcmpi(w1,"mail_db"))
- safestrncpy(mail_db, w2, sizeof(mail_db));
- else if(!strcmpi(w1,"auction_db"))
- safestrncpy(auction_db, w2, sizeof(auction_db));
- else if(!strcmpi(w1,"friend_db"))
- safestrncpy(friend_db, w2, sizeof(friend_db));
- else if(!strcmpi(w1,"hotkey_db"))
- safestrncpy(hotkey_db, w2, sizeof(hotkey_db));
- else if(!strcmpi(w1,"quest_db"))
- safestrncpy(quest_db,w2,sizeof(quest_db));
- else if(!strcmpi(w1,"homunculus_db"))
- safestrncpy(homunculus_db,w2,sizeof(homunculus_db));
- else if(!strcmpi(w1,"skill_homunculus_db"))
- safestrncpy(skill_homunculus_db,w2,sizeof(skill_homunculus_db));
- else if(!strcmpi(w1,"mercenary_db"))
- safestrncpy(mercenary_db,w2,sizeof(mercenary_db));
- else if(!strcmpi(w1,"mercenary_owner_db"))
- safestrncpy(mercenary_owner_db,w2,sizeof(mercenary_owner_db));
- else if(!strcmpi(w1,"ragsrvinfo_db"))
- safestrncpy(ragsrvinfo_db,w2,sizeof(ragsrvinfo_db));
- else if(!strcmpi(w1,"elemental_db"))
- safestrncpy(elemental_db,w2,sizeof(elemental_db));
- else if(!strcmpi(w1,"account_data_db"))
- safestrncpy(account_data_db,w2,sizeof(account_data_db));
- else if(!strcmpi(w1,"char_reg_num_db"))
- safestrncpy(char_reg_num_db, w2, sizeof(char_reg_num_db));
- else if(!strcmpi(w1,"char_reg_str_db"))
- safestrncpy(char_reg_str_db, w2, sizeof(char_reg_str_db));
- else if(!strcmpi(w1,"acc_reg_str_db"))
- safestrncpy(acc_reg_str_db, w2, sizeof(acc_reg_str_db));
- else if(!strcmpi(w1,"acc_reg_num_db"))
- safestrncpy(acc_reg_num_db, w2, sizeof(acc_reg_num_db));
- //support the import command, just like any other config
- else if(!strcmpi(w1,"import"))
- chr->sql_config_read(w2);
- else
- HPM->parseConf(w1, w2, HPCT_CHAR_INTER);
+/**
+ * Reads the 'inter_configuration/database_names/registry' config entry and initializes required variables.
+ *
+ * @param filename Path to configuration file (used in error and warning messages).
+ * @param config The current config being parsed.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_sql_config_read_registry(const char *filename, const struct config_t *config, bool imported)
+{
+ const struct config_setting_t *setting = NULL;
+
+ nullpo_retr(false, filename);
+ nullpo_retr(false, config);
+
+ if ((setting = libconfig->lookup(config, "inter_configuration/database_names/registry")) == NULL) {
+ if (imported)
+ return true;
+ ShowError("sql_config_read: inter_configuration/database_names/registry was not found in %s!\n", filename);
+ return false;
}
- fclose(fp);
- ShowInfo("Done reading %s.\n", cfgName);
+ // Not all registries are read by char-server
+ libconfig->setting_lookup_mutable_string(setting, "char_reg_num_db", char_reg_num_db, sizeof(char_reg_num_db));
+ libconfig->setting_lookup_mutable_string(setting, "char_reg_str_db", char_reg_str_db, sizeof(char_reg_str_db));
+ libconfig->setting_lookup_mutable_string(setting, "acc_reg_str_db", acc_reg_str_db, sizeof(acc_reg_str_db));
+ libconfig->setting_lookup_mutable_string(setting, "acc_reg_num_db", acc_reg_num_db, sizeof(acc_reg_num_db));
+
+ return true;
}
-void char_config_dispatch(char *w1, char *w2) {
- bool (*dispatch_to[]) (char *w1, char *w2) = {
- /* as many as it needs */
- pincode->config_read
- };
- int i, len = ARRAYLENGTH(dispatch_to);
- for(i = 0; i < len; i++) {
- if( (*dispatch_to[i])(w1,w2) )
- break;/* we found who this belongs to, can stop */
+/**
+ * Reads the 'inter_configuration/database_names/pc' config entry and initializes required variables.
+ *
+ * @param filename Path to configuration file (used in error and warning messages).
+ * @param config The current config being parsed.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_sql_config_read_pc(const char *filename, const struct config_t *config, bool imported)
+{
+ const struct config_setting_t *setting = NULL;
+
+ nullpo_retr(false, filename);
+ nullpo_retr(false, config);
+
+ if ((setting = libconfig->lookup(config, "inter_configuration/database_names/pc")) == NULL) {
+ if (imported)
+ return true;
+ ShowError("sql_config_read: inter_configuration/database_names/pc was not found in %s!\n", filename);
+ return false;
}
- if (i == len)
- HPM->parseConf(w1, w2, HPCT_CHAR);
+ libconfig->setting_lookup_mutable_string(setting, "hotkey_db", hotkey_db, sizeof(hotkey_db));
+ libconfig->setting_lookup_mutable_string(setting, "scdata_db", scdata_db, sizeof(scdata_db));
+ libconfig->setting_lookup_mutable_string(setting, "inventory_db", inventory_db, sizeof(inventory_db));
+ libconfig->setting_lookup_mutable_string(setting, "cart_db", cart_db, sizeof(cart_db));
+ libconfig->setting_lookup_mutable_string(setting, "charlog_db", charlog_db, sizeof(charlog_db));
+ libconfig->setting_lookup_mutable_string(setting, "storage_db", storage_db, sizeof(storage_db));
+ libconfig->setting_lookup_mutable_string(setting, "skill_db", skill_db, sizeof(skill_db));
+ libconfig->setting_lookup_mutable_string(setting, "memo_db", memo_db, sizeof(memo_db));
+ libconfig->setting_lookup_mutable_string(setting, "party_db", party_db, sizeof(party_db));
+ libconfig->setting_lookup_mutable_string(setting, "pet_db", pet_db, sizeof(pet_db));
+ libconfig->setting_lookup_mutable_string(setting, "friend_db", friend_db, sizeof(friend_db));
+ libconfig->setting_lookup_mutable_string(setting, "mail_db", mail_db, sizeof(mail_db));
+ libconfig->setting_lookup_mutable_string(setting, "auction_db", auction_db, sizeof(auction_db));
+ libconfig->setting_lookup_mutable_string(setting, "quest_db", quest_db, sizeof(quest_db));
+ libconfig->setting_lookup_mutable_string(setting, "homunculus_db", homunculus_db, sizeof(homunculus_db));
+ libconfig->setting_lookup_mutable_string(setting, "skill_homunculus_db", skill_homunculus_db, sizeof(skill_homunculus_db));
+ libconfig->setting_lookup_mutable_string(setting, "mercenary_db", mercenary_db, sizeof(mercenary_db));
+ libconfig->setting_lookup_mutable_string(setting, "mercenary_owner_db", mercenary_owner_db, sizeof(mercenary_owner_db));
+ libconfig->setting_lookup_mutable_string(setting, "elemental_db", elemental_db, sizeof(elemental_db));
+ libconfig->setting_lookup_mutable_string(setting, "account_data_db", account_data_db, sizeof(account_data_db));
+
+ return true;
}
-int char_config_read(const char* cfgName)
+/**
+ * Reads the 'inter_configuration/database_names/guild' config entry and initializes required variables.
+ *
+ * @param filename Path to configuration file (used in error and warning messages).
+ * @param config The current config being parsed.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_sql_config_read_guild(const char *filename, const struct config_t *config, bool imported)
{
- char line[1024], w1[1024], w2[1024];
- FILE* fp = fopen(cfgName, "r");
+ const struct config_setting_t *setting = NULL;
- if (fp == NULL) {
- ShowError("Configuration file not found: %s.\n", cfgName);
- return 1;
+ nullpo_retr(false, filename);
+ nullpo_retr(false, config);
+
+ if ((setting = libconfig->lookup(config, "inter_configuration/database_names/guild")) == NULL) {
+ if (imported)
+ return true;
+ ShowError("sql_config_read: inter_configuration/database_names/guild was not found in %s!\n", filename);
+ return false;
}
- while(fgets(line, sizeof(line), fp)) {
- if (line[0] == '/' && line[1] == '/')
+ libconfig->setting_lookup_mutable_string(setting, "main_db", guild_db, sizeof(guild_db));
+ libconfig->setting_lookup_mutable_string(setting, "alliance_db", guild_alliance_db, sizeof(guild_alliance_db));
+ libconfig->setting_lookup_mutable_string(setting, "castle_db", guild_castle_db, sizeof(guild_castle_db));
+ libconfig->setting_lookup_mutable_string(setting, "expulsion_db", guild_expulsion_db, sizeof(guild_expulsion_db));
+ libconfig->setting_lookup_mutable_string(setting, "member_db", guild_member_db, sizeof(guild_member_db));
+ libconfig->setting_lookup_mutable_string(setting, "skill_db", guild_skill_db, sizeof(guild_skill_db));
+ libconfig->setting_lookup_mutable_string(setting, "position_db", guild_position_db, sizeof(guild_position_db));
+ libconfig->setting_lookup_mutable_string(setting, "storage_db", guild_storage_db, sizeof(guild_storage_db));
+
+ return true;
+}
+
+/**
+ * Reads the 'char_configuration' config file and initializes required variables.
+ *
+ * @param filename Path to configuration file.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_config_read(const char *filename, bool imported)
+{
+ struct config_t config;
+ const char *import = NULL;
+ bool retval = true;
+
+ nullpo_retr(false, filename);
+
+ if (!libconfig->load_file(&config, filename))
+ return false; // Error message is already shown by libconfig->load_file
+
+ if (!chr->config_read_top(filename, &config, imported))
+ retval = false;
+ if (!chr->config_read_inter(filename, &config, imported))
+ retval = false;
+ if (!chr->config_read_permission(filename, &config, imported))
+ retval = false;
+ if (!chr->config_read_player(filename, &config, imported))
+ retval = false;
+ if (!chr->config_read_console(filename, &config, imported))
+ retval = false;
+ if (!chr->config_read_database(filename, &config, imported))
+ retval = false;
+ if (!inter->config_read_connection(filename, &config, imported))
+ retval = false;
+ if (!pincode->config_read(filename, &config, imported))
+ retval = false;
+
+ if (!HPM->parse_conf(&config, filename, HPCT_CHAR, imported))
+ retval = false;
+
+ ShowInfo("Done reading %s.\n", filename);
+
+ // import should overwrite any previous configuration, so it should be called last
+ if (libconfig->lookup_string(&config, "import", &import) == CONFIG_TRUE) {
+ if (strcmp(import, filename) == 0 || strcmp(import, chr->CHAR_CONF_NAME) == 0) {
+ ShowWarning("char_config_read: Loop detected in %s! Skipping 'import'...\n", filename);
+ } else {
+ if (!chr->config_read(import, true))
+ retval = false;
+ }
+ }
+
+ libconfig->destroy(&config);
+ return retval;
+}
+
+/**
+ * Reads the 'char_configuration' top level config entry and initializes required variables.
+ *
+ * @param filename Path to configuration file (used in error and warning messages).
+ * @param config The current config being parsed.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_config_read_top(const char *filename, const struct config_t *config, bool imported)
+{
+ const struct config_setting_t *setting = NULL;
+
+ nullpo_retr(false, filename);
+ nullpo_retr(false, config);
+
+ if ((setting = libconfig->lookup(config, "char_configuration")) == NULL) {
+ if (imported)
+ return true;
+ ShowError("char_config_read: char_configuration was not found in %s!\n", filename);
+ return false;
+ }
+
+ // char_configuration/server_name
+ if (libconfig->setting_lookup_mutable_string(setting, "server_name", chr->server_name, sizeof(chr->server_name)) == CONFIG_TRUE) {
+ ShowInfo("server name %s\n", chr->server_name);
+ } else if (!imported) {
+ ShowWarning("char_config_read: server_name was not set! Defaulting to 'Hercules'.\n");
+ safestrncpy(chr->server_name, "Hercules", sizeof(chr->server_name));
+ }
+ // char_configuration/wisp_server_name
+ if (libconfig->setting_lookup_mutable_string(setting, "wisp_server_name", wisp_server_name, sizeof(wisp_server_name)) == CONFIG_TRUE) {
+ // wisp_server_name should _always_ be equal or bigger than 4 characters!
+ if (strlen(wisp_server_name) < 4) { // TODO: This length should be a #define (i.e. MIN_NAME_LENGTH)
+ ShowWarning("char_config_read: char_configuration/wisp_server_name is too small! Defaulting to: Server.\n");
+ safestrncpy(chr->server_name, "Server", sizeof(chr->server_name));
+ }
+ }
+ // char_configuration/guild_exp_rate
+ libconfig->setting_lookup_int(setting, "guild_exp_rate", &guild_exp_rate);
+
+ return true;
+}
+
+/**
+ * Reads the 'char_configuration/inter' config entry and initializes required variables.
+ *
+ * @param filename Path to configuration file (used in error and warning messages).
+ * @param config The current config being parsed.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_config_read_inter(const char *filename, const struct config_t *config, bool imported)
+{
+ const struct config_setting_t *setting = NULL;
+ const char *str = NULL;
+
+ nullpo_retr(false, filename);
+ nullpo_retr(false, config);
+
+ if ((setting = libconfig->lookup(config, "char_configuration/inter")) == NULL) {
+ if (imported)
+ return true;
+ ShowError("char_config_read: char_configuration/inter was not found in %s!\n", filename);
+ return false;
+ }
+
+ // Login information
+ libconfig->setting_lookup_mutable_string(setting, "userid", chr->userid, sizeof(chr->userid));
+ libconfig->setting_lookup_mutable_string(setting, "passwd", chr->passwd, sizeof(chr->passwd));
+
+ // Login-server and character-server information
+ if (libconfig->setting_lookup_string(setting, "login_ip", &str) == CONFIG_TRUE)
+ chr->config_set_ip("Login server", str, &login_ip, login_ip_str);
+
+ if (libconfig->setting_lookup_string(setting, "char_ip", &str) == CONFIG_TRUE)
+ chr->config_set_ip("Character server", str, &chr->ip, char_ip_str);
+
+ if (libconfig->setting_lookup_string(setting, "bind_ip", &str) == CONFIG_TRUE)
+ chr->config_set_ip("Character server binding", str, &bind_ip, bind_ip_str);
+
+ libconfig->setting_lookup_uint16(setting, "login_port", &login_port);
+ libconfig->setting_lookup_uint16(setting, "char_port", &chr->port);
+
+ return true;
+}
+
+/**
+ * Reads the 'char_configuration/database' config entry and initializes required variables.
+ *
+ * @param filename Path to configuration file (used in error and warning messages).
+ * @param config The current config being parsed.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_config_read_database(const char *filename, const struct config_t *config, bool imported)
+{
+ const struct config_setting_t *setting = NULL;
+
+ nullpo_retr(false, filename);
+ nullpo_retr(false, config);
+
+ if ((setting = libconfig->lookup(config, "char_configuration/database")) == NULL) {
+ if (imported)
+ return true;
+ ShowError("char_config_read: char_configuration/database was not found in %s!\n", filename);
+ return false;
+ }
+ if (libconfig->setting_lookup_int(setting, "autosave_time", &autosave_interval) == CONFIG_TRUE) {
+ autosave_interval *= 1000;
+ if (autosave_interval <= 0)
+ autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
+ }
+ libconfig->setting_lookup_mutable_string(setting, "db_path", db_path, sizeof(db_path));
+ libconfig->setting_lookup_bool_real(setting, "log_char", &chr->enable_logs);
+ return true;
+}
+
+/**
+ * Reads the 'char_configuration/console' config entry and initializes required variables.
+ *
+ * @param filename Path to configuration file (used in error and warning messages).
+ * @param config The current config being parsed.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_config_read_console(const char *filename, const struct config_t *config, bool imported)
+{
+ const struct config_setting_t *setting;
+
+ nullpo_retr(false, filename);
+ nullpo_retr(false, config);
+
+ if ((setting = libconfig->lookup(config, "char_configuration/console")) == NULL) {
+ if (imported)
+ return true;
+ ShowError("char_config_read: char_configuration/console was not found in %s!\n", filename);
+ return false;
+ }
+ libconfig->setting_lookup_bool_real(setting, "stdout_with_ansisequence", &showmsg->stdout_with_ansisequence);
+ libconfig->setting_lookup_bool_real(setting, "save_log", &chr->show_save_log);
+ if (libconfig->setting_lookup_int(setting, "console_silent", &showmsg->silent) == CONFIG_TRUE) {
+ if (showmsg->silent) // only bother if its actually enabled
+ ShowInfo("Console Silent Setting: %d\n", showmsg->silent);
+ }
+ libconfig->setting_lookup_mutable_string(setting, "timestamp_format", showmsg->timestamp_format, sizeof(showmsg->timestamp_format));
+
+ return true;
+}
+
+/**
+ * Reads the 'char_configuration/player' config entry and initializes required variables.
+ *
+ * @param filename Path to configuration file (used in error and warning messages).
+ * @param config The current config being parsed.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_config_read_player(const char *filename, const struct config_t *config, bool imported)
+{
+ bool retval = true;
+
+ nullpo_retr(false, filename);
+ nullpo_retr(false, config);
+
+ if (!chr->config_read_player_new(filename, config, imported))
+ retval = false;
+ if (!chr->config_read_player_name(filename, config, imported))
+ retval = false;
+ if (!chr->config_read_player_deletion(filename, config, imported))
+ retval = false;
+ if (!chr->config_read_player_fame(filename, config, imported))
+ retval = false;
+
+ return retval;
+}
+
+/**
+ * Reads the 'char_configuration/player/fame' config entry and initializes required variables.
+ *
+ * @param filename Path to configuration file (used in error and warning messages).
+ * @param config The current config being parsed.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_config_read_player_fame(const char *filename, const struct config_t *config, bool imported)
+{
+ const struct config_setting_t *setting = NULL;
+
+ nullpo_retr(false, filename);
+ nullpo_retr(false, config);
+
+ if ((setting = libconfig->lookup(config, "char_configuration/player/fame")) == NULL) {
+ if (imported)
+ return true;
+ ShowError("char_config_read: char_configuration/player/fame was not found in %s!\n", filename);
+ return false;
+ }
+
+ libconfig->setting_lookup_int(setting, "alchemist", &fame_list_size_chemist);
+ if (fame_list_size_chemist > MAX_FAME_LIST) {
+ ShowWarning("Max fame list size is %d (fame_list_alchemist)\n", MAX_FAME_LIST);
+ fame_list_size_chemist = MAX_FAME_LIST;
+ }
+
+ libconfig->setting_lookup_int(setting, "blacksmith", &fame_list_size_smith);
+ if (fame_list_size_smith > MAX_FAME_LIST) {
+ ShowWarning("Max fame list size is %d (fame_list_blacksmith)\n", MAX_FAME_LIST);
+ fame_list_size_smith = MAX_FAME_LIST;
+ }
+
+ libconfig->setting_lookup_int(setting, "taekwon", &fame_list_size_taekwon);
+ if (fame_list_size_taekwon > MAX_FAME_LIST) {
+ ShowWarning("Max fame list size is %d (fame_list_taekwon)\n", MAX_FAME_LIST);
+ fame_list_size_taekwon = MAX_FAME_LIST;
+ }
+
+ return true;
+}
+
+/**
+ * Reads the 'char_configuration/player/deletion' config entry and initializes required variables.
+ *
+ * @param filename Path to configuration file (used in error and warning messages).
+ * @param config The current config being parsed.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_config_read_player_deletion(const char *filename, const struct config_t *config, bool imported)
+{
+ const struct config_setting_t *setting = NULL;
+
+ nullpo_retr(false, filename);
+ nullpo_retr(false, config);
+
+ if ((setting = libconfig->lookup(config, "char_configuration/player/deletion")) == NULL) {
+ if (imported)
+ return true;
+ ShowError("char_config_read: char_configuration/player/deletion was not found in %s!\n", filename);
+ return false;
+ }
+ libconfig->setting_lookup_int(setting, "level", &char_del_level);
+ libconfig->setting_lookup_int(setting, "delay", &char_del_delay);
+ libconfig->setting_lookup_bool_real(setting, "use_aegis_delete", &char_aegis_delete);
+
+ return true;
+}
+
+/**
+ * Reads the 'char_configuration/player/name' config entry and initializes required variables.
+ *
+ * @param filename Path to configuration file (used in error and warning messages).
+ * @param config The current config being parsed.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_config_read_player_name(const char *filename, const struct config_t *config, bool imported)
+{
+ const struct config_setting_t *setting = NULL;
+
+ nullpo_retr(false, filename);
+ nullpo_retr(false, config);
+
+ if ((setting = libconfig->lookup(config, "char_configuration/player/name")) == NULL) {
+ if (imported)
+ return true;
+ ShowError("char_config_read: char_configuration/player/name was not found in %s!\n", filename);
+ return false;
+ }
+ libconfig->setting_lookup_mutable_string(setting, "unknown_char_name", unknown_char_name, sizeof(unknown_char_name));
+ libconfig->setting_lookup_mutable_string(setting, "name_letters", char_name_letters, sizeof(char_name_letters));
+ libconfig->setting_lookup_int(setting, "name_option", &char_name_option);
+ libconfig->setting_lookup_bool_real(setting, "name_ignoring_case", &name_ignoring_case);
+
+ return true;
+}
+
+/**
+ * Defines start_items based on '(...)/player/new/start_item'.
+ *
+ * @param setting The already retrieved start_item setting.
+ */
+void char_config_set_start_item(const struct config_setting_t *setting)
+{
+ int i, count;
+
+ nullpo_retv(setting);
+
+ VECTOR_CLEAR(start_items);
+
+ count = libconfig->setting_length(setting);
+ if (!count)
+ return;
+
+ VECTOR_ENSURE(start_items, count, 1);
+
+ for (i = 0; i < count; i++) {
+ const struct config_setting_t *t = libconfig->setting_get_elem(setting, i);
+ struct start_item_s start_item = { 0 };
+
+ if (t == NULL)
continue;
- if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2)
+ if (libconfig->setting_lookup_int(t, "id", &start_item.id) != CONFIG_TRUE) {
+ ShowWarning("char_config_read: entry (%d) is missing id! Ignoring...\n", i);
continue;
+ }
+ if (libconfig->setting_lookup_int(t, "amount", &start_item.amount) != CONFIG_TRUE) {
+ ShowWarning("char_config_read: entry (%d) is missing amount! Defaulting to 1...\n", i);
+ start_item.amount = 1;
+ }
+ if (libconfig->setting_lookup_bool_real(t, "stackable", &start_item.stackable) != CONFIG_TRUE) {
+ // Without knowing if the item is stackable or not we can't add it!
+ ShowWarning("char_config_read: entry (%d) is missing stackable! Ignoring...\n", i);
+ continue;
+ }
+ if (libconfig->setting_lookup_int(t, "loc", &start_item.loc) != CONFIG_TRUE)
+ start_item.loc = 0;
+ VECTOR_PUSH(start_items, start_item);
+ }
+}
- remove_control_chars(w1);
- remove_control_chars(w2);
- if(strcmpi(w1,"timestamp_format") == 0) {
- safestrncpy(showmsg->timestamp_format, w2, sizeof(showmsg->timestamp_format));
- } else if(strcmpi(w1,"console_silent")==0){
- showmsg->silent = atoi(w2);
- if (showmsg->silent) /* only bother if its actually enabled */
- ShowInfo("Console Silent Setting: %d\n", atoi(w2));
- } else if(strcmpi(w1,"stdout_with_ansisequence")==0){
- showmsg->stdout_with_ansisequence = config_switch(w2) ? true : false;
- } else if (strcmpi(w1, "userid") == 0) {
- safestrncpy(chr->userid, w2, sizeof(chr->userid));
- } else if (strcmpi(w1, "passwd") == 0) {
- safestrncpy(chr->passwd, w2, sizeof(chr->passwd));
- } else if (strcmpi(w1, "server_name") == 0) {
- safestrncpy(chr->server_name, w2, sizeof(chr->server_name));
- } else if (strcmpi(w1, "wisp_server_name") == 0) {
- if (strlen(w2) >= 4) {
- safestrncpy(wisp_server_name, w2, sizeof(wisp_server_name));
- }
- } else if (strcmpi(w1, "login_ip") == 0) {
- login_ip = sockt->host2ip(w2);
- if (login_ip) {
- char ip_str[16];
- safestrncpy(login_ip_str, w2, sizeof(login_ip_str));
- ShowStatus("Login server IP address : %s -> %s\n", w2, sockt->ip2str(login_ip, ip_str));
- }
- } else if (strcmpi(w1, "login_port") == 0) {
- login_port = atoi(w2);
- } else if (strcmpi(w1, "char_ip") == 0) {
- chr->ip = sockt->host2ip(w2);
- if (chr->ip) {
- char ip_str[16];
- safestrncpy(char_ip_str, w2, sizeof(char_ip_str));
- ShowStatus("Character server IP address : %s -> %s\n", w2, sockt->ip2str(chr->ip, ip_str));
- }
- } else if (strcmpi(w1, "bind_ip") == 0) {
- bind_ip = sockt->host2ip(w2);
- if (bind_ip) {
- char ip_str[16];
- safestrncpy(bind_ip_str, w2, sizeof(bind_ip_str));
- ShowStatus("Character server binding IP address : %s -> %s\n", w2, sockt->ip2str(bind_ip, ip_str));
- }
- } else if (strcmpi(w1, "char_port") == 0) {
- chr->port = atoi(w2);
- } else if (strcmpi(w1, "char_server_type") == 0) {
- chr->server_type = atoi(w2);
- } else if (strcmpi(w1, "char_new") == 0) {
- char_new = (bool)atoi(w2);
- } else if (strcmpi(w1, "char_new_display") == 0) {
- chr->new_display = atoi(w2);
- } else if (strcmpi(w1, "max_connect_user") == 0) {
- max_connect_user = atoi(w2);
- if (max_connect_user < -1)
- max_connect_user = -1; // unlimited online players
- } else if(strcmpi(w1, "gm_allow_group") == 0) {
- gm_allow_group = atoi(w2);
- } else if (strcmpi(w1, "autosave_time") == 0) {
- autosave_interval = atoi(w2) * 1000;
- if (autosave_interval <= 0)
- autosave_interval = DEFAULT_AUTOSAVE_INTERVAL;
- } else if (strcmpi(w1, "save_log") == 0) {
- save_log = config_switch(w2);
+/**
+ * Reads the 'char_configuration/player/new' config entry and initializes required variables.
+ *
+ * @param filename Path to configuration file (used in error and warning messages).
+ * @param config The current config being parsed.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_config_read_player_new(const char *filename, const struct config_t *config, bool imported)
+{
+ const struct config_setting_t *setting = NULL, *setting2 = NULL;
+#ifdef RENEWAL
+ const char *start_point_setting = "start_point_re";
+#else
+ const char *start_point_setting = "start_point_pre";
+#endif
+ int64 i64 = 0;
+
+ nullpo_retr(false, filename);
+ nullpo_retr(false, config);
+
+ if ((setting = libconfig->lookup(config, "char_configuration/player/new")) == NULL) {
+ if (imported)
+ return true;
+ ShowError("char_config_read: char_configuration/player/new was not found in %s!\n", filename);
+ return false;
+ }
+
+ if (libconfig->setting_lookup_int64(setting, "zeny", &i64) == CONFIG_TRUE) {
+ if (i64 > MAX_ZENY) {
+ ShowWarning("char_config_read: player/new/zeny is too big! Capping to MAX_ZENY.\n");
+ start_zeny = MAX_ZENY;
+ } else {
+ start_zeny = (int)i64;
}
- #ifdef RENEWAL
- else if (strcmpi(w1, "start_point_re") == 0) {
- char map[MAP_NAME_LENGTH_EXT];
- int x, y;
- if (sscanf(w2, "%15[^,],%d,%d", map, &x, &y) < 3)
- continue;
- start_point.map = mapindex->name2id(map);
- if (!start_point.map)
- ShowError("Specified start_point_re '%s' not found in map-index cache.\n", map);
- start_point.x = x;
- start_point.y = y;
- }
- #else
- else if (strcmpi(w1, "start_point_pre") == 0) {
- char map[MAP_NAME_LENGTH_EXT];
- int x, y;
- if (sscanf(w2, "%15[^,],%d,%d", map, &x, &y) < 3)
- continue;
- start_point.map = mapindex->name2id(map);
- if (!start_point.map)
- ShowError("Specified start_point_pre '%s' not found in map-index cache.\n", map);
- start_point.x = x;
- start_point.y = y;
- }
- #endif
- else if (strcmpi(w1, "start_items") == 0) {
- int i;
- char *split;
+ }
- i = 0;
- split = strtok(w2, ",");
- while (split != NULL && i < MAX_START_ITEMS * 3) {
- char *split2 = split;
- split = strtok(NULL, ",");
- start_items[i] = atoi(split2);
+ if ((setting2 = libconfig->setting_get_member(setting, "start_items")))
+ chr->config_set_start_item(setting2);
- if (start_items[i] < 0)
- start_items[i] = 0;
+ // start_point / start_point_pre
+ if ((setting2 = libconfig->setting_get_member(setting, start_point_setting))) {
+ const char *str = NULL;
+ if (libconfig->setting_lookup_string(setting2, "map", &str) == CONFIG_TRUE) {
+ start_point.map = mapindex->name2id(str);
+ if (start_point.map == 0)
+ ShowError("char_config_read_player_new: Specified start_point %s not found in map-index cache.\n", str);
+ libconfig->setting_lookup_int16(setting2, "x", &start_point.x);
+ libconfig->setting_lookup_int16(setting2, "y", &start_point.y);
+ }
+ }
- ++i;
- }
+ return true;
+}
- // Format is: id1,quantity1,stackable1,idN,quantityN,stackableN
- if( i%3 )
- {
- ShowWarning("chr->config_read: There are not enough parameters in start_items, ignoring last item...\n");
- if( i%3 == 1 )
- start_items[i-1] = 0;
- else
- start_items[i-2] = 0;
- }
- } else if (strcmpi(w1, "start_zeny") == 0) {
- start_zeny = atoi(w2);
- if (start_zeny < 0)
- start_zeny = 0;
- } else if(strcmpi(w1,"log_char")==0) {
- log_char = atoi(w2); //log char or not [devil]
- } else if (strcmpi(w1, "unknown_char_name") == 0) {
- safestrncpy(unknown_char_name, w2, sizeof(unknown_char_name));
- unknown_char_name[NAME_LENGTH-1] = '\0';
- } else if (strcmpi(w1, "name_ignoring_case") == 0) {
- name_ignoring_case = (bool)config_switch(w2);
- } else if (strcmpi(w1, "char_name_option") == 0) {
- char_name_option = atoi(w2);
- } else if (strcmpi(w1, "char_name_letters") == 0) {
- safestrncpy(char_name_letters, w2, sizeof(char_name_letters));
- } else if (strcmpi(w1, "char_del_level") == 0) { //disable/enable char deletion by its level condition [Lupus]
- char_del_level = atoi(w2);
- } else if (strcmpi(w1, "char_del_delay") == 0) {
- char_del_delay = atoi(w2);
- } else if (strcmpi(w1, "char_aegis_delete") == 0) {
- char_aegis_delete = atoi(w2);
- } else if(strcmpi(w1,"db_path")==0) {
- safestrncpy(db_path, w2, sizeof(db_path));
- } else if (strcmpi(w1, "fame_list_alchemist") == 0) {
- fame_list_size_chemist = atoi(w2);
- if (fame_list_size_chemist > MAX_FAME_LIST) {
- ShowWarning("Max fame list size is %d (fame_list_alchemist)\n", MAX_FAME_LIST);
- fame_list_size_chemist = MAX_FAME_LIST;
- }
- } else if (strcmpi(w1, "fame_list_blacksmith") == 0) {
- fame_list_size_smith = atoi(w2);
- if (fame_list_size_smith > MAX_FAME_LIST) {
- ShowWarning("Max fame list size is %d (fame_list_blacksmith)\n", MAX_FAME_LIST);
- fame_list_size_smith = MAX_FAME_LIST;
- }
- } else if (strcmpi(w1, "fame_list_taekwon") == 0) {
- fame_list_size_taekwon = atoi(w2);
- if (fame_list_size_taekwon > MAX_FAME_LIST) {
- ShowWarning("Max fame list size is %d (fame_list_taekwon)\n", MAX_FAME_LIST);
- fame_list_size_taekwon = MAX_FAME_LIST;
- }
- } else if (strcmpi(w1, "guild_exp_rate") == 0) {
- guild_exp_rate = atoi(w2);
- } else if (strcmpi(w1, "char_maintenance_min_group_id") == 0) {
- char_maintenance_min_group_id = atoi(w2);
- } else if (strcmpi(w1, "import") == 0) {
- chr->config_read(w2);
- } else
- chr->config_dispatch(w1,w2);
+/**
+ * Reads the 'char_configuration/permission' config entry and initializes required variables.
+ *
+ * @param filename Path to configuration file (used in error and warning messages).
+ * @param config The current config being parsed.
+ * @param imported Whether the current config is imported from another file.
+ *
+ * @retval false in case of error.
+ */
+bool char_config_read_permission(const char *filename, const struct config_t *config, bool imported)
+{
+ const struct config_setting_t *setting = NULL;
+
+ nullpo_retr(false, filename);
+ nullpo_retr(false, config);
+
+ if ((setting = libconfig->lookup(config, "char_configuration/permission")) == NULL) {
+ if (imported)
+ return true;
+ ShowError("char_config_read: char_configuration/permission was not found in %s!\n", filename);
+ return false;
}
- fclose(fp);
- ShowInfo("Done reading %s.\n", cfgName);
- return 0;
+ libconfig->setting_lookup_bool_real(setting, "enable_char_creation", &enable_char_creation);
+ if (libconfig->setting_lookup_int16(setting, "display_new", &chr->new_display) != CONFIG_TRUE) {
+ // While normally true/false makes sense, we may accept any int16 here (it's passed as is to the client)
+ int i32 = 0;
+ if (libconfig->setting_lookup_bool(setting, "display_new", &i32) == CONFIG_TRUE)
+ chr->new_display = i32 == 0 ? 0 : 1;
+ }
+ libconfig->setting_lookup_int(setting, "max_connect_user", &max_connect_user);
+ libconfig->setting_lookup_int(setting, "gm_allow_group", &gm_allow_group);
+ libconfig->setting_lookup_int(setting, "maintenance_min_group_id", &char_maintenance_min_group_id);
+ if (libconfig->setting_lookup_int(setting, "server_type", &chr->server_type) == CONFIG_TRUE) {
+ if (chr->server_type < CST_NORMAL || chr->server_type >= CST_MAX) {
+ ShowWarning("char_config_read: Invalid permission/server_type %d, defaulting to CST_NORMAL.\n", chr->server_type);
+ chr->server_type = CST_NORMAL;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Loads an IP into 'out_ip' and shows status.
+ *
+ * @param type[in] String containing the type of IP being set (for logging purposes).
+ * @param value[in] New ip value to parse.
+ * @param out_ip[in] Pointer to numeric value that will be changed.
+ * @param out_ip_str[in,out] Pointer to str value that will be changed (expected to be already initialized, to display previous value, if any).
+ *
+ * @retval false in case of error.
+ */
+bool char_config_set_ip(const char *type, const char *value, uint32 *out_ip, char *out_ip_str)
+{
+ uint32 ip = 0;
+
+ nullpo_retr(false, type);
+ nullpo_retr(false, value);
+ nullpo_retr(false, out_ip);
+ nullpo_retr(false, out_ip_str);
+
+ if ((ip = sockt->host2ip(value)) == 0)
+ return false;
+ *out_ip = ip;
+
+ ShowStatus("%s IP address : %s -> %s\n", type, out_ip_str[0] != '\0' ? out_ip_str : "0.0.0.0", sockt->ip2str(ip, NULL));
+ safestrncpy(out_ip_str, value, sizeof *out_ip_str);
+ return true;
}
int do_final(void) {
@@ -5806,6 +6131,8 @@ int do_final(void) {
for (i = 0; i < MAX_MAP_SERVERS; i++)
VECTOR_CLEAR(chr->server[i].maps);
+ VECTOR_CLEAR(start_items);
+
aFree(chr->CHAR_CONF_NAME);
aFree(chr->NET_CONF_NAME);
aFree(chr->SQL_CONF_NAME);
@@ -5882,11 +6209,25 @@ static CMDLINEARG(netconfig)
chr->NET_CONF_NAME = aStrdup(params);
return true;
}
+
+/**
+ * --run-once handler
+ *
+ * Causes the server to run its loop once, and shutdown. Useful for testing.
+ * @see cmdline->exec
+ */
+static CMDLINEARG(runonce)
+{
+ core->runflag = CORE_ST_STOP;
+ return true;
+}
+
/**
* Initializes the command line arguments handlers.
*/
void cmdline_args_init_local(void)
{
+ CMDLINEARG_DEF2(run-once, runonce, "Closes server after loading (testing).", CMDLINE_OPT_NORMAL);
CMDLINEARG_DEF2(char-config, charconfig, "Alternative char-server configuration.", CMDLINE_OPT_PARAM);
CMDLINEARG_DEF2(inter-config, interconfig, "Alternative inter-server configuration.", CMDLINE_OPT_PARAM);
CMDLINEARG_DEF2(net-config, netconfig, "Alternative network configuration.", CMDLINE_OPT_PARAM);
@@ -5898,10 +6239,12 @@ int do_init(int argc, char **argv) {
char_load_defaults();
- chr->CHAR_CONF_NAME = aStrdup("conf/char-server.conf");
+ chr->CHAR_CONF_NAME = aStrdup("conf/char/char-server.conf");
chr->NET_CONF_NAME = aStrdup("conf/network.conf");
- chr->SQL_CONF_NAME = aStrdup("conf/inter-server.conf");
- chr->INTER_CONF_NAME = aStrdup("conf/inter-server.conf");
+ chr->SQL_CONF_NAME = aStrdup("conf/common/inter-server.conf");
+ chr->INTER_CONF_NAME = aStrdup("conf/common/inter-server.conf");
+
+ VECTOR_INIT(start_items);
for (i = 0; i < MAX_MAP_SERVERS; i++)
VECTOR_INIT(chr->server[i].maps);
@@ -5920,16 +6263,41 @@ int do_init(int argc, char **argv) {
start_point.map = mapindex->name2id("new_1-1");
#endif
+ safestrncpy(chr->userid, "s1", sizeof(chr->userid));
+ safestrncpy(chr->passwd, "p1", sizeof(chr->passwd));
+
cmdline->exec(argc, argv, CMDLINE_OPT_NORMAL);
- chr->config_read(chr->CHAR_CONF_NAME);
+ chr->config_read(chr->CHAR_CONF_NAME, false);
sockt->net_config_read(chr->NET_CONF_NAME);
- chr->sql_config_read(chr->SQL_CONF_NAME);
+ chr->sql_config_read(chr->SQL_CONF_NAME, false);
+ {
+ // TODO: Remove this when no longer needed.
+#define CHECK_OLD_LOCAL_CONF(oldname, newname) do { \
+ if (stat((oldname), &fileinfo) == 0 && fileinfo.st_size > 0) { \
+ ShowWarning("An old configuration file \"%s\" was found.\n", (oldname)); \
+ ShowWarning("If it contains settings you wish to keep, please merge them into \"%s\".\n", (newname)); \
+ ShowWarning("Otherwise, just delete it.\n"); \
+ ShowInfo("Resuming in 10 seconds...\n"); \
+ HSleep(10); \
+ } \
+} while (0)
+ struct stat fileinfo;
+
+ CHECK_OLD_LOCAL_CONF("conf/import/char_conf.txt", "conf/import/char-server.conf");
+ CHECK_OLD_LOCAL_CONF("conf/import/inter_conf.txt", "conf/import/inter-server.conf");
+ CHECK_OLD_LOCAL_CONF("conf/import/packet_conf.txt", "conf/import/socket.conf");
+
+#undef CHECK_OLD_LOCAL_CONF
+ }
+
+#ifndef BUILDBOT
if (strcmp(chr->userid, "s1")==0 && strcmp(chr->passwd, "p1")==0) {
ShowWarning("Using the default user/password s1/p1 is NOT RECOMMENDED.\n");
ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n");
- ShowNotice("And then change the user/password to use in conf/char-server.conf (or conf/import/char_conf.txt)\n");
+ ShowNotice("And then change the user/password to use in conf/char/char-server.conf (or conf/import/char-server.conf)\n");
}
+#endif
inter->init_sql(chr->INTER_CONF_NAME); // inter server configuration
@@ -6052,6 +6420,9 @@ void char_defaults(void)
chr->server_type = 0;
chr->new_display = 0;
+ chr->show_save_log = true;
+ chr->enable_logs = true;
+
chr->waiting_disconnect = char_waiting_disconnect;
chr->delete_char_sql = char_delete_char_sql;
chr->create_online_char_data = char_create_online_char_data;
@@ -6068,7 +6439,6 @@ void char_defaults(void)
chr->create_charstatus = char_create_charstatus;
chr->mmo_char_tosql = char_mmo_char_tosql;
chr->memitemdata_to_sql = char_memitemdata_to_sql;
- chr->inventory_to_sql = char_inventory_to_sql;
chr->mmo_gender = char_mmo_gender;
chr->mmo_chars_fromsql = char_mmo_chars_fromsql;
chr->mmo_char_fromsql = char_mmo_char_fromsql;
@@ -6209,6 +6579,20 @@ void char_defaults(void)
chr->online_data_cleanup_sub = char_online_data_cleanup_sub;
chr->online_data_cleanup = char_online_data_cleanup;
chr->sql_config_read = char_sql_config_read;
- chr->config_dispatch = char_config_dispatch;
+ chr->sql_config_read_registry = char_sql_config_read_registry;
+ chr->sql_config_read_pc = char_sql_config_read_pc;
+ chr->sql_config_read_guild = char_sql_config_read_guild;
chr->config_read = char_config_read;
+ chr->config_read_database = char_config_read_database;
+ chr->config_read_console = char_config_read_console;
+ chr->config_read_player_fame = char_config_read_player_fame;
+ chr->config_read_player_deletion = char_config_read_player_deletion;
+ chr->config_read_player_name = char_config_read_player_name;
+ chr->config_set_start_item = char_config_set_start_item;
+ chr->config_read_player_new = char_config_read_player_new;
+ chr->config_read_player = char_config_read_player;
+ chr->config_read_permission = char_config_read_permission;
+ chr->config_set_ip = char_config_set_ip;
+ chr->config_read_inter = char_config_read_inter;
+ chr->config_read_top = char_config_read_top;
}