From aee2f6317e1c927847993801b5973d7e2e27a418 Mon Sep 17 00:00:00 2001 From: shennetsind Date: Sat, 16 Nov 2013 16:30:11 -0200 Subject: Introducing Character Ban Support. @charban/@charunban, can temporarily block any accounts as opposed to the usual account-wide block. Special Thanks to Haruna, Yommy! Signed-off-by: shennetsind --- src/char/char.c | 215 +++++++++++++++++++++++++++++++++++++-------------- src/char/char.h | 1 + src/common/showmsg.h | 1 + src/common/utils.c | 11 +++ src/common/utils.h | 2 + src/login/login.c | 13 +--- src/map/atcommand.c | 8 +- src/map/chrif.c | 24 ++++-- src/map/clif.c | 32 ++++++++ src/map/clif.h | 2 + 10 files changed, 232 insertions(+), 77 deletions(-) (limited to 'src') diff --git a/src/char/char.c b/src/char/char.c index 112bbe0ae..ce05f32f4 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -1004,6 +1004,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]; + size_t unban_time; stmt = SQL->StmtMalloc(sql_handle); if( stmt == NULL ) { @@ -1012,8 +1013,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 " @@ -1021,7 +1024,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) @@ -1061,6 +1064,7 @@ int mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) || 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); @@ -1071,6 +1075,7 @@ 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); sd->found_char[p.slot] = p.char_id; + sd->unban_time[p.slot] = unban_time; j += mmo_char_tobuf(WBUFP(buf, j), &p); } @@ -1931,6 +1936,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)); @@ -1938,6 +1944,34 @@ 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] > now); + + 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] > now ) { + WFIFOL(fd, 4 + (24*c)) = sd->found_char[i]; + timestamp2string((char*)WFIFOP(fd,8 + (28*c)), 20, sd->unban_time[i], "%Y-%m-%d %H:%M:%S"); + 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 @@ -2304,6 +2338,9 @@ int parse_fromlogin(int fd) { #else mmo_char_send006b(i, sd); #endif + #if PACKETVER >= 20080000 + mmo_char_send020d(i, sd); + #endif #if PACKETVER >= 20110309 pincode->handle(i, sd); #endif @@ -3141,7 +3178,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; { @@ -3150,7 +3187,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); @@ -3160,25 +3197,24 @@ 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`,`name`,`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 - { + result = 1; // 1-player not found + } else { char name[NAME_LENGTH]; - int account_id; + 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, 2, &data, NULL); char_id = atoi(data); + SQL->GetData(sql_handle, 3, &data, NULL); unban_time = atol(data); if( login_fd <= 0 ) result = 3; // 3-login-server offline @@ -3186,53 +3222,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) = account_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; @@ -3984,6 +4078,15 @@ int parse_char(int fd) char_id = atoi(data); SQL->FreeResult(sql_handle); + /* 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 realise 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 */ diff --git a/src/char/char.h b/src/char/char.h index a3bbdd904..c7a387645 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -20,6 +20,7 @@ struct char_session_data { bool auth; // whether the session is authed or not int account_id, login_id1, login_id2, sex; int found_char[MAX_CHARS]; // ids of chars on this account + time_t unban_time[MAX_CHARS]; // char unban time array char email[40]; // e-mail (default: a@a.com) by [Yor] time_t expiration_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) int group_id; // permission diff --git a/src/common/showmsg.h b/src/common/showmsg.h index 59a0d9538..43d38973f 100644 --- a/src/common/showmsg.h +++ b/src/common/showmsg.h @@ -99,5 +99,6 @@ extern void ClearScreen(void); extern void ShowFatalError(const char *, ...); extern void ShowConfigWarning(config_setting_t *config, const char *string, ...); #endif +extern int _vShowMessage(enum msg_type flag, const char *string, va_list ap); #endif /* _SHOWMSG_H_ */ diff --git a/src/common/utils.c b/src/common/utils.c index 9e3dbac47..9a7d4971b 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -287,6 +287,17 @@ unsigned int get_percentage(const unsigned int A, const unsigned int B) return (unsigned int)floor(result); } +//----------------------------------------------------- +// custom timestamp formatting (from eApp) +//----------------------------------------------------- +const char* timestamp2string(char* str, size_t size, time_t timestamp, const char* format) +{ + size_t len = strftime(str, size, format, localtime(×tamp)); + memset(str + len, '\0', size - len); + return str; +} + + /* [Ind/Hercules] Caching */ bool HCache_check(const char *file) { struct stat bufa, bufb; diff --git a/src/common/utils.h b/src/common/utils.h index 32087d78f..3e1463d6b 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -25,6 +25,8 @@ bool exists(const char* filename); /// calculates the value of A / B, in percent (rounded down) unsigned int get_percentage(const unsigned int A, const unsigned int B); +const char* timestamp2string(char* str, size_t size, time_t timestamp, const char* format); + ////////////////////////////////////////////////////////////////////////// // byte word dword access [Shinomori] ////////////////////////////////////////////////////////////////////////// diff --git a/src/login/login.c b/src/login/login.c index 3da592ebc..75247845d 100644 --- a/src/login/login.c +++ b/src/login/login.c @@ -11,6 +11,7 @@ #include "../common/socket.h" #include "../common/strlib.h" #include "../common/timer.h" +#include "../common/utils.h" #include "../common/HPM.h" #include "account.h" #include "ipban.h" @@ -271,18 +272,6 @@ bool check_password(const char* md5key, int passwdenc, const char* passwd, const } } - -//----------------------------------------------------- -// custom timestamp formatting (from eApp) -//----------------------------------------------------- -const char* timestamp2string(char* str, size_t size, time_t timestamp, const char* format) -{ - size_t len = strftime(str, size, format, localtime(×tamp)); - memset(str + len, '\0', size - len); - return str; -} - - //-------------------------------------------- // Test to know if an IP come from LAN or WAN. //-------------------------------------------- diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 902d179db..a2bea32a8 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -2798,7 +2798,7 @@ ACMD(char_ban) return false; } - chrif->char_ask_name(sd->status.account_id, atcmd_player_name, 2, year, month, day, hour, minute, second); // type: 2 - ban + chrif->char_ask_name(sd->status.account_id, atcmd_player_name, !strcmpi(info->command,"charban") ? 6 : 2, year, month, day, hour, minute, second); // type: 2 - ban; 6 - charban clif->message(fd, msg_txt(88)); // Character name sent to char-server to ask it. return true; @@ -2838,7 +2838,7 @@ ACMD(char_unban) } // send answer to login server via char-server - chrif->char_ask_name(sd->status.account_id, atcmd_player_name, 4, 0, 0, 0, 0, 0, 0); // type: 4 - unban + chrif->char_ask_name(sd->status.account_id, atcmd_player_name, !strcmpi(info->command,"charunban") ? 7 : 4, 0, 0, 0, 0, 0, 0); // type: 4 - unban account; type 7 - unban character clif->message(fd, msg_txt(88)); // Character name sent to char-server to ask it. return true; @@ -3138,7 +3138,7 @@ ACMD(spiritball) if( !message || !*message || (number = atoi(message)) < 0 || number > max_spiritballs ) { char msg[CHAT_SIZE_MAX]; - safesnprintf(msg, sizeof(msg), msg_txt(1028), max_spiritballs); // Please enter a party name (usage: @party ). + safesnprintf(msg, sizeof(msg), msg_txt(1028), max_spiritballs); // Please enter an amount (usage: @spiritball ). clif->message(fd, msg); return false; } @@ -9448,7 +9448,9 @@ void atcommand_basecommands(void) { ACMD_DEF2("allstats", stat_all), ACMD_DEF2("block", char_block), ACMD_DEF2("ban", char_ban), + ACMD_DEF2("charban", char_ban),/* char-specific ban time */ ACMD_DEF2("unblock", char_unblock), + ACMD_DEF2("charunban", char_unban),/* char-specific ban time */ ACMD_DEF2("unban", char_unban), ACMD_DEF2("mount", mount_peco), ACMD_DEF(guildspy), diff --git a/src/map/chrif.c b/src/map/chrif.c index 87ec71ec5..56572d492 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -752,7 +752,7 @@ int chrif_changeemail(int id, const char *actual_email, const char *new_email) { * S 2b0e .l .24B .w { .w .w .w .w .w .w } * Send an account modification request to the login server (via char server). * type of operation: - * 1: block, 2: ban, 3: unblock, 4: unban, 5: changesex (use next function for 5) + * 1: block, 2: ban, 3: unblock, 4: unban, 5: changesex (use next function for 5), 6: charban *------------------------------------------*/ int chrif_char_ask_name(int acc, const char* character_name, unsigned short operation_type, int year, int month, int day, int hour, int minute, int second) { @@ -764,7 +764,7 @@ int chrif_char_ask_name(int acc, const char* character_name, unsigned short oper safestrncpy((char*)WFIFOP(chrif->fd,6), character_name, NAME_LENGTH); WFIFOW(chrif->fd,30) = operation_type; - if ( operation_type == 2 ) { + if ( operation_type == 2 || operation_type == 6 ) { WFIFOW(chrif->fd,32) = year; WFIFOW(chrif->fd,34) = month; WFIFOW(chrif->fd,36) = day; @@ -800,7 +800,7 @@ int chrif_changesex(struct map_session_data *sd) { * R 2b0f .l .24B .w .w * Processing a reply to chrif->char_ask_name() (request to modify an account). * type of operation: - * 1: block, 2: ban, 3: unblock, 4: unban, 5: changesex + * 1: block, 2: ban, 3: unblock, 4: unban, 5: changesex, 6: charban, 7: charunban * type of answer: * 0: login-server request done * 1: player not found @@ -811,6 +811,7 @@ void chrif_char_ask_name_answer(int acc, const char* player_name, uint16 type, u struct map_session_data* sd; char action[25]; char output[256]; + bool charsrv = ( type == 6 || type == 7 ) ? true : false; sd = map->id2sd(acc); @@ -819,13 +820,17 @@ void chrif_char_ask_name_answer(int acc, const char* player_name, uint16 type, u return; } + /* re-use previous msg_txt */ + if( type == 6 ) type = 2; + if( type == 7 ) type = 4; + if( type > 0 && type <= 5 ) snprintf(action,25,"%s",msg_txt(427+type)); //block|ban|unblock|unban|change the sex of else snprintf(action,25,"???"); switch( answer ) { - case 0 : sprintf(output, msg_txt(424), action, NAME_LENGTH, player_name); break; + case 0 : sprintf(output, msg_txt(charsrv?434:424), action, NAME_LENGTH, player_name); break; case 1 : sprintf(output, msg_txt(425), NAME_LENGTH, player_name); break; case 2 : sprintf(output, msg_txt(426), action, NAME_LENGTH, player_name); break; case 3 : sprintf(output, msg_txt(427), action, NAME_LENGTH, player_name); break; @@ -980,7 +985,7 @@ int chrif_accountban(int fd) { } sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters - if (RFIFOB(fd,6) == 0) { // 0: change of statut, 1: ban + if (RFIFOB(fd,6) == 0) { // 0: change of statut int ret_status = RFIFOL(fd,7); // status or final date of a banishment if(0message(sd->fd, msg_txt(411+ret_status)); @@ -988,13 +993,20 @@ int chrif_accountban(int fd) { clif->message(sd->fd, msg_txt(421)); else clif->message(sd->fd, msg_txt(420)); //"Your account has not more authorised." - } else if (RFIFOB(fd,6) == 1) { // 0: change of statut, 1: ban + } else if (RFIFOB(fd,6) == 1) { // 1: ban time_t timestamp; char tmpstr[2048]; timestamp = (time_t)RFIFOL(fd,7); // status or final date of a banishment strcpy(tmpstr, msg_txt(423)); //"Your account has been banished until " strftime(tmpstr + strlen(tmpstr), 24, "%d-%m-%Y %H:%M:%S", localtime(×tamp)); clif->message(sd->fd, tmpstr); + } else if (RFIFOB(fd,6) == 2) { // 2: change of status for character + time_t timestamp; + char tmpstr[2048]; + timestamp = (time_t)RFIFOL(fd,7); // status or final date of a banishment + strcpy(tmpstr, msg_txt(433)); //"This character has been banned until " + strftime(tmpstr + strlen(tmpstr), 24, "%d-%m-%Y %H:%M:%S", localtime(×tamp)); + clif->message(sd->fd, tmpstr); } set_eof(sd->fd); // forced to disconnect for the change diff --git a/src/map/clif.c b/src/map/clif.c index d84a0dea8..6740c7a74 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -5634,7 +5634,38 @@ void clif_displaymessage2(const int fd, const char* mes) { aFree(message); } } +/* oh noo! another version of 0x8e! */ +void clif_displaymessage_sprintf(const int fd, const char* mes, ...) { + va_list ap; + if( fd == -2 ) { + ShowInfo("HCP: "); + va_start(ap,mes); + _vShowMessage(MSG_NONE,mes,ap); + va_end(ap); + ShowMessage("\n"); + } else if ( fd > 0 ) { + int len = 1; + char *ptr; + + WFIFOHEAD(fd, 5 + 255);/* ensure the maximum */ + + /* process */ + va_start(ap,mes); + len += vsnprintf((char *)WFIFOP(fd,4), 255, mes, ap); + va_end(ap); + + /* adjusting */ + ptr = (char *)WFIFOP(fd,4); + ptr[len - 1] = '\0'; + + /* */ + WFIFOW(fd,0) = 0x8e; + WFIFOW(fd,2) = 5 + len; // 4 + len + NULL teminate + + WFIFOSET(fd, 5 + len); + } +} /// Send broadcast message in yellow or blue without font formatting (ZC_BROADCAST). /// 009a .W .?B void clif_broadcast(struct block_list* bl, const char* mes, int len, int type, enum send_target target) @@ -18407,6 +18438,7 @@ void clif_defaults(void) { clif->msgtable_num = clif_msgtable_num; clif->message = clif_displaymessage; clif->messageln = clif_displaymessage2; + clif->messages = clif_displaymessage_sprintf; clif->colormes = clif_colormes; clif->process_message = clif_process_message; clif->wisexin = clif_wisexin; diff --git a/src/map/clif.h b/src/map/clif.h index e50af7432..043f7dd3a 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -787,6 +787,8 @@ struct clif_interface { void (*msgtable_num) (int fd, int line, int num); void (*message) (const int fd, const char* mes); void (*messageln) (const int fd, const char* mes); + /* message+s(printf) */ + void (*messages) (const int fd, const char* mes, ...); int (*colormes) (int fd, enum clif_colors color, const char* msg); bool (*process_message) (struct map_session_data* sd, int format, char** name_, int* namelen_, char** message_, int* messagelen_); void (*wisexin) (struct map_session_data *sd,int type,int flag); -- cgit v1.2.3-60-g2f50