diff options
author | Happy <markaizer@gmail.com> | 2014-08-21 04:50:46 +0800 |
---|---|---|
committer | Happy <markaizer@gmail.com> | 2014-08-21 04:50:46 +0800 |
commit | f52e1007fe08c67003c0bc4c78231904dd3fd5cc (patch) | |
tree | 99907d827264e501774e58ab4630e41fa7103c02 /src/char/char.c | |
parent | 2410110dece79b4598c12f1c953219f1d0d1904a (diff) | |
parent | 769b1d05aa5cfa8cddfe7d21b35d5c5e4da3bbd6 (diff) | |
download | hercules-f52e1007fe08c67003c0bc4c78231904dd3fd5cc.tar.gz hercules-f52e1007fe08c67003c0bc4c78231904dd3fd5cc.tar.bz2 hercules-f52e1007fe08c67003c0bc4c78231904dd3fd5cc.tar.xz hercules-f52e1007fe08c67003c0bc4c78231904dd3fd5cc.zip |
Merge pull request #1 from HerculesWS/master
Update from original
Diffstat (limited to 'src/char/char.c')
-rw-r--r-- | src/char/char.c | 1410 |
1 files changed, 945 insertions, 465 deletions
diff --git a/src/char/char.c b/src/char/char.c index 7dfb6861c..9abb17257 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -2,7 +2,30 @@ // See the LICENSE file // Portions Copyright (c) Athena Dev Teams +#define HERCULES_CORE + +#include "../config/core.h" // CONSOLE_INPUT +#include "char.h" + +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <time.h> + +#include "int_elemental.h" +#include "int_guild.h" +#include "int_homun.h" +#include "int_mercenary.h" +#include "int_party.h" +#include "int_storage.h" +#include "inter.h" +#include "pincode.h" +#include "../common/HPM.h" #include "../common/cbasetypes.h" +#include "../common/console.h" #include "../common/core.h" #include "../common/db.h" #include "../common/malloc.h" @@ -13,23 +36,6 @@ #include "../common/strlib.h" #include "../common/timer.h" #include "../common/utils.h" -#include "int_guild.h" -#include "int_homun.h" -#include "int_mercenary.h" -#include "int_elemental.h" -#include "int_party.h" -#include "int_storage.h" -#include "char.h" -#include "inter.h" -#include "pincode.h" - -#include <sys/types.h> -#include <time.h> -#include <signal.h> -#include <string.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> // private declarations #define CHAR_CONF_NAME "conf/char-server.conf" @@ -43,7 +49,6 @@ char inventory_db[256] = "inventory"; char charlog_db[256] = "charlog"; char storage_db[256] = "storage"; char interlog_db[256] = "interlog"; -char reg_db[256] = "global_reg_value"; char skill_db[256] = "skill"; char memo_db[256] = "memo"; char guild_db[256] = "guild"; @@ -66,7 +71,13 @@ char skill_homunculus_db[256] = "skill_homunculus"; char mercenary_db[256] = "mercenary"; char mercenary_owner_db[256] = "mercenary_owner"; char ragsrvinfo_db[256] = "ragsrvinfo"; +char elemental_db[256] = "elemental"; char interreg_db[32] = "interreg"; +char account_data_db[256] = "account_data"; +char acc_reg_num_db[32] = "acc_reg_num_db"; +char acc_reg_str_db[32] = "acc_reg_str_db"; +char char_reg_str_db[32] = "char_reg_str_db"; +char char_reg_num_db[32] = "char_reg_num_db"; // show loading/saving messages int save_log = 1; @@ -75,7 +86,9 @@ static DBMap* char_db_; // int char_id -> struct mmo_charstatus* char db_path[1024] = "db"; -int db_use_sqldbs; +int db_use_sql_item_db; +int db_use_sql_mob_db; +int db_use_sql_mob_skill_db; struct mmo_map_server { int fd; @@ -99,12 +112,13 @@ uint32 char_ip = 0; char bind_ip_str[128]; uint32 bind_ip = INADDR_ANY; uint16 char_port = 6121; -int char_maintenance = 0; +int char_server_type = 0; +int char_maintenance_min_group_id = 0; bool char_new = true; int char_new_display = 0; 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 authorised 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] +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] char unknown_char_name[NAME_LENGTH] = "Unknown"; // Name to use when the requested name cannot be determined #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] @@ -112,8 +126,10 @@ char char_name_letters[1024] = ""; // list of letters/symbols allowed (or not) i int char_del_level = 0; //From which level u can delete character [Lupus] int char_del_delay = 86400; -int log_char = 1; // loggin char or not [devil] -int log_inter = 1; // loggin inter or not [devil] +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 char_delete2_req for more information // Advanced subnet check [LuzZza] struct s_subnet { @@ -127,12 +143,12 @@ 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*2]; +int start_items[MAX_START_ITEMS*3]; int guild_exp_rate = 100; //Custom limits for the fame lists. [Skotlex] int fame_list_size_chemist = MAX_FAME_LIST; -int fame_list_size_smith = MAX_FAME_LIST; +int fame_list_size_smith = MAX_FAME_LIST; int fame_list_size_taekwon = MAX_FAME_LIST; // Char-server-side stored fame lists [DracoRPG] @@ -173,7 +189,7 @@ static DBMap* auth_db; // int account_id -> struct auth_node* // Online User Database //----------------------------------------------------- -static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, intptr_t data); +static int chardb_waiting_disconnect(int tid, int64 tick, int id, intptr_t data); int delete_char_sql(int char_id); /** @@ -185,7 +201,7 @@ static DBData create_online_char_data(DBKey key, va_list args) CREATE(character, struct online_char_data, 1); character->account_id = key.i; character->char_id = -1; - character->server = -1; + character->server = -1; character->pincode_enable = -1; character->fd = -1; character->waiting_disconnect = INVALID_TIMER; @@ -208,7 +224,7 @@ void set_char_charselect(int account_id) character->pincode_enable = *pincode->charselect + *pincode->enabled; if(character->waiting_disconnect != INVALID_TIMER) { - iTimer->delete_timer(character->waiting_disconnect, chardb_waiting_disconnect); + timer->delete(character->waiting_disconnect, chardb_waiting_disconnect); character->waiting_disconnect = INVALID_TIMER; } @@ -249,7 +265,7 @@ void set_char_online(int map_id, int char_id, int account_id) //Get rid of disconnect timer if(character->waiting_disconnect != INVALID_TIMER) { - iTimer->delete_timer(character->waiting_disconnect, chardb_waiting_disconnect); + timer->delete(character->waiting_disconnect, chardb_waiting_disconnect); character->waiting_disconnect = INVALID_TIMER; } @@ -294,7 +310,7 @@ void set_char_offline(int char_id, int account_id) server[character->server].users--; if(character->waiting_disconnect != INVALID_TIMER){ - iTimer->delete_timer(character->waiting_disconnect, chardb_waiting_disconnect); + timer->delete(character->waiting_disconnect, chardb_waiting_disconnect); character->waiting_disconnect = INVALID_TIMER; } @@ -324,15 +340,15 @@ void set_char_offline(int char_id, int account_id) static int char_db_setoffline(DBKey key, DBData *data, va_list ap) { struct online_char_data* character = (struct online_char_data*)DB->data2ptr(data); - int server = va_arg(ap, int); - if (server == -1) { + int server_id = va_arg(ap, int); + if (server_id == -1) { character->char_id = -1; character->server = -1; if(character->waiting_disconnect != INVALID_TIMER){ - iTimer->delete_timer(character->waiting_disconnect, chardb_waiting_disconnect); + timer->delete(character->waiting_disconnect, chardb_waiting_disconnect); character->waiting_disconnect = INVALID_TIMER; } - } else if (character->server == server) + } else if (character->server == server_id) character->server = -2; //In some map server that we aren't connected to. return 0; } @@ -457,7 +473,8 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p) (p->ele_id != cp->ele_id) || (p->shield != cp->shield) || (p->head_top != cp->head_top) || (p->head_mid != cp->head_mid) || (p->head_bottom != cp->head_bottom) || (p->delete_date != cp->delete_date) || (p->rename != cp->rename) || (p->slotchange != cp->slotchange) || (p->robe != cp->robe) || - (p->show_equip != cp->show_equip) || (p->allow_party != cp->allow_party) + (p->show_equip != cp->show_equip) || (p->allow_party != cp->allow_party) || (p->font != cp->font) || + (p->uniqueitem_counter != cp->uniqueitem_counter ) ) { //Save status unsigned int opt = 0; @@ -473,7 +490,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p) "`option`='%d',`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'" + "`delete_date`='%lu',`robe`='%d',`slotchange`='%d', `char_opt`='%u', `font`='%u', `uniqueitem_counter` ='%u'" " WHERE `account_id`='%d' AND `char_id` = '%d'", char_db, p->base_level, p->job_level, p->base_exp, p->job_exp, p->zeny, @@ -484,7 +501,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p) mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y, mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, p->rename, (unsigned long)p->delete_date, // FIXME: platform-dependent size - p->robe,p->slotchange,opt, + p->robe,p->slotchange,opt,p->font,p->uniqueitem_counter, p->account_id, p->char_id) ) { Sql_ShowDebug(sql_handle); @@ -492,6 +509,14 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p) } else strcat(save_status, " status"); } + + if( p->bank_vault != cp->bank_vault || p->mod_exp != cp->mod_exp || p->mod_drop != cp->mod_drop || p->mod_death != cp->mod_death ) { + if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`bank_vault`,`base_exp`,`base_drop`,`base_death`) VALUES ('%d','%d','%d','%d','%d')",account_data_db,p->account_id,p->bank_vault,p->mod_exp,p->mod_drop,p->mod_death) ) { + Sql_ShowDebug(sql_handle); + errors++; + } else + strcat(save_status, " accdata"); + } //Values that will seldom change (to speed up saving) if ( @@ -670,6 +695,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p) strcat(save_status, " hotkeys"); } #endif + StrBuf->Destroy(&buf); if (save_status[0]!='\0' && save_log) ShowInfo("Saved char %d - %s:%s.\n", char_id, p->name, save_status); @@ -709,8 +735,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit // 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`"); - for( j = 0; j < MAX_SLOTS; ++j ) + StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`"); + for( j = 0; j < MAX_SLOTS; ++j ) StrBuf->Printf(&buf, ", `card%d`", j); StrBuf->Printf(&buf, " FROM `%s` WHERE `%s`='%d'", tablename, selectoption, id); @@ -727,13 +753,14 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit 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_USHORT, &item.equip, 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); - for( j = 0; j < MAX_SLOTS; ++j ) - SQL->StmtBindColumn(stmt, 8+j, SQLDT_SHORT, &item.card[j], 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); // bit array indicating which inventory items have already been matched flag = (bool*) aCalloc(max, sizeof(bool)); @@ -760,17 +787,18 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit items[i].identify == item.identify && items[i].refine == item.refine && items[i].attribute == item.attribute && - items[i].expire_time == item.expire_time ) + items[i].expire_time == item.expire_time && + 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'", - tablename, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time); - for( j = 0; j < MAX_SLOTS; ++j ) + StrBuf->Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `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 )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); + StrBuf->Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id); if( SQL_ERROR == SQL->QueryStr(sql_handle, StrBuf->Value(&buf)) ) { @@ -795,8 +823,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit SQL->StmtFree(stmt); StrBuf->Clear(&buf); - StrBuf->Printf(&buf, "INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `unique_id`", tablename, selectoption); - for( j = 0; j < MAX_SLOTS; ++j ) + 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 ) StrBuf->Printf(&buf, ", `card%d`", j); StrBuf->AppendStr(&buf, ") VALUES "); @@ -813,15 +841,12 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit else found = true; - StrBuf->Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%"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].unique_id); - for( j = 0; j < MAX_SLOTS; ++j ) + StrBuf->Printf(&buf, "('%d', '%d', '%d', '%d', '%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 ) StrBuf->Printf(&buf, ", '%d'", items[i].card[j]); StrBuf->AppendStr(&buf, ")"); - - updateLastUid(items[i].unique_id); // Unique Non Stackable Item ID } - dbUpdateUid(sql_handle); // Unique Non Stackable Item ID if( found && SQL_ERROR == SQL->QueryStr(sql_handle, StrBuf->Value(&buf)) ) { @@ -853,8 +878,8 @@ int inventory_to_sql(const struct item items[], int max, int id) { // 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`"); - for( j = 0; j < MAX_SLOTS; ++j ) + 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); @@ -871,14 +896,15 @@ int inventory_to_sql(const struct item items[], int max, int id) { 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_USHORT, &item.equip, 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); - 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_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)); @@ -904,15 +930,16 @@ int inventory_to_sql(const struct item items[], int max, int id) { items[i].refine == item.refine && items[i].attribute == item.attribute && items[i].expire_time == item.expire_time && - items[i].favorite == item.favorite ) + 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'", - inventory_db, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite); - for( j = 0; j < MAX_SLOTS; ++j ) - StrBuf->Printf(&buf, ", `card%d`=%d", j, items[i].card[j]); + 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(sql_handle, StrBuf->Value(&buf)) ) { @@ -935,8 +962,8 @@ int inventory_to_sql(const struct item items[], int max, int id) { SQL->StmtFree(stmt); StrBuf->Clear(&buf); - StrBuf->Printf(&buf, "INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `unique_id`", inventory_db); - for( j = 0; j < MAX_SLOTS; ++j ) + 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 "); @@ -952,15 +979,12 @@ int inventory_to_sql(const struct item items[], int max, int id) { else found = true; - StrBuf->Printf(&buf, "('%d', '%d', '%d', '%d', '%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].favorite, items[i].unique_id); - for( j = 0; j < MAX_SLOTS; ++j ) + 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, ")"); - - updateLastUid(items[i].unique_id);// Unique Non Stackable Item ID } - dbUpdateUid(sql_handle); if( found && SQL_ERROR == SQL->QueryStr(sql_handle, StrBuf->Value(&buf)) ) { Sql_ShowDebug(sql_handle); @@ -984,6 +1008,7 @@ int mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) struct mmo_charstatus p; int j = 0, i; char last_map[MAP_NAME_LENGTH_EXT]; + time_t unban_time = 0; stmt = SQL->StmtMalloc(sql_handle); if( stmt == NULL ) { @@ -992,8 +1017,10 @@ int mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) } memset(&p, 0, sizeof(p)); - for(i = 0 ; i < MAX_CHARS; i++ ) + for(i = 0 ; i < MAX_CHARS; i++ ) { sd->found_char[i] = -1; + sd->unban_time[i] = 0; + } // read char data if( SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT " @@ -1001,7 +1028,7 @@ int mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) "`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`," "`status_point`,`skill_point`,`option`,`karma`,`manner`,`hair`,`hair_color`," "`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`rename`,`delete_date`," - "`robe`,`slotchange`" + "`robe`,`slotchange`,`unban_time`" " FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", char_db, sd->account_id, MAX_CHARS) || SQL_ERROR == SQL->StmtExecute(stmt) || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p.char_id, 0, NULL, NULL) @@ -1037,10 +1064,11 @@ int mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) || SQL_ERROR == SQL->StmtBindColumn(stmt, 30, SQLDT_SHORT, &p.head_mid, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 31, SQLDT_SHORT, &p.head_bottom, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 32, SQLDT_STRING, &last_map, sizeof(last_map), NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 33, SQLDT_USHORT, &p.rename, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 33, SQLDT_USHORT, &p.rename, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 34, SQLDT_UINT32, &p.delete_date, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 35, SQLDT_SHORT, &p.robe, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 36, SQLDT_USHORT, &p.slotchange, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 37, SQLDT_LONG, &unban_time, 0, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); @@ -1049,8 +1077,9 @@ int mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) } for( i = 0; i < MAX_CHARS && SQL_SUCCESS == SQL->StmtNextRow(stmt); i++ ) { - p.last_point.map = mapindex_name2id(last_map); + p.last_point.map = mapindex->name2id(last_map); sd->found_char[p.slot] = p.char_id; + sd->unban_time[p.slot] = unban_time; j += mmo_char_tobuf(WBUFP(buf, j), &p); } @@ -1080,6 +1109,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything int hotkey_num; #endif unsigned int opt; + int account_id; memset(p, 0, sizeof(struct mmo_charstatus)); @@ -1099,7 +1129,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything "`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`," "`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`," "`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`,`slotchange`," - "`char_opt`" + "`char_opt`,`font`,`uniqueitem_counter`" " FROM `%s` WHERE `char_id`=? LIMIT 1", char_db) || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0) || SQL_ERROR == SQL->StmtExecute(stmt) @@ -1132,7 +1162,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything || SQL_ERROR == SQL->StmtBindColumn(stmt, 26, SQLDT_INT, &p->guild_id, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 27, SQLDT_INT, &p->pet_id, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 28, SQLDT_INT, &p->hom_id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 29, SQLDT_INT, &p->ele_id, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 29, SQLDT_INT, &p->ele_id, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 30, SQLDT_SHORT, &p->hair, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 31, SQLDT_SHORT, &p->hair_color, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 32, SQLDT_SHORT, &p->clothes_color, 0, NULL, NULL) @@ -1152,11 +1182,13 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything || SQL_ERROR == SQL->StmtBindColumn(stmt, 46, SQLDT_INT, &p->mother, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 47, SQLDT_INT, &p->child, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 48, SQLDT_INT, &p->fame, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 49, SQLDT_USHORT, &p->rename, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 49, SQLDT_USHORT, &p->rename, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 50, SQLDT_UINT32, &p->delete_date, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 51, SQLDT_SHORT, &p->robe, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 52, SQLDT_USHORT, &p->slotchange, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 53, SQLDT_UINT, &opt, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 53, SQLDT_UINT, &opt, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 54, SQLDT_UCHAR, &p->font, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 55, SQLDT_UINT, &p->uniqueitem_counter, 0, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); @@ -1169,17 +1201,20 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything SQL->StmtFree(stmt); return 0; } - p->last_point.map = mapindex_name2id(last_map); - p->save_point.map = mapindex_name2id(save_map); + + account_id = p->account_id; + + p->last_point.map = mapindex->name2id(last_map); + p->save_point.map = mapindex->name2id(save_map); if( p->last_point.map == 0 ) { - p->last_point.map = (unsigned short)strdb_iget(mapindex_db, MAP_DEFAULT); + p->last_point.map = (unsigned short)strdb_iget(mapindex->db, MAP_DEFAULT); p->last_point.x = MAP_DEFAULT_X; p->last_point.y = MAP_DEFAULT_Y; } if( p->save_point.map == 0 ) { - p->save_point.map = (unsigned short)strdb_iget(mapindex_db, MAP_DEFAULT); + p->save_point.map = (unsigned short)strdb_iget(mapindex->db, MAP_DEFAULT); p->save_point.x = MAP_DEFAULT_X; p->save_point.y = MAP_DEFAULT_Y; } @@ -1203,35 +1238,36 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything SqlStmt_ShowDebug(stmt); for( i = 0; i < MAX_MEMOPOINTS && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i ) { - tmp_point.map = mapindex_name2id(point_map); + tmp_point.map = mapindex->name2id(point_map); memcpy(&p->memo_point[i], &tmp_point, sizeof(tmp_point)); } strcat(t_msg, " memo"); //read inventory - //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `expire_time`, `favorite`, `unique_id`) - StrBuf->Init(&buf); - StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `unique_id`"); - for( i = 0; i < MAX_SLOTS; ++i ) + //`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 ) StrBuf->Printf(&buf, ", `card%d`", i); StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", inventory_db, MAX_INVENTORY); if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0) || SQL_ERROR == SQL->StmtExecute(stmt) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_item.id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &tmp_item.nameid, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &tmp_item.amount, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_USHORT, &tmp_item.equip, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &tmp_item.identify, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &tmp_item.refine, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.favorite, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) ) - SqlStmt_ShowDebug(stmt); + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_item.id, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &tmp_item.nameid, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &tmp_item.amount, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &tmp_item.equip, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &tmp_item.identify, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &tmp_item.refine, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.favorite, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_UCHAR, &tmp_item.bound, 0, NULL, NULL) + || 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, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) + if( SQL_ERROR == SQL->StmtBindColumn(stmt, 11+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); for( i = 0; i < MAX_INVENTORY && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i ) @@ -1240,28 +1276,30 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything strcat(t_msg, " inventory"); //read cart - //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, expire_time`, `unique_id`) - StrBuf->Clear(&buf); - StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `unique_id`"); - for( j = 0; j < MAX_SLOTS; ++j ) + //`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 ) StrBuf->Printf(&buf, ", `card%d`", j); StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", cart_db, MAX_CART); + memset(&tmp_item, 0, sizeof(tmp_item)); if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0) || SQL_ERROR == SQL->StmtExecute(stmt) || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_item.id, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &tmp_item.nameid, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &tmp_item.amount, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_USHORT, &tmp_item.equip, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &tmp_item.equip, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &tmp_item.identify, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &tmp_item.refine, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) ) - SqlStmt_ShowDebug(stmt); + || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_UCHAR, &tmp_item.bound, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, 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, 9+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) + if( SQL_ERROR == SQL->StmtBindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); for( i = 0; i < MAX_CART && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i ) @@ -1333,8 +1371,23 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything mercenary_owner_fromsql(char_id, p); strcat(t_msg, " mercenary"); + /* default */ + p->mod_exp = p->mod_drop = p->mod_death = 100; + + //`account_data` (`account_id`,`bank_vault`,`base_exp`,`base_drop`,`base_death`) + if( SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `bank_vault`,`base_exp`,`base_drop`,`base_death` FROM `%s` WHERE `account_id`=? LIMIT 1", account_data_db) + || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &account_id, 0) + || SQL_ERROR == SQL->StmtExecute(stmt) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p->bank_vault, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_USHORT, &p->mod_exp, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_USHORT, &p->mod_drop, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_USHORT, &p->mod_death, 0, NULL, NULL) ) + SqlStmt_ShowDebug(stmt); + + 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 successfuly! + if (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); @@ -1360,7 +1413,7 @@ int mmo_char_sql_init(void) //and send the loginserver the new state.... // Force all users offline in sql when starting char-server - // (useful when servers crashs and don't clean the database) + // (useful when servers crashes and don't clean the database) set_all_offline_sql(); return 0; @@ -1415,7 +1468,7 @@ bool char_slotchange(struct char_session_data *sd, int fd, unsigned short from, } //----------------------------------- -// Function to change chararcter's names +// Function to change character's names //----------------------------------- int rename_char_sql(struct char_session_data *sd, int char_id) { @@ -1456,9 +1509,10 @@ int rename_char_sql(struct char_session_data *sd, int char_id) // log change if( log_char ) { - if( SQL_ERROR == SQL->Query(sql_handle, "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" - "VALUES (NOW(), '%s', '%d', '%d', '%s', '0', '0', '0', '0', '0', '0', '0', '0')", - charlog_db, "change char name", sd->account_id, char_dat.slot, esc_name) ) + if( SQL_ERROR == SQL->Query(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(sql_handle); } @@ -1486,9 +1540,9 @@ int check_char_name(char * name, char * esc_name) if( strcmpi(name, wisp_server_name) == 0 ) return -1; // nick reserved for internal server messages - // Check Authorised letters/symbols in the name of the character + // Check Authorized letters/symbols in the name of the character if( char_name_option == 1 ) - { // only letters/symbols in char_name_letters are authorised + { // only letters/symbols in char_name_letters are authorized for( i = 0; i < NAME_LENGTH && name[i]; i++ ) if( strchr(char_name_letters, name[i]) == NULL ) return -2; @@ -1497,7 +1551,7 @@ int check_char_name(char * name, char * esc_name) { // letters/symbols in char_name_letters are forbidden for( i = 0; i < NAME_LENGTH && name[i]; i++ ) if( strchr(char_name_letters, name[i]) != NULL ) - return -2; + return -5; } if( name_ignoring_case ) { if( SQL_ERROR == SQL->Query(sql_handle, "SELECT 1 FROM `%s` WHERE BINARY `name` = '%s' LIMIT 1", char_db, esc_name) ) { @@ -1516,9 +1570,16 @@ int check_char_name(char * name, char * esc_name) return 0; } -//----------------------------------- -// Function to create a new character -//----------------------------------- +/** + * Creates a new character + * Return values: + * -1: 'Charname already exists' + * -2: 'Char creation denied'/ Unknown error + * -3: 'You are underaged' + * -4: 'You are not eligible to open the Character Slot.' + * -5: 'Symbols in Character Names are forbidden' + * char_id: Success + **/ #if PACKETVER >= 20120307 int make_new_char_sql(struct char_session_data* sd, char* name_, int slot, int hair_color, int hair_style) { int str = 1, agi = 1, vit = 1, int_ = 1, dex = 1, luk = 1; @@ -1528,7 +1589,7 @@ int make_new_char_sql(struct char_session_data* sd, char* name_, int str, int ag char name[NAME_LENGTH]; char esc_name[NAME_LENGTH*2+1]; - int char_id, flag, k; + int char_id, flag, k, l; safestrncpy(name, name_, NAME_LENGTH); normalize_name(name,TRIM_CHARS); @@ -1557,13 +1618,6 @@ int make_new_char_sql(struct char_session_data* sd, char* name_, int str, int ag if( sd->found_char[slot] != -1 ) return -2; /* character account limit exceeded */ - // validation success, log result - if (log_char) { - if( SQL_ERROR == SQL->Query(sql_handle, "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)" - "VALUES (NOW(), '%s', '%d', '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d')", - charlog_db, "make new char", sd->account_id, slot, esc_name, str, agi, vit, int_, dex, luk, hair_style, hair_color) ) - Sql_ShowDebug(sql_handle); - } #if PACKETVER >= 20120307 //Insert the new char entry to the database if( SQL_ERROR == SQL->Query(sql_handle, "INSERT INTO `%s` (`account_id`, `char_num`, `name`, `zeny`, `status_point`,`str`, `agi`, `vit`, `int`, `dex`, `luk`, `max_hp`, `hp`," @@ -1591,11 +1645,40 @@ int make_new_char_sql(struct char_session_data* sd, char* name_, int str, int ag #endif //Retrieve the newly auto-generated char id char_id = (int)SQL->LastInsertId(sql_handle); + + if( !char_id ) + return -2; + + // Validation success, log result + if (log_char) { + if( SQL_ERROR == SQL->Query(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) ) + Sql_ShowDebug(sql_handle); + } + //Give the char the default items - - for (k = 0; k < ARRAYLENGTH(start_items) && start_items[k] != 0; k += 2) { - if( SQL_ERROR == SQL->Query(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(sql_handle); + 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(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(sql_handle); + } + else if( start_items[k+2] == 0 ) + { + // Non-stackable items should have their own entries (issue: 7279) + for( l = 0; l < start_items[k+1]; l++ ) + { + if( SQL_ERROR == SQL->Query(sql_handle, + "INSERT INTO `%s` (`char_id`,`nameid`, `amount`, `identify`) VALUES ('%d', '%d', '%d', '%d')", + inventory_db, char_id, start_items[k], 1, 1) + ) + Sql_ShowDebug(sql_handle); + } + } } ShowInfo("Created char: account: %d, char: %d, slot: %d, name: %s\n", sd->account_id, char_id, slot, name); @@ -1655,8 +1738,8 @@ int delete_char_sql(int char_id) SQL->GetData(sql_handle, 6, &data, NULL); partner_id = atoi(data); SQL->GetData(sql_handle, 7, &data, NULL); father_id = atoi(data); SQL->GetData(sql_handle, 8, &data, NULL); mother_id = atoi(data); - SQL->GetData(sql_handle, 9, &data, NULL); - elemental_id = atoi(data); + SQL->GetData(sql_handle, 9, &data, NULL); + elemental_id = atoi(data); SQL->EscapeStringLen(sql_handle, esc_name, name, min(len, NAME_LENGTH)); SQL->FreeResult(sql_handle); @@ -1697,7 +1780,7 @@ int delete_char_sql(int char_id) /* delete char's pet */ //Delete the hatched pet if you have one... - if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d' AND `incuvate` = '0'", pet_db, char_id) ) + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d' AND `incubate` = '0'", pet_db, char_id) ) Sql_ShowDebug(sql_handle); //Delete all pets that are stored in eggs (inventory + cart) @@ -1710,9 +1793,9 @@ int delete_char_sql(int char_id) if( hom_id ) mapif_homunculus_delete(hom_id); - /* remove elemental */ - if (elemental_id) - mapif_elemental_delete(elemental_id); + /* remove elemental */ + if (elemental_id) + mapif_elemental_delete(elemental_id); /* remove mercenary data */ mercenary_owner_delete(char_id); @@ -1745,7 +1828,9 @@ int delete_char_sql(int char_id) Sql_ShowDebug(sql_handle); /* delete character registry */ - if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'", reg_db, char_id) ) + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", char_reg_str_db, char_id) ) + Sql_ShowDebug(sql_handle); + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", char_reg_num_db, char_id) ) Sql_ShowDebug(sql_handle); /* delete skills */ @@ -1762,15 +1847,16 @@ int delete_char_sql(int char_id) Sql_ShowDebug(sql_handle); #endif - if (log_char) { - if( SQL_ERROR == SQL->Query(sql_handle, "INSERT INTO `%s`(`time`, `account_id`,`char_num`,`char_msg`,`name`) VALUES (NOW(), '%d', '%d', 'Deleted char (CID %d)', '%s')", - charlog_db, account_id, 0, char_id, esc_name) ) - Sql_ShowDebug(sql_handle); - } - /* delete character */ if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", char_db, char_id) ) Sql_ShowDebug(sql_handle); + else if( log_char ) { + if( SQL_ERROR == SQL->Query(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(sql_handle); + } /* No need as we used inter_guild_leave [Skotlex] // Also delete info from guildtables. @@ -1807,8 +1893,7 @@ int count_users(void) // Used in packets 0x6b (chars info) and 0x6d (new char info) // Returns the size #define MAX_CHAR_BUF 144 //Max size (for WFIFOHEAD calls) -int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) -{ +int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) { unsigned short offset = 0; uint8* buf; @@ -1827,10 +1912,15 @@ int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) WBUFL(buf,32) = p->karma; WBUFL(buf,36) = p->manner; WBUFW(buf,40) = min(p->status_point, INT16_MAX); +#if PACKETVER > 20081217 WBUFL(buf,42) = p->hp; WBUFL(buf,46) = p->max_hp; offset+=4; buf = WBUFP(buffer,offset); +#else + WBUFW(buf,42) = min(p->hp, INT16_MAX); + WBUFW(buf,44) = min(p->max_hp, INT16_MAX); +#endif 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; @@ -1856,14 +1946,16 @@ int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) WBUFB(buf,102) = min(p->dex, UINT8_MAX); WBUFB(buf,103) = min(p->luk, UINT8_MAX); WBUFW(buf,104) = p->slot; +#if PACKETVER >= 20061023 WBUFW(buf,106) = ( p->rename > 0 ) ? 0 : 1; 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), (char*)WBUFP(buf,108)); offset += MAP_NAME_LENGTH_EXT; #endif #if PACKETVER >= 20100803 - WBUFL(buf,124) = TOL(p->delete_date); + WBUFL(buf,124) = (int)p->delete_date; offset += 4; #endif #if PACKETVER >= 20110111 @@ -1883,6 +1975,7 @@ int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) return 106+offset; } + /* Made Possible by Yommy~! <3 */ void mmo_char_send099d(int fd, struct char_session_data *sd) { WFIFOHEAD(fd,4 + (MAX_CHARS*MAX_CHAR_BUF)); @@ -1890,6 +1983,44 @@ void mmo_char_send099d(int fd, struct char_session_data *sd) { WFIFOW(fd,2) = mmo_chars_fromsql(sd, WFIFOP(fd,4)) + 4; WFIFOSET(fd,WFIFOW(fd,2)); } +/* Sends character ban list */ +/* Made Possible by Yommy~! <3 */ +void mmo_char_send020d(int fd, struct char_session_data *sd) { + int i; + time_t now = time(NULL); + + ARR_FIND(0, MAX_CHARS, i, sd->unban_time[i]); + + if( i != MAX_CHARS ) { + int c; + + WFIFOHEAD(fd, 4 + (MAX_CHARS*24)); + + WFIFOW(fd, 0) = 0x20d; + + 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"); + + if( sd->unban_time[i] > now ) + WFIFOL(fd, 4 + (24*c)) = sd->found_char[i]; + else { + /* reset -- client keeps this information even if you logout so we need to clear */ + WFIFOL(fd, 4 + (24*c)) = 0; + /* also update on mysql */ + sd->unban_time[i] = 0; + if( SQL_ERROR == SQL->Query(sql_handle, "UPDATE `%s` SET `unban_time`='0' WHERE `char_id`='%d' LIMIT 1", char_db, sd->found_char[i]) ) + Sql_ShowDebug(sql_handle); + } + c++; + } + } + + WFIFOW(fd, 2) = 4 + (24*c); + + WFIFOSET(fd, WFIFOW(fd, 2)); + } +} int mmo_char_send006b(int fd, struct char_session_data* sd); //---------------------------------------- // [Ind/Hercules] notify client about charselect window data @@ -1917,7 +2048,7 @@ int mmo_char_send006b(int fd, struct char_session_data* sd) int j, offset = 0; #if PACKETVER >= 20100413 offset += 3; -#endif +#endif if (save_log) ShowInfo("Loading Char Data ("CL_BOLD"%d"CL_RESET")\n",sd->account_id); @@ -2011,8 +2142,8 @@ void disconnect_player(int account_id) struct char_session_data* sd; // disconnect player if online on char-server - ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == account_id ); - if( i < fd_max ) + ARR_FIND( 0, sockt->fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->account_id == account_id ); + if( i < sockt->fd_max ) set_eof(i); } @@ -2026,7 +2157,7 @@ static void char_auth_ok(int fd, struct char_session_data *sd) { //Character already online. KICK KICK KICK mapif_disconnectplayer(server[character->server].fd, character->account_id, character->char_id, 2); if (character->waiting_disconnect == INVALID_TIMER) - character->waiting_disconnect = iTimer->add_timer(iTimer->gettick()+20000, chardb_waiting_disconnect, character->account_id, 0); + character->waiting_disconnect = timer->add(timer->gettick()+20000, chardb_waiting_disconnect, character->account_id, 0); if (character) character->pincode_enable = -1; WFIFOHEAD(fd,3); @@ -2063,7 +2194,7 @@ static void char_auth_ok(int fd, struct char_session_data *sd) // continues when account data is received... } -int send_accounts_tologin(int tid, unsigned int tick, int id, intptr_t data); +int send_accounts_tologin(int tid, int64 tick, int id, intptr_t data); void mapif_server_reset(int id); @@ -2071,7 +2202,7 @@ void mapif_server_reset(int id); void loginif_reset(void) { int id; - // TODO kick everyone out and reset everything or wait for connect and try to reaquire locks [FlavioJS] + // TODO kick everyone out and reset everything or wait for connect and try to reacquire locks [FlavioJS] for( id = 0; id < ARRAYLENGTH(server); ++id ) mapif_server_reset(id); flush_fifos(); @@ -2105,7 +2236,7 @@ void loginif_on_ready(void) loginif_check_shutdown(); //Send online accounts to login server. - send_accounts_tologin(INVALID_TIMER, iTimer->gettick(), 0, 0); + send_accounts_tologin(INVALID_TIMER, timer->gettick(), 0, 0); // if no map-server already connected, display a message... ARR_FIND( 0, ARRAYLENGTH(server), i, server[i].fd > 0 && server[i].map[0] ); @@ -2116,7 +2247,6 @@ void loginif_on_ready(void) int parse_fromlogin(int fd) { struct char_session_data* sd = NULL; - int i; // only process data from the login-server if( fd != login_fd ) { @@ -2131,7 +2261,7 @@ int parse_fromlogin(int fd) { loginif_on_disconnect(); return 0; } else if ( session[fd]->flag.ping ) {/* we've reached stall time */ - if( DIFF_TICK(last_tick, session[fd]->rdata_tick) > (stall_time * 2) ) {/* we can't wait any longer */ + if( DIFF_TICK(sockt->last_tick, session[fd]->rdata_tick) > (sockt->stall_time * 2) ) {/* we can't wait any longer */ set_eof(fd); return 0; } else if( session[fd]->flag.ping != 2 ) { /* we haven't sent ping out yet */ @@ -2147,9 +2277,16 @@ int parse_fromlogin(int fd) { while(RFIFOREST(fd) >= 2) { uint16 command = RFIFOW(fd,0); + + if( HPM->packetsc[hpParse_FromLogin] ) { + int success = HPM->parse_packets(fd,hpParse_FromLogin); + if( success == 1 ) continue; + else if( success == 2 ) return 0; + } + switch( command ) { - // acknowledgement of connect-to-loginserver request + // acknowledgment of connect-to-loginserver request case 0x2711: if (RFIFOREST(fd) < 3) return 0; @@ -2169,9 +2306,9 @@ int parse_fromlogin(int fd) { RFIFOSKIP(fd,3); break; - // acknowledgement of account authentication request + // acknowledgment of account authentication request case 0x2713: - if (RFIFOREST(fd) < 25) + if (RFIFOREST(fd) < 33) return 0; { int account_id = RFIFOL(fd,2); @@ -2182,7 +2319,9 @@ int parse_fromlogin(int fd) { int request_id = RFIFOL(fd,16); uint32 version = RFIFOL(fd,20); uint8 clienttype = RFIFOB(fd,24); - RFIFOSKIP(fd,25); + int group_id = RFIFOL(fd,25); + unsigned int expiration_time = RFIFOL(fd, 29); + RFIFOSKIP(fd,33); if( session_isActive(request_id) && (sd=(struct char_session_data*)session[request_id]->session_data) && !sd->auth && sd->account_id == account_id && sd->login_id1 == login_id1 && sd->login_id2 == login_id2 && sd->sex == sex ) @@ -2190,30 +2329,46 @@ int parse_fromlogin(int fd) { int client_fd = request_id; sd->version = version; sd->clienttype = clienttype; - switch( result ) - { - case 0:// ok - char_auth_ok(client_fd, sd); - break; - case 1:// auth failed - WFIFOHEAD(client_fd,3); - WFIFOW(client_fd,0) = 0x6c; - WFIFOB(client_fd,2) = 0;// rejected from server - WFIFOSET(client_fd,3); - break; + switch( result ) { + case 0:// ok + /* restrictions apply */ + if( char_server_type == CST_MAINTENANCE && group_id < char_maintenance_min_group_id ) { + WFIFOHEAD(client_fd,3); + WFIFOW(client_fd,0) = 0x6c; + WFIFOB(client_fd,2) = 0;// rejected from server + WFIFOSET(client_fd,3); + break; + } + /* the client will already deny this request, this check is to avoid someone bypassing. */ + if( char_server_type == CST_PAYING && (time_t)expiration_time < time(NULL) ) { + WFIFOHEAD(client_fd,3); + WFIFOW(client_fd,0) = 0x6c; + WFIFOB(client_fd,2) = 0;// rejected from server + WFIFOSET(client_fd,3); + break; + } + char_auth_ok(client_fd, sd); + break; + case 1:// auth failed + WFIFOHEAD(client_fd,3); + WFIFOW(client_fd,0) = 0x6c; + WFIFOB(client_fd,2) = 0;// rejected from server + WFIFOSET(client_fd,3); + break; } } } break; case 0x2717: // account data + { + int i; if (RFIFOREST(fd) < 72) return 0; // find the authenticated session with this account id - ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->auth && sd->account_id == RFIFOL(fd,2) ); - if( i < fd_max ) { - int server_id; + ARR_FIND( 0, sockt->fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->auth && sd->account_id == RFIFOL(fd,2) ); + if( i < sockt->fd_max ) { memcpy(sd->email, RFIFOP(fd,6), 40); sd->expiration_time = (time_t)RFIFOL(fd,46); sd->group_id = RFIFOB(fd,50); @@ -2226,10 +2381,8 @@ int parse_fromlogin(int fd) { safestrncpy(sd->birthdate, (const char*)RFIFOP(fd,52), sizeof(sd->birthdate)); safestrncpy(sd->pincode, (const char*)RFIFOP(fd,63), sizeof(sd->pincode)); sd->pincode_change = RFIFOL(fd,68); - ARR_FIND( 0, ARRAYLENGTH(server), server_id, server[server_id].fd > 0 && server[server_id].map[0] ); // continued from char_auth_ok... - if( server_id == ARRAYLENGTH(server) || //server not online, bugreport:2359 - (max_connect_user == 0 && sd->group_id != gm_allow_group) || + if( (max_connect_user == 0 && sd->group_id != gm_allow_group) || ( max_connect_user > 0 && count_users() >= max_connect_user && sd->group_id != gm_allow_group ) ) { // refuse connection (over populated) WFIFOHEAD(i,3); @@ -2243,12 +2396,16 @@ int parse_fromlogin(int fd) { #else mmo_char_send006b(i, sd); #endif + #if PACKETVER >= 20060819 + mmo_char_send020d(i, sd); + #endif #if PACKETVER >= 20110309 pincode->handle(i, sd); #endif } } RFIFOSKIP(fd,72); + } break; // login-server alive packet @@ -2266,70 +2423,82 @@ int parse_fromlogin(int fd) { { unsigned char buf[7]; + int char_id[MAX_CHARS]; + int class_[MAX_CHARS]; + int guild_id[MAX_CHARS]; + int num; + int i; + char* data; + struct auth_node* node; + int acc = RFIFOL(fd,2); int sex = RFIFOB(fd,6); + RFIFOSKIP(fd,7); - if( acc > 0 ) - {// TODO: Is this even possible? - int char_id[MAX_CHARS]; - int class_[MAX_CHARS]; - int guild_id[MAX_CHARS]; - int num; - char* data; + // This should _never_ happen + if( acc <= 0 ) { + ShowError("Received invalid account id from login server! (aid: %d)\n", acc); + return 0; + } - struct auth_node* node = (struct auth_node*)idb_get(auth_db, acc); - if( node != NULL ) - node->sex = sex; + node = (struct auth_node*)idb_get(auth_db, acc); + if( node != NULL ) + node->sex = sex; - // get characters - if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `char_id`,`class`,`guild_id` FROM `%s` WHERE `account_id` = '%d'", char_db, acc) ) - Sql_ShowDebug(sql_handle); - for( i = 0; i < MAX_CHARS && SQL_SUCCESS == SQL->NextRow(sql_handle); ++i ) + // get characters + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `char_id`,`class`,`guild_id` FROM `%s` WHERE `account_id` = '%d'", char_db, acc) ) + Sql_ShowDebug(sql_handle); + for( i = 0; i < MAX_CHARS && SQL_SUCCESS == SQL->NextRow(sql_handle); ++i ) + { + SQL->GetData(sql_handle, 0, &data, NULL); char_id[i] = atoi(data); + SQL->GetData(sql_handle, 1, &data, NULL); class_[i] = atoi(data); + SQL->GetData(sql_handle, 2, &data, NULL); guild_id[i] = atoi(data); + } + num = i; + for( i = 0; i < num; ++i ) + { + if( class_[i] == JOB_BARD || class_[i] == JOB_DANCER || + class_[i] == JOB_CLOWN || class_[i] == JOB_GYPSY || + class_[i] == JOB_BABY_BARD || class_[i] == JOB_BABY_DANCER || + class_[i] == JOB_MINSTREL || class_[i] == JOB_WANDERER || + class_[i] == JOB_MINSTREL_T || class_[i] == JOB_WANDERER_T || + class_[i] == JOB_BABY_MINSTREL || class_[i] == JOB_BABY_WANDERER || + class_[i] == JOB_KAGEROU || class_[i] == JOB_OBORO ) { - SQL->GetData(sql_handle, 0, &data, NULL); char_id[i] = atoi(data); - SQL->GetData(sql_handle, 1, &data, NULL); class_[i] = atoi(data); - SQL->GetData(sql_handle, 2, &data, NULL); guild_id[i] = atoi(data); + // job modification + if( class_[i] == JOB_BARD || class_[i] == JOB_DANCER ) + class_[i] = (sex ? JOB_BARD : JOB_DANCER); + else if( class_[i] == JOB_CLOWN || class_[i] == JOB_GYPSY ) + class_[i] = (sex ? JOB_CLOWN : JOB_GYPSY); + else if( class_[i] == JOB_BABY_BARD || class_[i] == JOB_BABY_DANCER ) + class_[i] = (sex ? JOB_BABY_BARD : JOB_BABY_DANCER); + else if( class_[i] == JOB_MINSTREL || class_[i] == JOB_WANDERER ) + class_[i] = (sex ? JOB_MINSTREL : JOB_WANDERER); + else if( class_[i] == JOB_MINSTREL_T || class_[i] == JOB_WANDERER_T ) + class_[i] = (sex ? JOB_MINSTREL_T : JOB_WANDERER_T); + else if( class_[i] == JOB_BABY_MINSTREL || class_[i] == JOB_BABY_WANDERER ) + class_[i] = (sex ? JOB_BABY_MINSTREL : JOB_BABY_WANDERER); + else if( class_[i] == JOB_KAGEROU || class_[i] == JOB_OBORO ) + class_[i] = (sex ? JOB_KAGEROU : JOB_OBORO); } - num = i; - for( i = 0; i < num; ++i ) - { - if( class_[i] == JOB_BARD || class_[i] == JOB_DANCER || - class_[i] == JOB_CLOWN || class_[i] == JOB_GYPSY || - class_[i] == JOB_BABY_BARD || class_[i] == JOB_BABY_DANCER || - class_[i] == JOB_MINSTREL || class_[i] == JOB_WANDERER || - class_[i] == JOB_MINSTREL_T || class_[i] == JOB_WANDERER_T || - class_[i] == JOB_BABY_MINSTREL || class_[i] == JOB_BABY_WANDERER || - class_[i] == JOB_KAGEROU || class_[i] == JOB_OBORO ) - { - // job modification - if( class_[i] == JOB_BARD || class_[i] == JOB_DANCER ) - class_[i] = (sex ? JOB_BARD : JOB_DANCER); - else if( class_[i] == JOB_CLOWN || class_[i] == JOB_GYPSY ) - class_[i] = (sex ? JOB_CLOWN : JOB_GYPSY); - else if( class_[i] == JOB_BABY_BARD || class_[i] == JOB_BABY_DANCER ) - class_[i] = (sex ? JOB_BABY_BARD : JOB_BABY_DANCER); - else if( class_[i] == JOB_MINSTREL || class_[i] == JOB_WANDERER ) - class_[i] = (sex ? JOB_MINSTREL : JOB_WANDERER); - else if( class_[i] == JOB_MINSTREL_T || class_[i] == JOB_WANDERER_T ) - class_[i] = (sex ? JOB_MINSTREL_T : JOB_WANDERER_T); - else if( class_[i] == JOB_BABY_MINSTREL || class_[i] == JOB_BABY_WANDERER ) - class_[i] = (sex ? JOB_BABY_MINSTREL : JOB_BABY_WANDERER); - else if( class_[i] == JOB_KAGEROU || class_[i] == JOB_OBORO ) - class_[i] = (sex ? JOB_KAGEROU : JOB_OBORO); - } - if( SQL_ERROR == SQL->Query(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_[i], char_id[i]) ) - Sql_ShowDebug(sql_handle); + if( SQL_ERROR == SQL->Query(sql_handle, "UPDATE `%s` SET `equip`='0' WHERE `char_id`='%d'", inventory_db, char_id[i]) ) + Sql_ShowDebug(sql_handle); - if( guild_id[i] )// If there is a guild, update the guild_member data [Skotlex] - inter_guild_sex_changed(guild_id[i], acc, char_id[i], sex); - } - SQL->FreeResult(sql_handle); + if( SQL_ERROR == SQL->Query(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_[i], char_id[i]) ) + Sql_ShowDebug(sql_handle); - // disconnect player if online on char-server - disconnect_player(acc); + if( guild_id[i] )// If there is a guild, update the guild_member data [Skotlex] + inter_guild_sex_changed(guild_id[i], acc, char_id[i], sex); } + SQL->FreeResult(sql_handle); + + // disconnect player if online on char-server + disconnect_player(acc); // notify all mapservers about this change WBUFW(buf,0) = 0x2b0d; @@ -2340,17 +2509,12 @@ int parse_fromlogin(int fd) { break; // reply to an account_reg2 registry request - case 0x2729: + case 0x3804: if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) return 0; - - { //Receive account_reg2 registry, forward to map servers. - unsigned char buf[13+ACCOUNT_REG2_NUM*sizeof(struct global_reg)]; - memcpy(buf,RFIFOP(fd,0), RFIFOW(fd,2)); - WBUFW(buf,0) = 0x3804; //Map server can now receive all kinds of reg values with the same packet. [Skotlex] - mapif_sendall(buf, WBUFW(buf,2)); + //Receive account_reg2 registry, forward to map servers. + mapif_sendall(RFIFOP(fd, 0), RFIFOW(fd,2)); RFIFOSKIP(fd, RFIFOW(fd,2)); - } break; // State change of account/ban notification (from login-server) @@ -2362,7 +2526,7 @@ int parse_fromlogin(int fd) { unsigned char buf[11]; WBUFW(buf,0) = 0x2b14; WBUFL(buf,2) = RFIFOL(fd,2); - WBUFB(buf,6) = RFIFOB(fd,6); // 0: change of statut, 1: ban + WBUFB(buf,6) = RFIFOB(fd,6); // 0: change of status, 1: ban WBUFL(buf,7) = RFIFOL(fd,7); // status or final date of a banishment mapif_sendall(buf, 11); } @@ -2386,14 +2550,14 @@ int parse_fromlogin(int fd) { { //Kick it from the map server it is on. mapif_disconnectplayer(server[character->server].fd, character->account_id, character->char_id, 2); if (character->waiting_disconnect == INVALID_TIMER) - character->waiting_disconnect = iTimer->add_timer(iTimer->gettick()+AUTH_TIMEOUT, chardb_waiting_disconnect, character->account_id, 0); + character->waiting_disconnect = timer->add(timer->gettick()+AUTH_TIMEOUT, chardb_waiting_disconnect, character->account_id, 0); } else {// Manual kick from char server. struct char_session_data *tsd; int i; - ARR_FIND( 0, fd_max, i, session[i] && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == aid ); - if( i < fd_max ) + ARR_FIND( 0, sockt->fd_max, i, session[i] && (tsd = (struct char_session_data*)session[i]->session_data) && tsd->account_id == aid ); + if( i < sockt->fd_max ) { WFIFOHEAD(i,3); WFIFOW(i,0) = 0x81; @@ -2438,6 +2602,26 @@ int parse_fromlogin(int fd) { } break; + case 0x2736: // Failed accinfo lookup to forward to mapserver + if (RFIFOREST(fd) < 18) + return 0; + + mapif_parse_accinfo2(false, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), + NULL, NULL, NULL, NULL, NULL, NULL, NULL, -1, 0, 0); + RFIFOSKIP(fd,18); + break; + + case 0x2737: // Successful accinfo lookup to forward to mapserver + if (RFIFOREST(fd) < 183) + return 0; + + 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)); + RFIFOSKIP(fd,183); + break; + default: ShowError("Unknown packet 0x%04x received from login-server, disconnecting.\n", command); set_eof(fd); @@ -2449,18 +2633,17 @@ int parse_fromlogin(int fd) { return 0; } -int check_connect_login_server(int tid, unsigned int tick, int id, intptr_t data); -int send_accounts_tologin(int tid, unsigned int tick, int id, intptr_t data); +int check_connect_login_server(int tid, int64 tick, int id, intptr_t data); void do_init_loginif(void) { // establish char-login connection if not present - iTimer->add_timer_func_list(check_connect_login_server, "check_connect_login_server"); - iTimer->add_timer_interval(iTimer->gettick() + 1000, check_connect_login_server, 0, 0, 10 * 1000); + timer->add_func_list(check_connect_login_server, "check_connect_login_server"); + timer->add_interval(timer->gettick() + 1000, check_connect_login_server, 0, 0, 10 * 1000); // send a list of all online account IDs to login server - iTimer->add_timer_func_list(send_accounts_tologin, "send_accounts_tologin"); - iTimer->add_timer_interval(iTimer->gettick() + 1000, send_accounts_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour + timer->add_func_list(send_accounts_tologin, "send_accounts_tologin"); + timer->add_interval(timer->gettick() + 1000, send_accounts_tologin, 0, 0, 3600 * 1000); //Sync online accounts every hour } void do_final_loginif(void) @@ -2484,23 +2667,77 @@ int request_accreg2(int account_id, int char_id) } return 0; } - -//Send packet forward to login-server for account saving -int save_accreg2(unsigned char* buf, int len) -{ - if (login_fd > 0) { - WFIFOHEAD(login_fd,len+4); - memcpy(WFIFOP(login_fd,4), buf, len); - WFIFOW(login_fd,0) = 0x2728; - WFIFOW(login_fd,2) = len+4; - WFIFOSET(login_fd,len+4); - return 1; +/** + * Handles global account reg saving that continues with global_accreg_to_login_add and global_accreg_to_send + **/ +void global_accreg_to_login_start (int account_id, int char_id) { + WFIFOHEAD(login_fd, 60000 + 300); + WFIFOW(login_fd,0) = 0x2728; + WFIFOW(login_fd,2) = 14; + WFIFOL(login_fd,4) = account_id; + WFIFOL(login_fd,8) = char_id; + WFIFOW(login_fd,12) = 0;/* count */ +} +/** + * Completes global account reg saving that starts global_accreg_to_login_start and continues with global_accreg_to_login_add + **/ +void global_accreg_to_login_send (void) { + WFIFOSET(login_fd, WFIFOW(login_fd,2)); +} +/** + * Handles global account reg saving that starts global_accreg_to_login_start and ends with global_accreg_to_send + **/ +void global_accreg_to_login_add (const char *key, unsigned int index, intptr_t val, bool is_string) { + int nlen = WFIFOW(login_fd, 2); + size_t len; + + len = strlen(key)+1; + + WFIFOB(login_fd, nlen) = (unsigned char)len;/* won't be higher; the column size is 32 */ + nlen += 1; + + safestrncpy((char*)WFIFOP(login_fd,nlen), key, len); + nlen += len; + + WFIFOL(login_fd, nlen) = index; + nlen += 4; + + if( is_string ) { + WFIFOB(login_fd, nlen) = val ? 2 : 3; + nlen += 1; + + if( val ) { + char *sval = (char*)val; + len = strlen(sval)+1; + + WFIFOB(login_fd, nlen) = (unsigned char)len;/* won't be higher; the column size is 254 */ + nlen += 1; + + safestrncpy((char*)WFIFOP(login_fd,nlen), sval, len); + nlen += len; + } + + } else { + WFIFOB(login_fd, nlen) = val ? 0 : 1; + nlen += 1; + + if( val ) { + WFIFOL(login_fd, nlen) = (int)val; + nlen += 4; + } + + } + + WFIFOW(login_fd,12) += 1; + + WFIFOW(login_fd, 2) = nlen; + if( WFIFOW(login_fd, 2) > 60000 ) { + int account_id = WFIFOL(login_fd,4), char_id = WFIFOL(login_fd,8); + global_accreg_to_login_send(); + global_accreg_to_login_start(account_id,char_id);/* prepare next */ } - return 0; } - -void char_read_fame_list(void) -{ +void char_read_fame_list(void) { int i; char* data; size_t len; @@ -2558,8 +2795,7 @@ void char_read_fame_list(void) } // Send map-servers the fame ranking lists -int char_send_fame_list(int fd) -{ +int char_send_fame_list(int fd) { int i, len = 8; unsigned char buf[32000]; @@ -2594,8 +2830,7 @@ int char_send_fame_list(int fd) return 0; } -void char_update_fame_list(int type, int index, int fame) -{ +void char_update_fame_list(int type, int index, int fame) { unsigned char buf[8]; WBUFW(buf,0) = 0x2b22; WBUFB(buf,2) = type; @@ -2681,6 +2916,16 @@ void mapif_on_disconnect(int id) mapif_server_reset(id); } +void mapif_on_parse_accinfo(int account_id, int u_fd, int u_aid, int u_group, int map_fd) { + WFIFOHEAD(login_fd,22); + WFIFOW(login_fd,0) = 0x2740; + WFIFOL(login_fd,2) = account_id; + WFIFOL(login_fd,6) = u_fd; + WFIFOL(login_fd,10) = u_aid; + WFIFOL(login_fd,14) = u_group; + WFIFOL(login_fd,18) = map_fd; + WFIFOSET(login_fd,22); +} int parse_frommap(int fd) { @@ -2701,12 +2946,20 @@ int parse_frommap(int fd) } while(RFIFOREST(fd) >= 2) { + + if( HPM->packetsc[hpParse_FromMap] ) { + if( (i = HPM->parse_packets(fd,hpParse_FromMap)) ) { + if( i == 1 ) continue; + if( i == 2 ) return 0; + } + } + switch(RFIFOW(fd,0)) { case 0x2b0a: if( RFIFOREST(fd) < RFIFOW(fd, 2) ) return 0; - socket_datasync(fd, false); + sockt->datasync(fd, false); RFIFOSKIP(fd,RFIFOW(fd,2)); break; @@ -2796,14 +3049,14 @@ int parse_frommap(int fd) int aid, cid; aid = RFIFOL(fd,2); cid = RFIFOL(fd,6); - if( SQL_ERROR == SQL->Query(sql_handle, "SELECT type, tick, val1, val2, val3, val4 from `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `type`, `tick`, `val1`, `val2`, `val3`, `val4` " + "FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", scdata_db, aid, cid) ) { Sql_ShowDebug(sql_handle); break; } - if( SQL->NumRows(sql_handle) > 0 ) - { + if( SQL->NumRows(sql_handle) > 0 ) { struct status_change_data scdata; int count; char* data; @@ -2824,8 +3077,7 @@ int parse_frommap(int fd) } if (count >= 50) ShowWarning("Too many status changes for %d:%d, some of them were not loaded.\n", aid, cid); - if (count > 0) - { + if (count > 0) { WFIFOW(fd,2) = 14 + count*sizeof(struct status_change_data); WFIFOW(fd,12) = count; WFIFOSET(fd,WFIFOW(fd,2)); @@ -2834,6 +3086,14 @@ int parse_frommap(int fd) if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", scdata_db, aid, cid) ) Sql_ShowDebug(sql_handle); } + } else { //no sc (needs a response) + WFIFOHEAD(fd,14); + WFIFOW(fd,0) = 0x2b1d; + WFIFOW(fd,2) = 14; + WFIFOL(fd,4) = aid; + WFIFOL(fd,8) = cid; + WFIFOW(fd,12) = 0; + WFIFOSET(fd,WFIFOW(fd,2)); } SQL->FreeResult(sql_handle); #endif @@ -2886,9 +3146,8 @@ int parse_frommap(int fd) int aid = RFIFOL(fd,4), cid = RFIFOL(fd,8), size = RFIFOW(fd,2); struct online_char_data* character; - if (size - 13 != sizeof(struct mmo_charstatus)) - { - ShowError("parse_from_map (save-char): Size mismatch! %d != %d\n", size-13, sizeof(struct mmo_charstatus)); + if (size - 13 != sizeof(struct mmo_charstatus)) { + ShowError("parse_from_map (save-char): Size mismatch! %d != %"PRIuS"\n", size-13, sizeof(struct mmo_charstatus)); RFIFOSKIP(fd,size); break; } @@ -2901,7 +3160,7 @@ int parse_frommap(int fd) memcpy(&char_dat, RFIFOP(fd,13), sizeof(struct mmo_charstatus)); mmo_char_tosql(cid, &char_dat); } else { //This may be valid on char-server reconnection, when re-sending characters that already logged off. - ShowError("parse_from_map (save-char): Received data for non-existant/offline character (%d:%d).\n", aid, cid); + ShowError("parse_from_map (save-char): Received data for non-existing/offline character (%d:%d).\n", aid, cid); set_char_online(id, cid, aid); } @@ -2919,14 +3178,15 @@ int parse_frommap(int fd) break; case 0x2b02: // req char selection - if( RFIFOREST(fd) < 18 ) + if( RFIFOREST(fd) < 22 ) return 0; { int account_id = RFIFOL(fd,2); uint32 login_id1 = RFIFOL(fd,6); uint32 login_id2 = RFIFOL(fd,10); uint32 ip = RFIFOL(fd,14); - RFIFOSKIP(fd,18); + int32 group_id = RFIFOL(fd, 18); + RFIFOSKIP(fd,22); if( runflag != CHARSERVER_ST_RUNNING ) { @@ -2946,8 +3206,10 @@ int parse_frommap(int fd) node->char_id = 0; node->login_id1 = login_id1; node->login_id2 = login_id2; + node->group_id = group_id; //node->sex = 0; node->ip = ntohl(ip); + /* sounds troublesome. */ //node->expiration_time = 0; // unlimited/unknown time by default (not display in map-server) //node->gmlevel = 0; idb_put(auth_db, account_id, node); @@ -3069,7 +3331,7 @@ int parse_frommap(int fd) RFIFOSKIP(fd, 86); break; - case 0x2b0e: // Request from map-server to change an account's status (will just be forwarded to login server) + case 0x2b0e: // Request from map-server to change an account's or character's status (accounts will just be forwarded to login server) if (RFIFOREST(fd) < 44) return 0; { @@ -3078,7 +3340,7 @@ int parse_frommap(int fd) 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 - int type = RFIFOW(fd,30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban + int type = RFIFOW(fd,30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5 changesex, 6 charban, 7 charunban short year = RFIFOW(fd,32); short month = RFIFOW(fd,34); short day = RFIFOW(fd,36); @@ -3088,25 +3350,22 @@ int parse_frommap(int fd) RFIFOSKIP(fd,44); SQL->EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); - if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `account_id`,`name` FROM `%s` WHERE `name` = '%s'", char_db, esc_name) ) + + if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `account_id`,`char_id`,`unban_time` FROM `%s` WHERE `name` = '%s'", char_db, esc_name) ) Sql_ShowDebug(sql_handle); - else - if( SQL->NumRows(sql_handle) == 0 ) - { + else if( SQL->NumRows(sql_handle) == 0 ) { result = 1; // 1-player not found - } - else - if( SQL_SUCCESS != SQL->NextRow(sql_handle) ) + } else if( SQL_SUCCESS != SQL->NextRow(sql_handle) ) { Sql_ShowDebug(sql_handle); - //FIXME: set proper result value? - else - { - char name[NAME_LENGTH]; - int account_id; + result = 1; // 1-player not found + } else { + int account_id, char_id; char* data; + time_t unban_time; SQL->GetData(sql_handle, 0, &data, NULL); account_id = atoi(data); - SQL->GetData(sql_handle, 1, &data, NULL); safestrncpy(name, data, sizeof(name)); + SQL->GetData(sql_handle, 1, &data, NULL); char_id = atoi(data); + SQL->GetData(sql_handle, 2, &data, NULL); unban_time = atol(data); if( login_fd <= 0 ) result = 3; // 3-login-server offline @@ -3114,53 +3373,111 @@ int parse_frommap(int fd) // else // if( acc != -1 && isGM(acc) < isGM(account_id) ) // result = 2; // 2-gm level too low - else - switch( type ) { - case 1: // block - WFIFOHEAD(login_fd,10); - WFIFOW(login_fd,0) = 0x2724; - WFIFOL(login_fd,2) = account_id; - WFIFOL(login_fd,6) = 5; // new account status - WFIFOSET(login_fd,10); - break; - case 2: // ban - WFIFOHEAD(login_fd,18); - WFIFOW(login_fd, 0) = 0x2725; - WFIFOL(login_fd, 2) = account_id; - WFIFOW(login_fd, 6) = year; - WFIFOW(login_fd, 8) = month; - WFIFOW(login_fd,10) = day; - WFIFOW(login_fd,12) = hour; - WFIFOW(login_fd,14) = minute; - WFIFOW(login_fd,16) = second; - WFIFOSET(login_fd,18); - break; - case 3: // unblock - WFIFOHEAD(login_fd,10); - WFIFOW(login_fd,0) = 0x2724; - WFIFOL(login_fd,2) = account_id; - WFIFOL(login_fd,6) = 0; // new account status - WFIFOSET(login_fd,10); - break; - case 4: // unban - WFIFOHEAD(login_fd,6); - WFIFOW(login_fd,0) = 0x272a; - WFIFOL(login_fd,2) = account_id; - WFIFOSET(login_fd,6); - break; - case 5: // changesex - WFIFOHEAD(login_fd,6); - WFIFOW(login_fd,0) = 0x2727; - WFIFOL(login_fd,2) = account_id; - WFIFOSET(login_fd,6); - break; + else { + switch( type ) { + case 1: // block + WFIFOHEAD(login_fd,10); + WFIFOW(login_fd,0) = 0x2724; + WFIFOL(login_fd,2) = account_id; + WFIFOL(login_fd,6) = 5; // new account status + WFIFOSET(login_fd,10); + break; + case 2: // ban + WFIFOHEAD(login_fd,18); + WFIFOW(login_fd, 0) = 0x2725; + WFIFOL(login_fd, 2) = account_id; + WFIFOW(login_fd, 6) = year; + WFIFOW(login_fd, 8) = month; + WFIFOW(login_fd,10) = day; + WFIFOW(login_fd,12) = hour; + WFIFOW(login_fd,14) = minute; + WFIFOW(login_fd,16) = second; + WFIFOSET(login_fd,18); + break; + case 3: // unblock + WFIFOHEAD(login_fd,10); + WFIFOW(login_fd,0) = 0x2724; + WFIFOL(login_fd,2) = account_id; + WFIFOL(login_fd,6) = 0; // new account status + WFIFOSET(login_fd,10); + break; + case 4: // unban + WFIFOHEAD(login_fd,6); + WFIFOW(login_fd,0) = 0x272a; + WFIFOL(login_fd,2) = account_id; + WFIFOSET(login_fd,6); + break; + case 5: // changesex + WFIFOHEAD(login_fd,6); + WFIFOW(login_fd,0) = 0x2727; + WFIFOL(login_fd,2) = account_id; + WFIFOSET(login_fd,6); + break; + case 6: //char ban + /* handled by char server, so no redirection */ + { + time_t timestamp; + struct tm *tmtime; + SqlStmt* stmt = SQL->StmtMalloc(sql_handle); + + if (unban_time == 0 || unban_time < time(NULL)) + timestamp = time(NULL); // new ban + else + timestamp = unban_time; // add to existing ban + + tmtime = localtime(×tamp); + tmtime->tm_year = tmtime->tm_year + year; + tmtime->tm_mon = tmtime->tm_mon + month; + tmtime->tm_mday = tmtime->tm_mday + day; + tmtime->tm_hour = tmtime->tm_hour + hour; + tmtime->tm_min = tmtime->tm_min + minute; + tmtime->tm_sec = tmtime->tm_sec + second; + timestamp = mktime(tmtime); + + 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*)×tamp, sizeof(timestamp)) + || SQL_SUCCESS != SQL->StmtBindParam(stmt, 1, SQLDT_INT, (void*)&char_id, sizeof(char_id)) + || SQL_SUCCESS != SQL->StmtExecute(stmt) + + ) { + SqlStmt_ShowDebug(stmt); + } + + SQL->StmtFree(stmt); + + // condition applies; send to all map-servers to disconnect the player + if( timestamp > time(NULL) ) { + unsigned char buf[11]; + + WBUFW(buf,0) = 0x2b14; + WBUFL(buf,2) = char_id; + WBUFB(buf,6) = 2; + WBUFL(buf,7) = (unsigned int)timestamp; + mapif_sendall(buf, 11); + + // disconnect player if online on char-server + disconnect_player(account_id); + } + } + break; + case 7: //char unban + /* handled by char server, so no redirection */ + if( SQL_ERROR == SQL->Query(sql_handle, "UPDATE `%s` SET `unban_time` = '0' WHERE `char_id` = '%d' LIMIT 1", char_db, char_id) ) { + Sql_ShowDebug(sql_handle); + result = 1; + } + break; + + } } } SQL->FreeResult(sql_handle); // send answer if a player ask, not if the server ask - if( acc != -1 && type != 5) { // Don't send answer for changesex + if( acc != -1 && type != 5 ) { // Don't send answer for changesex WFIFOHEAD(fd,34); WFIFOW(fd, 0) = 0x2b0f; WFIFOL(fd, 2) = acc; @@ -3184,12 +3501,11 @@ int parse_frommap(int fd) int player_pos; int fame_pos; - switch(type) - { - case 1: size = fame_list_size_smith; list = smith_fame_list; break; - case 2: size = fame_list_size_chemist; list = chemist_fame_list; break; - case 3: size = fame_list_size_taekwon; list = taekwon_fame_list; break; - default: size = 0; list = NULL; break; + switch(type) { + case RANKTYPE_BLACKSMITH: size = fame_list_size_smith; list = smith_fame_list; break; + case RANKTYPE_ALCHEMIST: size = fame_list_size_chemist; list = chemist_fame_list; break; + case RANKTYPE_TAEKWON: size = fame_list_size_taekwon; list = taekwon_fame_list; break; + default: size = 0; list = NULL; break; } ARR_FIND(0, size, player_pos, list[player_pos].id == cid);// position of the player @@ -3197,22 +3513,20 @@ int parse_frommap(int fd) if( player_pos == size && fame_pos == size ) ;// not on list and not enough fame to get on it - else if( fame_pos == player_pos ) - {// same position + else if( fame_pos == player_pos ) { + // same position list[player_pos].fame = fame; char_update_fame_list(type, player_pos, fame); - } - else - {// move in the list - if( player_pos == size ) - {// new ranker - not in the list + } else { + // move in the list + if( player_pos == size ) { + // new ranker - not in the list ARR_MOVE(size - 1, fame_pos, list, struct fame_list); list[fame_pos].id = cid; list[fame_pos].fame = fame; char_loadName(cid, list[fame_pos].name); - } - else - {// already in the list + } else { + // already in the list if( fame_pos == size ) --fame_pos;// move to the end of the list ARR_MOVE(player_pos, fame_pos, list, struct fame_list); @@ -3287,11 +3601,14 @@ int parse_frommap(int fd) cid = RFIFOL(fd, 8); count = RFIFOW(fd, 12); + /* clear; ensure no left overs e.g. permanent */ + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id`='%d'", scdata_db, aid, cid) ) + Sql_ShowDebug(sql_handle); + if( count > 0 ) { struct status_change_data data; StringBuf buf; - int i; StrBuf->Init(&buf); StrBuf->Printf(&buf, "INSERT INTO `%s` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES ", scdata_db); @@ -3320,14 +3637,14 @@ int parse_frommap(int fd) break; case 0x2b26: // auth request from map-server - if (RFIFOREST(fd) < 19) + if (RFIFOREST(fd) < 20) return 0; { int account_id; int char_id; int login_id1; - char sex; + char sex, standalone; uint32 ip; struct auth_node* node; struct mmo_charstatus* cd; @@ -3338,15 +3655,36 @@ int parse_frommap(int fd) login_id1 = RFIFOL(fd,10); sex = RFIFOB(fd,14); ip = ntohl(RFIFOL(fd,15)); - RFIFOSKIP(fd,19); + standalone = RFIFOB(fd, 19); + RFIFOSKIP(fd,20); node = (struct auth_node*)idb_get(auth_db, account_id); cd = (struct mmo_charstatus*)uidb_get(char_db_,char_id); - if( cd == NULL ) - { //Really shouldn't happen. + + if( cd == NULL ) { //Really shouldn't happen. mmo_char_fromsql(char_id, &char_dat, true); cd = (struct mmo_charstatus*)uidb_get(char_db_,char_id); } + + if( runflag == CHARSERVER_ST_RUNNING && cd && standalone ) { + cd->sex = sex; + + WFIFOHEAD(fd,25 + sizeof(struct mmo_charstatus)); + WFIFOW(fd,0) = 0x2afd; + WFIFOW(fd,2) = 25 + sizeof(struct mmo_charstatus); + WFIFOL(fd,4) = account_id; + WFIFOL(fd,8) = 0; + WFIFOL(fd,12) = 0; + WFIFOL(fd,16) = 0; + WFIFOL(fd,20) = 0; + WFIFOB(fd,24) = 0; + memcpy(WFIFOP(fd,25), cd, sizeof(struct mmo_charstatus)); + WFIFOSET(fd, WFIFOW(fd,2)); + + set_char_online(id, char_id, account_id); + break; + } + if( runflag == CHARSERVER_ST_RUNNING && cd != NULL && node != NULL && @@ -3429,6 +3767,40 @@ int parse_frommap(int fd) RFIFOFLUSH(fd); } break; + + /* individual sc data insertion/update */ + case 0x2740: + if( RFIFOREST(fd) < 28 ) + return 0; + else { + int account_id = RFIFOL(fd, 2), char_id = RFIFOL(fd, 6), + val1 = RFIFOL(fd, 12), val2 = RFIFOL(fd, 16), + val3 = RFIFOL(fd, 20), val4 = RFIFOL(fd, 24); + short type = RFIFOW(fd, 10); + + if( SQL_ERROR == SQL->Query(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) ) + Sql_ShowDebug(sql_handle); + + RFIFOSKIP(fd, 28); + } + break; + + /* individual sc data delete */ + case 0x2741: + if( RFIFOREST(fd) < 12 ) + return 0; + else { + int account_id = RFIFOL(fd, 2), char_id = RFIFOL(fd, 6); + short type = RFIFOW(fd, 10); + + if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `char_id` = '%d' AND `type` = '%d' LIMIT 1", + scdata_db, account_id, char_id, type) ) + Sql_ShowDebug(sql_handle); + + RFIFOSKIP(fd, 12); + } + break; default: @@ -3507,6 +3879,7 @@ int lan_subnetcheck(uint32 ip) } +/// Answers to deletion request (HC_DELETE_CHAR3_RESERVED) /// @param result /// 0 (0x718): An unknown error has occurred. /// 1: none/success @@ -3520,7 +3893,12 @@ void char_delete2_ack(int fd, int char_id, uint32 result, time_t delete_date) WFIFOW(fd,0) = 0x828; WFIFOL(fd,2) = char_id; WFIFOL(fd,6) = result; - WFIFOL(fd,10) = TOL(delete_date); +#if PACKETVER >= 20130000 + WFIFOL(fd,10) = (int)(delete_date - time(NULL)); +#else + WFIFOL(fd,10) = (int)delete_date; + +#endif WFIFOSET(fd,14); } @@ -3535,6 +3913,14 @@ void char_delete2_ack(int fd, int char_id, uint32 result, time_t delete_date) /// Any (0x718): An unknown error has occurred. void char_delete2_accept_ack(int fd, int char_id, uint32 result) {// HC: <082a>.W <char id>.L <Msg:0-5>.L + +#if PACKETVER >= 20130000 /* not sure the exact date -- must refresh or client gets stuck */ + if( result == 1 ) { + struct char_session_data* sd = (struct char_session_data*)session[fd]->session_data; + mmo_char_send099d(fd, sd); + } +#endif + WFIFOHEAD(fd,10); WFIFOW(fd,0) = 0x82a; WFIFOL(fd,2) = char_id; @@ -3559,7 +3945,7 @@ void char_delete2_cancel_ack(int fd, int char_id, uint32 result) static void char_delete2_req(int fd, struct char_session_data* sd) {// CH: <0827>.W <char id>.L - int char_id, i; + int char_id, party_id, guild_id, i; char* data; time_t delete_date; @@ -3586,22 +3972,34 @@ static void char_delete2_req(int fd, struct char_session_data* sd) return; } -/* - // Aegis imposes these checks probably to avoid dead member - // entries in guilds/parties, otherwise they are not required. - // TODO: Figure out how these are enforced during waiting. - if( guild_id ) - {// character in guild - char_delete2_ack(fd, char_id, 4, 0); - return; - } + // This check is imposed by Aegis to avoid dead entries in databases + // _it is not needed_ as we clear data properly + // see issue: 7338 + if( char_aegis_delete ) + { + if( SQL_SUCCESS != SQL->Query(sql_handle, "SELECT `party_id`, `guild_id` FROM `%s` WHERE `char_id`='%d'", char_db, char_id) + || SQL_SUCCESS != SQL->NextRow(sql_handle) + ) + { + Sql_ShowDebug(sql_handle); + char_delete2_ack(fd, char_id, 3, 0); + return; + } + SQL->GetData(sql_handle, 0, &data, NULL); party_id = atoi(data); + SQL->GetData(sql_handle, 1, &data, NULL); guild_id = atoi(data); - if( party_id ) - {// character in party - char_delete2_ack(fd, char_id, 5, 0); - return; + if( guild_id ) + { + char_delete2_ack(fd, char_id, 4, 0); + return; + } + + if( party_id ) + { + char_delete2_ack(fd, char_id, 5, 0); + return; + } } -*/ // success delete_date = time(NULL)+char_del_delay; @@ -3620,7 +4018,7 @@ static void char_delete2_req(int fd, struct char_session_data* sd) 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, k; + int char_id, i; unsigned int base_level; char* data; time_t delete_date; @@ -3683,11 +4081,8 @@ static void char_delete2_accept(int fd, struct char_session_data* sd) } // refresh character list cache - for(k = i; k < MAX_CHARS-1; k++) { - sd->found_char[k] = sd->found_char[k+1]; - } - sd->found_char[MAX_CHARS-1] = -1; - + sd->found_char[i] = -1; + char_delete2_accept_ack(fd, char_id, 1); } @@ -3721,7 +4116,6 @@ static void char_delete2_cancel(int fd, struct char_session_data* sd) int parse_char(int fd) { - int i, ch; char email[40]; unsigned short cmd; int map_fd; @@ -3750,8 +4144,14 @@ int parse_char(int fd) while( RFIFOREST(fd) >= 2 ) { //For use in packets that depend on an sd being present [Skotlex] - #define FIFOSD_CHECK(rest) { if(RFIFOREST(fd) < rest) return 0; if (sd==NULL || !sd->auth) { RFIFOSKIP(fd,rest); return 0; } } + #define FIFOSD_CHECK(rest) do { if(RFIFOREST(fd) < (rest)) return 0; if (sd==NULL || !sd->auth) { RFIFOSKIP(fd,(rest)); return 0; } } while (0) + if( HPM->packetsc[hpParse_Char] ) { + int success = HPM->parse_packets(fd,hpParse_Char); + if( success == 1 ) continue; + else if( success == 2 ) return 0; + } + cmd = RFIFOW(fd,0); switch( cmd ) { @@ -3773,7 +4173,7 @@ int parse_char(int fd) ShowInfo("request connect - account_id:%d/login_id1:%d/login_id2:%d\n", account_id, login_id1, login_id2); if (sd) { - //Received again auth packet for already authentified account?? Discard it. + //Received again auth packet for already authenticated account?? Discard it. //TODO: Perhaps log this as a hack attempt? //TODO: and perhaps send back a reply? break; @@ -3792,8 +4192,7 @@ int parse_char(int fd) WFIFOL(fd,0) = account_id; WFIFOSET(fd,4); - if( runflag != CHARSERVER_ST_RUNNING ) - { + if( runflag != CHARSERVER_ST_RUNNING ) { WFIFOHEAD(fd,3); WFIFOW(fd,0) = 0x6c; WFIFOB(fd,2) = 0;// rejected from server @@ -3801,7 +4200,7 @@ int parse_char(int fd) break; } - // search authentification + // search authentication node = (struct auth_node*)idb_get(auth_db, account_id); if( node != NULL && node->account_id == account_id && @@ -3809,6 +4208,22 @@ int parse_char(int fd) node->login_id2 == login_id2 /*&& node->ip == ipl*/ ) {// authentication found (coming from map server) + /* restrictions apply */ + if( char_server_type == CST_MAINTENANCE && node->group_id < char_maintenance_min_group_id ) { + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x6c; + WFIFOB(fd,2) = 0;// rejected from server + WFIFOSET(fd,3); + break; + } + /* the client will already deny this request, this check is to avoid someone bypassing. */ + if( char_server_type == CST_PAYING && (time_t)node->expiration_time < time(NULL) ) { + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x6c; + WFIFOB(fd,2) = 0;// rejected from server + WFIFOSET(fd,3); + break; + } idb_remove(auth_db, account_id); char_auth_ok(fd, sd); } @@ -3816,7 +4231,7 @@ int parse_char(int fd) {// authentication not found (coming from login server) if (login_fd > 0) { // don't send request if no login-server WFIFOHEAD(login_fd,23); - WFIFOW(login_fd,0) = 0x2712; // ask login-server to authentify an account + WFIFOW(login_fd,0) = 0x2712; // ask login-server to authenticate an account WFIFOL(login_fd,2) = sd->account_id; WFIFOL(login_fd,6) = sd->login_id1; WFIFOL(login_fd,10) = sd->login_id2; @@ -3844,12 +4259,15 @@ int parse_char(int fd) int char_id; uint32 subnet_map_ip; struct auth_node* node; + int server_id = 0; + int i; int slot = RFIFOB(fd,2); RFIFOSKIP(fd,3); + #if PACKETVER >= 20110309 if( *pincode->enabled ){ // hack check - struct online_char_data* character; + struct online_char_data* character; character = (struct online_char_data*)idb_get(online_char_db, sd->account_id); if( character && character->pincode_enable == -1){ WFIFOHEAD(fd,3); @@ -3860,6 +4278,19 @@ int parse_char(int fd) } } #endif + + ARR_FIND( 0, ARRAYLENGTH(server), server_id, server[server_id].fd > 0 && server[server_id].map[0] ); + /* not available, tell it to wait (client wont close; char select will respawn). + * magic response found by Ind thanks to Yommy <3 */ + if( server_id == ARRAYLENGTH(server) ) { + 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) */ + WFIFOSET(fd, 24); + break; + } + if ( SQL_SUCCESS != SQL->Query(sql_handle, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d'", char_db, sd->account_id, slot) || SQL_SUCCESS != SQL->NextRow(sql_handle) || SQL_SUCCESS != SQL->GetData(sql_handle, 0, &data, NULL) ) @@ -3876,7 +4307,16 @@ int parse_char(int fd) char_id = atoi(data); SQL->FreeResult(sql_handle); - /* set char as online prior to loading its data so 3rd party applications will realise the sql data is not reliable */ + /* client doesn't let it get to this point if you're banned, so its a forged packet */ + if( sd->found_char[slot] == char_id && sd->unban_time[slot] > time(NULL) ) { + WFIFOHEAD(fd,3); + WFIFOW(fd,0) = 0x6c; + WFIFOB(fd,2) = 0; // rejected from server + WFIFOSET(fd,3); + break; + } + + /* set char as online prior to loading its data so 3rd party applications will realize the sql data is not reliable */ set_char_online(-2,char_id,sd->account_id); if( !mmo_char_fromsql(char_id, &char_dat, true) ) { /* failed? set it back offline */ set_char_offline(char_id, sd->account_id); @@ -3894,10 +4334,11 @@ int parse_char(int fd) if (log_char) { 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(sql_handle, esc_name, char_dat.name, strnlen(char_dat.name, NAME_LENGTH)); - if( SQL_ERROR == SQL->Query(sql_handle, "INSERT INTO `%s`(`time`, `account_id`,`char_num`,`name`) VALUES (NOW(), '%d', '%d', '%s')", - charlog_db, sd->account_id, slot, esc_name) ) + if( SQL_ERROR == SQL->Query(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(sql_handle); } ShowInfo("Selected char: (Account %d: %d - %s)\n", sd->account_id, slot, char_dat.name); @@ -3918,22 +4359,22 @@ int parse_char(int fd) WFIFOSET(fd,3); break; } - if ((i = search_mapserver((j=mapindex_name2id(MAP_PRONTERA)),-1,-1)) >= 0) { + if ((i = search_mapserver((j=mapindex->name2id(MAP_PRONTERA)),-1,-1)) >= 0) { cd->last_point.x = 273; cd->last_point.y = 354; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_GEFFEN)),-1,-1)) >= 0) { + } else if ((i = search_mapserver((j=mapindex->name2id(MAP_GEFFEN)),-1,-1)) >= 0) { cd->last_point.x = 120; cd->last_point.y = 100; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_MORROC)),-1,-1)) >= 0) { + } else if ((i = search_mapserver((j=mapindex->name2id(MAP_MORROC)),-1,-1)) >= 0) { cd->last_point.x = 160; cd->last_point.y = 94; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_ALBERTA)),-1,-1)) >= 0) { + } else if ((i = search_mapserver((j=mapindex->name2id(MAP_ALBERTA)),-1,-1)) >= 0) { cd->last_point.x = 116; cd->last_point.y = 57; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_PAYON)),-1,-1)) >= 0) { + } else if ((i = search_mapserver((j=mapindex->name2id(MAP_PAYON)),-1,-1)) >= 0) { cd->last_point.x = 87; cd->last_point.y = 117; - } else if ((i = search_mapserver((j=mapindex_name2id(MAP_IZLUDE)),-1,-1)) >= 0) { + } else if ((i = search_mapserver((j=mapindex->name2id(MAP_IZLUDE)),-1,-1)) >= 0) { cd->last_point.x = 94; cd->last_point.y = 103; } else { @@ -3967,7 +4408,7 @@ int parse_char(int fd) 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)); + mapindex->getmapname_ext(mapindex_id2name(cd->last_point.map), (char*)WFIFOP(fd,6)); subnet_map_ip = lan_subnetcheck(ipl); // Advanced subnet check [LuzZza] WFIFOL(fd,22) = htonl((subnet_map_ip) ? subnet_map_ip : server[i].ip); WFIFOW(fd,26) = ntows(htons(server[i].port)); // [!] LE byte order here [!] @@ -3992,41 +4433,51 @@ int parse_char(int fd) #if PACKETVER >= 20120307 // S 0970 <name>.24B <slot>.B <hair color>.W <hair style>.W case 0x970: + { + int result; FIFOSD_CHECK(31); #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: + { + int result; FIFOSD_CHECK(37); #endif if( !char_new ) //turn character creation on/off [Kevin] - i = -2; + result = -2; else #if PACKETVER >= 20120307 - i = make_new_char_sql(sd, (char*)RFIFOP(fd,2),RFIFOB(fd,26),RFIFOW(fd,27),RFIFOW(fd,29)); + result = make_new_char_sql(sd, (char*)RFIFOP(fd,2),RFIFOB(fd,26),RFIFOW(fd,27),RFIFOW(fd,29)); #else - i = 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)); + result = 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 - //'Charname already exists' (-1), 'Char creation denied' (-2) and 'You are underaged' (-3) - if (i < 0) { + if (result < 0) { WFIFOHEAD(fd,3); WFIFOW(fd,0) = 0x6e; /* Others I found [Ind] */ /* 0x02 = Symbols in Character Names are forbidden */ - /* 0x03 = You are not elegible to open the Character Slot. */ - switch (i) { - case -1: WFIFOB(fd,2) = 0x00; break; - case -2: WFIFOB(fd,2) = 0xFF; break; - case -3: WFIFOB(fd,2) = 0x01; break; - case -4: WFIFOB(fd,2) = 0x03; break; + /* 0x03 = You are not eligible to open the Character Slot. */ + /* 0x0B = This service is only available for premium users. */ + switch (result) { + case -1: WFIFOB(fd,2) = 0x00; break; // 'Charname already exists' + case -2: WFIFOB(fd,2) = 0xFF; break; // 'Char creation denied' + case -3: WFIFOB(fd,2) = 0x01; break; // 'You are underaged' + case -4: WFIFOB(fd,2) = 0x03; break; // 'You are not eligible to open the Character Slot.' + case -5: WFIFOB(fd,2) = 0x02; break; // 'Symbols in Character Names are forbidden' + + default: + ShowWarning("parse_char: Unknown result received from make_new_char_sql!\n"); + WFIFOB(fd,2) = 0xFF; + break; } WFIFOSET(fd,3); } else { int len; // retrieve data struct mmo_charstatus char_dat; - mmo_char_fromsql(i, &char_dat, false); //Only the short data is needed. + mmo_char_fromsql(result, &char_dat, false); //Only the short data is needed. // send to player WFIFOHEAD(fd,2+MAX_CHAR_BUF); @@ -4035,15 +4486,14 @@ int parse_char(int fd) WFIFOSET(fd,len); // add new entry to the chars list - ARR_FIND( 0, MAX_CHARS, ch, sd->found_char[ch] == -1 ); - if( ch < MAX_CHARS ) - sd->found_char[ch] = i; // the char_id of the new char + 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 + } break; // delete char @@ -4054,9 +4504,10 @@ int parse_char(int fd) if (cmd == 0x1fb) FIFOSD_CHECK(56); { int cid = RFIFOL(fd,2); + int i; #if PACKETVER >= 20110309 if( *pincode->enabled ){ // hack check - struct online_char_data* character; + struct online_char_data* character; character = (struct online_char_data*)idb_get(online_char_db, sd->account_id); if( character && character->pincode_enable == -1 ){ WFIFOHEAD(fd,3); @@ -4096,9 +4547,7 @@ int parse_char(int fd) } // remove char from list and compact it - for(ch = i; ch < MAX_CHARS-1; ch++) - sd->found_char[ch] = sd->found_char[ch+1]; - sd->found_char[MAX_CHARS-1] = -1; + sd->found_char[i] = -1; /* Delete character */ if(delete_char_sql(cid)<0){ @@ -4191,7 +4640,7 @@ int parse_char(int fd) //Confirm change name. // 0x28f <char_id>.L case 0x28f: - // 0: Sucessfull + // 0: Successful // 1: This character's name has already been changed. You cannot change a character's name more than once. // 2: User information is not correct. // 3: You have failed to change this character's name. @@ -4264,6 +4713,7 @@ int parse_char(int fd) { char* l_user = (char*)RFIFOP(fd,2); char* l_pass = (char*)RFIFOP(fd,26); + int i; l_user[23] = '\0'; l_pass[23] = '\0'; ARR_FIND( 0, ARRAYLENGTH(server), i, server[i].fd <= 0 ); @@ -4291,11 +4741,11 @@ int parse_char(int fd) realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); char_mapif_init(fd); } - socket_datasync(fd, true); + sockt->datasync(fd, true); RFIFOSKIP(fd,60); } - return 0; // avoid processing of followup packets here + return 0; // avoid processing of follow-up packets here // checks the entered pin case 0x8b8: @@ -4426,8 +4876,7 @@ int mapif_send(int fd, unsigned char *buf, unsigned int len) return 0; } -int broadcast_user_count(int tid, unsigned int tick, int id, intptr_t data) -{ +int broadcast_user_count(int tid, int64 tick, int id, intptr_t data) { uint8 buf[6]; int users = count_users(); @@ -4472,8 +4921,7 @@ static int send_accounts_tologin_sub(DBKey key, DBData *data, va_list ap) return 0; } -int send_accounts_tologin(int tid, unsigned int tick, int id, intptr_t data) -{ +int send_accounts_tologin(int tid, int64 tick, int id, intptr_t data) { if (login_fd > 0 && session[login_fd]) { // send account list to login server @@ -4490,8 +4938,7 @@ int send_accounts_tologin(int tid, unsigned int tick, int id, intptr_t data) return 0; } -int check_connect_login_server(int tid, unsigned int tick, int id, intptr_t data) -{ +int check_connect_login_server(int tid, int64 tick, int id, intptr_t data) { if (login_fd > 0 && session[login_fd] != NULL) return 0; @@ -4515,7 +4962,7 @@ int check_connect_login_server(int tid, unsigned int tick, int id, intptr_t data WFIFOW(login_fd,58) = htons(char_port); memcpy(WFIFOP(login_fd,60), server_name, 20); WFIFOW(login_fd,80) = 0; - WFIFOW(login_fd,82) = char_maintenance; + WFIFOW(login_fd,82) = char_server_type; WFIFOW(login_fd,84) = char_new_display; //only display (New) if they want to [Kevin] WFIFOSET(login_fd,86); @@ -4526,8 +4973,7 @@ int check_connect_login_server(int tid, unsigned int tick, int id, intptr_t data //Invoked 15 seconds after mapif_disconnectplayer in case the map server doesn't //replies/disconnect the player we tried to kick. [Skotlex] //------------------------------------------------ -static int chardb_waiting_disconnect(int tid, unsigned int tick, int id, intptr_t data) -{ +static int chardb_waiting_disconnect(int tid, int64 tick, int id, intptr_t data) { struct online_char_data* character; if ((character = (struct online_char_data*)idb_get(online_char_db, id)) != NULL && character->waiting_disconnect == tid) { //Mark it offline due to timeout. @@ -4553,15 +4999,14 @@ static int online_data_cleanup_sub(DBKey key, DBData *data, va_list ap) return 0; } -static int online_data_cleanup(int tid, unsigned int tick, int id, intptr_t data) -{ +static int online_data_cleanup(int tid, int64 tick, int id, intptr_t data) { online_char_db->foreach(online_char_db, online_data_cleanup_sub); return 0; } //---------------------------------- -// Reading Lan Support configuration -// Rewrote: Anvanced subnet check [LuzZza] +// Reading LAN Support configuration +// Rewrote: Advanced subnet check [LuzZza] //---------------------------------- int char_lan_config_read(const char *lancfgName) { @@ -4579,7 +5024,7 @@ int char_lan_config_read(const char *lancfgName) if ((line[0] == '/' && line[1] == '/') || line[0] == '\n' || line[1] == '\n') continue; - if(sscanf(line,"%[^:]: %[^:]:%[^:]:%[^\r\n]", w1, w2, w3, w4) != 4) { + if (sscanf(line,"%63[^:]: %63[^:]:%63[^:]:%63[^\r\n]", w1, w2, w3, w4) != 4) { ShowWarning("Error syntax of configuration file %s in line %d.\n", lancfgName, line_num); continue; @@ -4628,7 +5073,7 @@ void sql_config_read(const char* cfgName) if(line[0] == '/' && line[1] == '/') continue; - if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2) + if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2) continue; if(!strcmpi(w1,"char_db")) @@ -4643,8 +5088,6 @@ void sql_config_read(const char* cfgName) safestrncpy(charlog_db, w2, sizeof(charlog_db)); else if(!strcmpi(w1,"storage_db")) safestrncpy(storage_db, w2, sizeof(storage_db)); - else if(!strcmpi(w1,"reg_db")) - safestrncpy(reg_db, w2, sizeof(reg_db)); else if(!strcmpi(w1,"skill_db")) safestrncpy(skill_db, w2, sizeof(skill_db)); else if(!strcmpi(w1,"interlog_db")) @@ -4691,8 +5134,21 @@ void sql_config_read(const char* cfgName) 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,"interreg_db")) safestrncpy(interreg_db,w2,sizeof(interreg_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")) sql_config_read(w2); @@ -4725,7 +5181,7 @@ int char_config_read(const char* cfgName) if (line[0] == '/' && line[1] == '/') continue; - if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2) + if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2) continue; remove_control_chars(w1); @@ -4773,8 +5229,8 @@ int char_config_read(const char* cfgName) } } else if (strcmpi(w1, "char_port") == 0) { char_port = atoi(w2); - } else if (strcmpi(w1, "char_maintenance") == 0) { - char_maintenance = atoi(w2); + } else if (strcmpi(w1, "char_server_type") == 0) { + char_server_type = atoi(w2); } else if (strcmpi(w1, "char_new") == 0) { char_new = (bool)atoi(w2); } else if (strcmpi(w1, "char_new_display") == 0) { @@ -4796,7 +5252,7 @@ int char_config_read(const char* cfgName) int x, y; if (sscanf(w2, "%15[^,],%d,%d", map, &x, &y) < 3) continue; - start_point.map = mapindex_name2id(map); + start_point.map = mapindex->name2id(map); if (!start_point.map) ShowError("Specified start_point %s not found in map-index cache.\n", map); start_point.x = x; @@ -4807,18 +5263,25 @@ int char_config_read(const char* cfgName) i = 0; split = strtok(w2, ","); - while (split != NULL && i < MAX_START_ITEMS*2) { + while (split != NULL && i < MAX_START_ITEMS*3) { split2 = split; split = strtok(NULL, ","); start_items[i] = atoi(split2); + if (start_items[i] < 0) start_items[i] = 0; + ++i; } - if (i%2) { //we know it must be a even number - ShowError("Specified 'start_items' is missing a parameter. Removing '%d'.\n", start_items[i - 1]); - start_items[i - 1] = 0; + // Format is: id1,quantity1,stackable1,idN,quantityN,stackableN + if( i%3 ) + { + ShowWarning("char_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); @@ -4839,6 +5302,8 @@ int char_config_read(const char* cfgName) 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) { @@ -4861,6 +5326,8 @@ int char_config_read(const char* cfgName) } } 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) { char_config_read(w2); } else @@ -4872,10 +5339,13 @@ int char_config_read(const char* cfgName) return 0; } -void do_final(void) { +int do_final(void) { int i; + ShowStatus("Terminating...\n"); + HPM->event(HPET_FINAL); + set_all_offline(-1); set_all_offline_sql(); @@ -4899,13 +5369,14 @@ void do_final(void) { } SQL->Free(sql_handle); - mapindex_final(); + mapindex->final(); for(i = 0; i < MAX_MAP_SERVERS; i++ ) if( server[i].map ) aFree(server[i].map); ShowStatus("Finished.\n"); + return EXIT_SUCCESS; } //------------------------------ @@ -4946,13 +5417,13 @@ int do_init(int argc, char **argv) { for(i = 0; i < MAX_MAP_SERVERS; i++ ) server[i].map = NULL; - //Read map indexes - mapindex_init(); - start_point.map = mapindex_name2id("new_zone01"); - - + mapindex_defaults(); pincode_defaults(); + //Read map indexes + mapindex->init(); + start_point.map = mapindex->name2id("new_zone01"); + char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]); char_lan_config_read((argc > 3) ? argv[3] : LAN_CONF_NAME); sql_config_read(SQL_CONF_NAME); @@ -4967,14 +5438,19 @@ int do_init(int argc, char **argv) { auth_db = idb_alloc(DB_OPT_RELEASE_DATA); online_char_db = idb_alloc(DB_OPT_RELEASE_DATA); + + HPM->share(sql_handle,"sql_handle"); + HPM->config_read(NULL, 0); + HPM->event(HPET_INIT); + mmo_char_sql_init(); char_read_fame_list(); //Read fame lists. - if ((naddr_ != 0) && (!login_ip || !char_ip)) { + if ((sockt->naddr_ != 0) && (!login_ip || !char_ip)) { char ip_str[16]; - ip2str(addr_[0], ip_str); + ip2str(sockt->addr_[0], ip_str); - if (naddr_ > 1) + if (sockt->naddr_ > 1) ShowStatus("Multiple interfaces detected.. using %s as our IP address\n", ip_str); else ShowStatus("Defaulting to %s as our IP address\n", ip_str); @@ -4992,17 +5468,17 @@ int do_init(int argc, char **argv) { do_init_mapif(); // periodically update the overall user count on all mapservers + login server - iTimer->add_timer_func_list(broadcast_user_count, "broadcast_user_count"); - iTimer->add_timer_interval(iTimer->gettick() + 1000, broadcast_user_count, 0, 0, 5 * 1000); + timer->add_func_list(broadcast_user_count, "broadcast_user_count"); + timer->add_interval(timer->gettick() + 1000, broadcast_user_count, 0, 0, 5 * 1000); // Timer to clear (online_char_db) - iTimer->add_timer_func_list(chardb_waiting_disconnect, "chardb_waiting_disconnect"); + timer->add_func_list(chardb_waiting_disconnect, "chardb_waiting_disconnect"); // Online Data timers (checking if char still connected) - iTimer->add_timer_func_list(online_data_cleanup, "online_data_cleanup"); - iTimer->add_timer_interval(iTimer->gettick() + 1000, online_data_cleanup, 0, 0, 600 * 1000); + timer->add_func_list(online_data_cleanup, "online_data_cleanup"); + timer->add_interval(timer->gettick() + 1000, online_data_cleanup, 0, 0, 600 * 1000); - //Cleaning the tables for NULL entrys @ startup [Sirius] + //Cleaning the tables for NULL entries @ startup [Sirius] //Chardb clean if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '0'", char_db) ) Sql_ShowDebug(sql_handle); @@ -5023,7 +5499,9 @@ int do_init(int argc, char **argv) { } Sql_HerculesUpdateCheck(sql_handle); - +#ifdef CONSOLE_INPUT + console->input->setSQL(sql_handle); +#endif ShowStatus("The char-server is "CL_GREEN"ready"CL_RESET" (Server is listening on the port %d).\n\n", char_port); if( runflag != CORE_ST_STOP ) @@ -5032,5 +5510,7 @@ int do_init(int argc, char **argv) { runflag = CHARSERVER_ST_RUNNING; } + HPM->event(HPET_READY); + return 0; } |