/*========================================== * Auth confirmation ack *------------------------------------------*/ void chrif_authok(int fd) { int account_id; uint32 login_id1; uint32 login_id2; time_t expiration_time; int gmlevel; struct mmo_charstatus* status; int char_id; struct auth_node *node; TBL_PC* sd; //Check if both servers agree on the struct's size if( RFIFOW(fd,2) - 24 != sizeof(struct mmo_charstatus) ) { ShowError("chrif_authok: Data size mismatch! %d != %d\n", RFIFOW(fd,2) - 24, sizeof(struct mmo_charstatus)); return; } account_id = RFIFOL(fd,4); login_id1 = RFIFOL(fd,8); login_id2 = RFIFOL(fd,12); expiration_time = (time_t)(int32)RFIFOL(fd,16); gmlevel = RFIFOL(fd,20); status = (struct mmo_charstatus*)RFIFOP(fd,24); char_id = status->char_id; //Check if we don't already have player data in our server //Causes problems if the currently connected player tries to quit or this data belongs to an already connected player which is trying to re-auth. if ((sd = map_id2sd(account_id)) != NULL) return; if ((node = chrif_search(account_id)) == NULL) return; // should not happen if (node->state != ST_LOGIN) return; //character in logout phase, do not touch that data. if (node->sd == NULL) { /* //When we receive double login info and the client has not connected yet, //discard the older one and keep the new one. chrif_auth_delete(node->account_id, node->char_id, ST_LOGIN); */ return; // should not happen } sd = node->sd; if( runflag == SERVER_STATE_RUN && node->char_dat == NULL && node->account_id == account_id && node->char_id == char_id && node->login_id1 == login_id1 ) { //Auth Ok if (pc_authok(sd, login_id2, expiration_time, gmlevel, status)) return; } else { //Auth Failed pc_authfail(sd); } chrif_char_offline(sd); //Set him offline, the char server likely has it set as online already. chrif_auth_delete(account_id, char_id, ST_LOGIN); }
void mapif_parse_ItemBoundRetrieve(int fd) { mapif_parse_ItemBoundRetrieve_sub(fd); /* tell map server the operation is over and it can unlock the storage */ mapif_itembound_ack(fd,RFIFOL(fd,6),RFIFOW(fd,10)); }
/*========================================== * *------------------------------------------*/ int chrif_parse(int fd) { int packet_len, cmd; // only process data from the char-server if (fd != char_fd) { ShowDebug("chrif_parse: Disconnecting invalid session #%d (is not the char-server)\n", fd); do_close(fd); return 0; } if (session[fd]->flag.eof) { do_close(fd); char_fd = -1; chrif_on_disconnect(); return 0; } while (RFIFOREST(fd) >= 2) { cmd = RFIFOW(fd,0); if (cmd < 0x2af8 || cmd >= 0x2af8 + ARRAYLENGTH(packet_len_table) || packet_len_table[cmd-0x2af8] == 0) { int r = intif_parse(fd); // intifに渡す if (r == 1) continue; // intifで処理した if (r == 2) return 0; // intifで処理したが、データが足りない ShowWarning("chrif_parse: session #%d, intif_parse failed (unrecognized command 0x%.4x).\n", fd, cmd); set_eof(fd); return 0; } packet_len = packet_len_table[cmd-0x2af8]; if (packet_len == -1) { // dynamic-length packet, second WORD holds the length if (RFIFOREST(fd) < 4) return 0; packet_len = RFIFOW(fd,2); } if ((int)RFIFOREST(fd) < packet_len) return 0; //ShowDebug("Received packet 0x%4x (%d bytes) from char-server (connection %d)\n", RFIFOW(fd,0), packet_len, fd); switch(cmd) { case 0x2af9: chrif_connectack(fd); break; case 0x2afb: chrif_sendmapack(fd); break; case 0x2afd: chrif_authok(fd); break; case 0x2b00: map_setusers(RFIFOL(fd,2)); chrif_keepalive(fd); break; case 0x2b03: clif_charselectok(RFIFOL(fd,2), RFIFOB(fd,6)); break; case 0x2b04: chrif_recvmap(fd); break; case 0x2b06: chrif_changemapserverack(RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), RFIFOW(fd,18), RFIFOW(fd,20), RFIFOW(fd,22), RFIFOL(fd,24), RFIFOW(fd,28)); break; case 0x2b09: map_addnickdb(RFIFOL(fd,2), (char*)RFIFOP(fd,6)); break; case 0x2b0d: chrif_changedsex(fd); break; case 0x2b0f: chrif_char_ask_name_answer(RFIFOL(fd,2), (char*)RFIFOP(fd,6), RFIFOW(fd,30), RFIFOW(fd,32)); break; case 0x2b12: chrif_divorceack(RFIFOL(fd,2), RFIFOL(fd,6)); break; case 0x2b14: chrif_accountban(fd); break; case 0x2b1b: chrif_recvfamelist(fd); break; case 0x2b1d: chrif_load_scdata(fd); break; case 0x2b1e: chrif_update_ip(fd); break; case 0x2b1f: chrif_disconnectplayer(fd); break; case 0x2b20: chrif_removemap(fd); break; case 0x2b21: chrif_save_ack(fd); break; case 0x2b22: chrif_updatefamelist_ack(fd); break; case 0x2b24: chrif_keepalive_ack(fd); break; case 0x2b25: chrif_deadopt(RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break; case 0x2b27: chrif_authfail(fd); break; default: ShowError("chrif_parse : unknown packet (session #%d): 0x%x. Disconnecting.\n", fd, cmd); set_eof(fd); return 0; } if (fd == char_fd) //There's the slight chance we lost the connection during parse, in which case this would segfault if not checked [Skotlex] RFIFOSKIP(fd, packet_len); } return 0; }
int mapif_parse_CreatePet(int fd){ RFIFOHEAD(fd); mapif_create_pet(fd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOW(fd, 10), RFIFOW(fd, 12), RFIFOW(fd, 14), RFIFOW(fd, 16), RFIFOW(fd, 18), RFIFOW(fd, 20), RFIFOB(fd, 22), RFIFOB(fd, 23), (char*)RFIFOP(fd, 24)); return 0; }
//------------------------------------------------ //Guild bound items pull for offline characters [Akinari] //Revised by [Mhalicot] //------------------------------------------------ int mapif_parse_ItemBoundRetrieve_sub(int fd) { #ifdef GP_BOUND_ITEMS StringBuf buf; SqlStmt* stmt; struct item item; int j, i=0, s; struct item items[MAX_INVENTORY]; int char_id = RFIFOL(fd,2); int aid = RFIFOL(fd,6); int guild_id = RFIFOW(fd,10); StrBuf->Init(&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`='%d' AND `bound` = '%d'",inventory_db,char_id,IBT_GUILD); stmt = SqlStmt_Malloc(sql_handle); if(SQL_ERROR == SqlStmt_PrepareStr(stmt, StrBuf->Value(&buf)) || SQL_ERROR == SqlStmt_Execute(stmt)) { Sql_ShowDebug(sql_handle); SqlStmt_Free(stmt); StrBuf->Destroy(&buf); return 1; } SqlStmt_BindColumn(stmt, 0, SQLDT_INT, &item.id, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 1, SQLDT_SHORT, &item.nameid, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 2, SQLDT_SHORT, &item.amount, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 3, SQLDT_USHORT, &item.equip, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 4, SQLDT_CHAR, &item.identify, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 8, SQLDT_UCHAR, &item.bound, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 9, SQLDT_UINT64, &item.unique_id, 0, NULL, NULL); for( j = 0; j < MAX_SLOTS; ++j ) SqlStmt_BindColumn(stmt, 10 + j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); while (SQL_SUCCESS == SqlStmt_NextRow(stmt)) { memcpy(&items[i],&item,sizeof(struct item)); i++; } Sql_FreeResult(sql_handle); if(!i) { //No items found - No need to continue StrBuf->Destroy(&buf); SqlStmt_Free(stmt); return 0; } //First we delete the character's items StrBuf->Clear(&buf); StrBuf->Printf(&buf, "DELETE FROM `%s` WHERE",inventory_db); for(j=0; j<i; j++) { if(j) StrBuf->AppendStr(&buf, " OR"); StrBuf->Printf(&buf, " `id`=%d",items[j].id); } if (SQL_ERROR == SqlStmt_PrepareStr(stmt, StrBuf->Value(&buf)) || SQL_ERROR == SqlStmt_Execute(stmt)) { Sql_ShowDebug(sql_handle); SqlStmt_Free(stmt); StrBuf->Destroy(&buf); return 1; } //Now let's update the guild storage with those deleted items StrBuf->Clear(&buf); StrBuf->Printf(&buf, "INSERT INTO `%s` (`guild_id`, `nameid`, `amount`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`", guild_storage_db); for( j = 0; j < MAX_SLOTS; ++j ) StrBuf->Printf(&buf, ", `card%d`", j); StrBuf->AppendStr(&buf, ") VALUES "); for(j = 0; j < i; ++j) { if(j) StrBuf->AppendStr(&buf, ","); StrBuf->Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d'", guild_id, items[j].nameid, items[j].amount, items[j].identify, items[j].refine, items[j].attribute, items[j].expire_time, items[j].bound, items[j].unique_id); for(s = 0; s < MAX_SLOTS; ++s) StrBuf->Printf(&buf, ", '%d'", items[j].card[s]); StrBuf->AppendStr(&buf, ")"); } if (SQL_ERROR == SqlStmt_PrepareStr(stmt, StrBuf->Value(&buf)) || SQL_ERROR == SqlStmt_Execute(stmt)) { Sql_ShowDebug(sql_handle); SqlStmt_Free(stmt); StrBuf->Destroy(&buf); return 1; } StrBuf->Destroy(&buf); SqlStmt_Free(stmt); //Finally reload storage and tell map we're done mapif_load_guild_storage(fd,aid,guild_id,0); #endif return 0; }
/** * Char-server request to connect to the login-server. * This is needed to exchange packets. * @param fd: file descriptor to parse from (client) * @param sd: client session * @param ip: ipv4 address (client) * @return 0 packet received too shirt, 1 success */ static int logclif_parse_reqcharconnec(int fd, struct login_session_data *sd, char* ip){ if (RFIFOREST(fd) < 86) return 0; { int result; char server_name[20]; char message[256]; uint32 server_ip; uint16 server_port; uint16 type; uint16 new_; safestrncpy(sd->userid, (char*)RFIFOP(fd,2), NAME_LENGTH); safestrncpy(sd->passwd, (char*)RFIFOP(fd,26), NAME_LENGTH); if( login_config.use_md5_passwds ) MD5_String(sd->passwd, sd->passwd); sd->passwdenc = 0; sd->version = login_config.client_version_to_connect; // hack to skip version check server_ip = ntohl(RFIFOL(fd,54)); server_port = ntohs(RFIFOW(fd,58)); safestrncpy(server_name, (char*)RFIFOP(fd,60), 20); type = RFIFOW(fd,82); new_ = RFIFOW(fd,84); RFIFOSKIP(fd,86); ShowInfo("Connection request of the char-server '%s' @ %u.%u.%u.%u:%u (account: '%s', pass: '******', ip: '%s')\n", server_name, CONVIP(server_ip), server_port, sd->userid, sd->passwd, ip); sprintf(message, "charserver - %s@%u.%u.%u.%u:%u", server_name, CONVIP(server_ip), server_port); login_log(session[fd]->client_addr, sd->userid, 100, message); result = login_mmo_auth(sd, true); if( runflag == LOGINSERVER_ST_RUNNING && result == -1 && sd->sex == 'S' && sd->account_id >= 0 && sd->account_id < ARRAYLENGTH(ch_server) && !session_isValid(ch_server[sd->account_id].fd) ) { ShowStatus("Connection of the char-server '%s' accepted.\n", server_name); safestrncpy(ch_server[sd->account_id].name, server_name, sizeof(ch_server[sd->account_id].name)); ch_server[sd->account_id].fd = fd; ch_server[sd->account_id].ip = server_ip; ch_server[sd->account_id].port = server_port; ch_server[sd->account_id].users = 0; ch_server[sd->account_id].type = type; ch_server[sd->account_id].new_ = new_; session[fd]->func_parse = logchrif_parse; session[fd]->flag.server = 1; realloc_fifo(fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK); // send connection success WFIFOHEAD(fd,3); WFIFOW(fd,0) = 0x2711; WFIFOB(fd,2) = 0; WFIFOSET(fd,3); } else { ShowNotice("Connection of the char-server '%s' REFUSED.\n", server_name); WFIFOHEAD(fd,3); WFIFOW(fd,0) = 0x2711; WFIFOB(fd,2) = 3; WFIFOSET(fd,3); } } return 1; }
/** * Entry point from client to log-server. * Function that checks incoming command, then splits it to the correct handler. * @param fd: file descriptor to parse, (link to client) * @return 0=invalid session,marked for disconnection,unknow packet, banned..; 1=success */ int logclif_parse(int fd) { struct login_session_data* sd = (struct login_session_data*)session[fd]->session_data; char ip[16]; uint32 ipl = session[fd]->client_addr; ip2str(ipl, ip); if( session[fd]->flag.eof ) { ShowInfo("Closed connection from '"CL_WHITE"%s"CL_RESET"'.\n", ip); do_close(fd); return 0; } if( sd == NULL ) { // Perform ip-ban check if( login_config.ipban && ipban_check(ipl) ) { ShowStatus("Connection refused: IP isn't authorised (deny/allow, ip: %s).\n", ip); login_log(ipl, "unknown", -3, "ip banned"); WFIFOHEAD(fd,23); WFIFOW(fd,0) = 0x6a; WFIFOB(fd,2) = 3; // 3 = Rejected from Server WFIFOSET(fd,23); set_eof(fd); return 0; } // create a session for this new connection CREATE(session[fd]->session_data, struct login_session_data, 1); sd = (struct login_session_data*)session[fd]->session_data; sd->fd = fd; } while( RFIFOREST(fd) >= 2 ) { uint16 command = RFIFOW(fd,0); int next=1; switch( command ) { // New alive packet: used to verify if client is always alive. case 0x0200: next = logclif_parse_keepalive(fd); break; // client md5 hash (binary) case 0x0204: next = logclif_parse_updclhash(fd,sd); break; // request client login (raw password) case 0x0064: // S 0064 <version>.L <username>.24B <password>.24B <clienttype>.B case 0x0277: // S 0277 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B case 0x02b0: // S 02b0 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B <g_isGravityID>.B // request client login (md5-hashed password) case 0x01dd: // S 01dd <version>.L <username>.24B <password hash>.16B <clienttype>.B case 0x01fa: // S 01fa <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.B(index of the connection in the clientinfo file (+10 if the command-line contains "pc")) case 0x027c: // S 027c <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.13B(junk) case 0x0825: // S 0825 <packetsize>.W <version>.L <clienttype>.B <userid>.24B <password>.27B <mac>.17B <ip>.15B <token>.(packetsize - 0x5C)B next = logclif_parse_reqauth(fd, sd, command, ip); break; // Sending request of the coding key case 0x01db: next = logclif_parse_reqkey(fd, sd); break; // Connection request of a char-server case 0x2710: logclif_parse_reqcharconnec(fd,sd, ip); return 0; // processing will continue elsewhere default: ShowNotice("Abnormal end of connection (ip: %s): Unknown packet 0x%x\n", ip, command); set_eof(fd); return 0; } if(next==0) return 0; // avoid processing of followup packets (prev was probably incomplete) } return 0; }
/** * Received a connection request. * @param fd: file descriptor to parse from (client) * @param sd: client session * @param command: packet type sent * @param ip: ipv4 address (client) * S 0064 <version>.L <username>.24B <password>.24B <clienttype>.B * S 0277 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B * S 02b0 <version>.L <username>.24B <password>.24B <clienttype>.B <ip address>.16B <adapter address>.13B <g_isGravityID>.B * S 01dd <version>.L <username>.24B <password hash>.16B <clienttype>.B * S 01fa <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.B(index of the connection in the clientinfo file (+10 if the command-line contains "pc")) * S 027c <version>.L <username>.24B <password hash>.16B <clienttype>.B <?>.13B(junk) * S 0825 <packetsize>.W <version>.L <clienttype>.B <userid>.24B <password>.27B <mac>.17B <ip>.15B <token>.(packetsize - 0x5C)B * @param fd: fd to parse from (client fd) * @return 0 failure, 1 success */ static int logclif_parse_reqauth(int fd, struct login_session_data *sd, int command, char* ip){ size_t packet_len = RFIFOREST(fd); if( (command == 0x0064 && packet_len < 55) || (command == 0x0277 && packet_len < 84) || (command == 0x02b0 && packet_len < 85) || (command == 0x01dd && packet_len < 47) || (command == 0x01fa && packet_len < 48) || (command == 0x027c && packet_len < 60) || (command == 0x0825 && (packet_len < 4 || packet_len < RFIFOW(fd, 2))) ) return 0; else { int result; uint32 version; char username[NAME_LENGTH]; char password[PASSWD_LENGTH]; unsigned char passhash[16]; uint8 clienttype; bool israwpass = (command==0x0064 || command==0x0277 || command==0x02b0 || command == 0x0825); // Shinryo: For the time being, just use token as password. if(command == 0x0825) { char *accname = (char *)RFIFOP(fd, 9); char *token = (char *)RFIFOP(fd, 0x5C); size_t uAccLen = strlen(accname); size_t uTokenLen = RFIFOREST(fd) - 0x5C; version = RFIFOL(fd,4); if(uAccLen > NAME_LENGTH - 1 || uAccLen <= 0 || uTokenLen > NAME_LENGTH - 1 || uTokenLen <= 0) { logclif_auth_failed(sd, 3); return 0; } safestrncpy(username, accname, uAccLen + 1); safestrncpy(password, token, uTokenLen + 1); clienttype = RFIFOB(fd, 8); } else { version = RFIFOL(fd,2); safestrncpy(username, (const char*)RFIFOP(fd,6), NAME_LENGTH); if( israwpass ) { safestrncpy(password, (const char*)RFIFOP(fd,30), PASSWD_LENGTH); clienttype = RFIFOB(fd,54); } else { memcpy(passhash, RFIFOP(fd,30), 16); clienttype = RFIFOB(fd,46); } } RFIFOSKIP(fd,RFIFOREST(fd)); // assume no other packet was sent sd->clienttype = clienttype; sd->version = version; safestrncpy(sd->userid, username, NAME_LENGTH); if( israwpass ) { ShowStatus("Request for connection of %s (ip: %s) version=%d\n", sd->userid, ip,sd->version); safestrncpy(sd->passwd, password, NAME_LENGTH); if( login_config.use_md5_passwds ) MD5_String(sd->passwd, sd->passwd); sd->passwdenc = 0; } else { ShowStatus("Request for connection (passwdenc mode) of %s (ip: %s) version=%d\n", sd->userid, ip,sd->version); bin2hex(sd->passwd, passhash, 16); // raw binary data here! sd->passwdenc = PASSWORDENC; } if( sd->passwdenc != 0 && login_config.use_md5_passwds ) { logclif_auth_failed(sd, 3); // send "rejected from server" return 0; } result = login_mmo_auth(sd, false); if( result == -1 ) logclif_auth_ok(sd); else logclif_auth_failed(sd, result); } return 1; }
// Wisp/page request to send int mapif_parse_WisRequest(int fd) { struct WisData* wd; char name[NAME_LENGTH]; char esc_name[NAME_LENGTH*2+1];// escaped name char* data; size_t len; if ( fd <= 0 ) {return 0;} // check if we have a valid fd if (RFIFOW(fd,2)-52 >= sizeof(wd->msg)) { ShowWarning("inter: Wis message size too long.\n"); return 0; } else if (RFIFOW(fd,2)-52 <= 0) { // normaly, impossible, but who knows... ShowError("inter: Wis message doesn't exist.\n"); return 0; } safestrncpy(name, (char*)RFIFOP(fd,28), NAME_LENGTH); //Received name may be too large and not contain \0! [Skotlex] Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `name` FROM `%s` WHERE `name`='%s'", schema_config.char_db, esc_name) ) Sql_ShowDebug(sql_handle); // search if character exists before to ask all map-servers if( SQL_SUCCESS != Sql_NextRow(sql_handle) ) { unsigned char buf[27]; WBUFW(buf, 0) = 0x3802; memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH); WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target chmapif_send(fd, buf, 27); } else {// Character exists. So, ask all map-servers // to be sure of the correct name, rewrite it Sql_GetData(sql_handle, 0, &data, &len); memset(name, 0, NAME_LENGTH); memcpy(name, data, min(len, NAME_LENGTH)); // if source is destination, don't ask other servers. if( strncmp((const char*)RFIFOP(fd,4), name, NAME_LENGTH) == 0 ) { uint8 buf[27]; WBUFW(buf, 0) = 0x3802; memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH); WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target chmapif_send(fd, buf, 27); } else { static int wisid = 0; CREATE(wd, struct WisData, 1); // Whether the failure of previous wisp/page transmission (timeout) check_ttl_wisdata(); wd->id = ++wisid; wd->fd = fd; wd->len= RFIFOW(fd,2)-52; memcpy(wd->src, RFIFOP(fd, 4), NAME_LENGTH); memcpy(wd->dst, RFIFOP(fd,28), NAME_LENGTH); memcpy(wd->msg, RFIFOP(fd,52), wd->len); wd->tick = gettick(); idb_put(wis_db, wd->id, wd); mapif_wis_message(wd); } } Sql_FreeResult(sql_handle); return 0; }
// broadcast sending int mapif_parse_broadcast(int fd) { mapif_broadcast(RFIFOP(fd,16), RFIFOW(fd,2), RFIFOL(fd,4), RFIFOW(fd,8), RFIFOW(fd,10), RFIFOW(fd,12), RFIFOW(fd,14), fd); return 0; }
//------------------------------------------------ //Guild bound items pull for offline characters [Akinari] //Revised by [Mhalicot] //------------------------------------------------ int mapif_parse_ItemBoundRetrieve_sub(int fd) { #ifdef GP_BOUND_ITEMS StringBuf buf; SqlStmt* stmt; struct item item; int j, i=0, s=0, bound_qt=0; struct item items[MAX_INVENTORY]; unsigned int bound_item[MAX_INVENTORY] = {0}; int char_id = RFIFOL(fd,2); int aid = RFIFOL(fd,6); int guild_id = RFIFOW(fd,10); StrBuf->Init(&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`='%d' AND `bound` = '%d'",inventory_db,char_id,IBT_GUILD); stmt = SQL->StmtMalloc(sql_handle); if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) || SQL_ERROR == SQL->StmtExecute(stmt) ) { Sql_ShowDebug(sql_handle); SQL->StmtFree(stmt); StrBuf->Destroy(&buf); return 1; } 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, 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_UCHAR, &item.bound, 0, NULL, NULL); SQL->StmtBindColumn(stmt, 9, SQLDT_UINT64, &item.unique_id, 0, NULL, NULL); for( j = 0; j < MAX_SLOTS; ++j ) SQL->StmtBindColumn(stmt, 10+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); while( SQL_SUCCESS == SQL->StmtNextRow(stmt) ) { memcpy(&items[i],&item,sizeof(struct item)); i++; } SQL->FreeResult(sql_handle); if(!i) { //No items found - No need to continue StrBuf->Destroy(&buf); SQL->StmtFree(stmt); return 0; } //First we delete the character's items StrBuf->Clear(&buf); StrBuf->Printf(&buf, "DELETE FROM `%s` WHERE",inventory_db); for(j=0; j<i; j++) { if( j ) StrBuf->AppendStr(&buf, " OR"); StrBuf->Printf(&buf, " `id`=%d",items[j].id); if( items[j].bound && items[j].equip ) { // Only the items that are also stored in `char` `equip` if( items[j].equip&EQP_HAND_R || items[j].equip&EQP_HAND_L || items[j].equip&EQP_HEAD_TOP || items[j].equip&EQP_HEAD_MID || items[j].equip&EQP_HEAD_LOW || items[j].equip&EQP_GARMENT ) { bound_item[bound_qt] = items[j].equip; bound_qt++; } } } if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) || SQL_ERROR == SQL->StmtExecute(stmt) ) { Sql_ShowDebug(sql_handle); SQL->StmtFree(stmt); StrBuf->Destroy(&buf); return 1; } // Removes any view id that was set by an item that was removed if( bound_qt ) { #define CHECK_REMOVE(var,mask,token) do { /* Verifies equip bitmasks (see item.equip) and handles the sql statement */ \ if ((var)&(mask)) { \ if ((var) != (mask) && s) StrBuf->AppendStr(&buf, ","); \ StrBuf->AppendStr(&buf,"`"#token"`='0'"); \ (var) &= ~(mask); \ s++; \ } \ } while(0) StrBuf->Clear(&buf); StrBuf->Printf(&buf, "UPDATE `%s` SET ", char_db); for( j = 0; j < bound_qt; j++ ) { // Equips can be at more than one slot at the same time CHECK_REMOVE(bound_item[j],EQP_HAND_R,weapon); CHECK_REMOVE(bound_item[j],EQP_HAND_L,shield); CHECK_REMOVE(bound_item[j],EQP_HEAD_TOP,head_top); CHECK_REMOVE(bound_item[j],EQP_HEAD_MID,head_mid); CHECK_REMOVE(bound_item[j],EQP_HEAD_LOW,head_bottom); CHECK_REMOVE(bound_item[j],EQP_GARMENT,robe); } StrBuf->Printf(&buf, " WHERE `char_id`='%d'", char_id); if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) || SQL_ERROR == SQL->StmtExecute(stmt) ) { Sql_ShowDebug(sql_handle); SQL->StmtFree(stmt); StrBuf->Destroy(&buf); return 1; } #undef CHECK_REMOVE } //Now let's update the guild storage with those deleted items /// TODO/FIXME: /// This approach is basically the same as the one from memitemdata_to_sql, but /// the latter compares current database values and this is not needed in this case /// maybe sometime separate memitemdata_to_sql into different methods in order to use /// call that function here as well [Panikon] StrBuf->Clear(&buf); StrBuf->Printf(&buf,"INSERT INTO `%s` (`guild_id`,`nameid`,`amount`,`equip`,`identify`,`refine`," "`attribute`,`expire_time`,`bound`,`unique_id`", guild_storage_db); for( s = 0; s < MAX_SLOTS; ++s ) StrBuf->Printf(&buf, ", `card%d`", s); StrBuf->AppendStr(&buf," ) VALUES "); for( j = 0; j < i; ++j ) { if( j ) StrBuf->AppendStr(&buf, ","); StrBuf->Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d'", guild_id, items[j].nameid, items[j].amount, items[j].equip, items[j].identify, items[j].refine, items[j].attribute, items[j].expire_time, items[j].bound, items[j].unique_id); for( s = 0; s < MAX_SLOTS; ++s ) StrBuf->Printf(&buf, ", '%d'", items[j].card[s]); StrBuf->AppendStr(&buf, ")"); } if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) || SQL_ERROR == SQL->StmtExecute(stmt) ) { Sql_ShowDebug(sql_handle); SQL->StmtFree(stmt); StrBuf->Destroy(&buf); return 1; } StrBuf->Destroy(&buf); SQL->StmtFree(stmt); //Finally reload storage and tell map we're done mapif_load_guild_storage(fd,aid,guild_id,0); // If character is logged in char, disconnect disconnect_player(aid); #endif return 0; }
/*! * \brief Parse from Login * \details Parse informations from auth server * \author Fimbulwinter Development Team * \author GreenBox * \date 08/12/11 * **/ int CharServer::parse_from_login(tcp_connection::pointer cl) { CharSessionData *csd; if (cl->flags.eof) { cl->do_close(); connect_to_auth(); return 0; } while(RFIFOREST(cl) >= 2) { unsigned short cmd = RFIFOW(cl, 0); switch (cmd) { case INTER_AC_REQ_ACC_DATA_REPLY: if (RFIFOREST(cl) < 62) return 0; { int tag = RFIFOL(cl, 2); if (tcp_connection::session_exists(tag) && (csd = (CharSessionData *)tcp_connection::get_session_by_tag(tag)->get_data())) { memcpy(csd->email, RFIFOP(cl,6), 40); csd->expiration_time = (time_t)RFIFOL(cl,46); csd->gmlevel = RFIFOB(cl,50); strncpy(csd->birthdate, (const char*)RFIFOP(cl,51), sizeof(csd->birthdate)); // TODO: Check max users and min level to bypass csd->auth = true; send_chars(csd->cl, csd); } } cl->skip(62); break; case INTER_AC_AUTH_REPLY: if (RFIFOREST(cl) < 20) return 0; { int account_id = RFIFOL(cl,2); unsigned int login_id1 = RFIFOL(cl,6); unsigned int login_id2 = RFIFOL(cl,10); unsigned char sex = RFIFOB(cl,14); unsigned char result = RFIFOB(cl,15); int request_id = RFIFOL(cl,16); cl->skip(20); if (tcp_connection::session_exists(request_id) && (csd = (CharSessionData *)tcp_connection::get_session_by_tag(request_id)->get_data()) && !csd->auth && csd->account_id == account_id && csd->login_id1 == login_id1 && csd->login_id2 == login_id2 && csd->sex == sex) { tcp_connection::pointer client_cl = csd->cl; if (result == 0) { auth_ok(client_cl, csd); } else { WFIFOPACKET(client_cl,packet,HC_REFUSE_ENTER); packet->header = HEADER_HC_REFUSE_ENTER; packet->error_code = 0; client_cl->send_buffer(sizeof(struct PACKET_HC_REFUSE_ENTER)); } } } break; case INTER_AC_KICK: { int aid = RFIFOL(cl, 2); cl->skip(6); if (online_chars.count(aid)) { if (online_chars[aid].server > -1) { // TODO: Kick from ZoneServer } else { if (!online_chars[aid].cl->flags.eof) { WFIFOPACKET(online_chars[aid].cl,packet,SC_NOTIFY_BAN); packet->header = HEADER_SC_NOTIFY_BAN; packet->error_code = 2; online_chars[aid].cl->send_buffer(sizeof(struct PACKET_SC_NOTIFY_BAN)); online_chars[aid].cl->set_eof(); } else set_char_offline(aid, -1); } } } break; case INTER_AC_LOGIN_REPLY: { unsigned char result = RFIFOB(cl, 2); cl->skip(3); if (result == 0) { auth_conn_ok = true; ShowStatus("Connected to AuthServer.\n"); } else { ShowError("Connectiong rejected from AuthServer."); cl->set_eof(); return 0; } } break; default: ShowWarning("Unknown packet 0x%x sent from AuthServer, closing connection.\n", cmd, cl->socket().remote_endpoint().address().to_string().c_str()); cl->set_eof(); return 0; } } return 0; }