summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--conf/char-server.conf5
-rw-r--r--conf/messages.conf3
-rw-r--r--sql-files/main.sql2
-rw-r--r--sql-files/upgrades/2013-11-16--07-49.sql3
-rw-r--r--sql-files/upgrades/index.txt3
-rw-r--r--src/char/char.c302
-rw-r--r--src/char/char.h1
-rw-r--r--src/common/mmo.h8
-rw-r--r--src/common/showmsg.h1
-rw-r--r--src/common/utils.c11
-rw-r--r--src/common/utils.h2
-rw-r--r--src/login/login.c25
-rw-r--r--src/map/atcommand.c54
-rw-r--r--src/map/chrif.c24
-rw-r--r--src/map/clif.c32
-rw-r--r--src/map/clif.h2
16 files changed, 341 insertions, 137 deletions
diff --git a/conf/char-server.conf b/conf/char-server.conf
index b2120471c..a7f26daaa 100644
--- a/conf/char-server.conf
+++ b/conf/char-server.conf
@@ -65,7 +65,10 @@ console_silent: 0
// No functional side effects at the moment.
// Displayed next to the server name in the client.
// 0=normal, 1=maintenance, 2=over 18, 3=paying, 4=P2P
-char_maintenance: 0
+char_server_type: 0
+
+// Minimum Group ID to join char server when it is on char_server_type 1 (maintenance)
+char_maintenance_min_group_id: 99
// Enable or disable creation of new characters.
// Now it is actually supported [Kevin]
diff --git a/conf/messages.conf b/conf/messages.conf
index 2d429d79e..f98a91083 100644
--- a/conf/messages.conf
+++ b/conf/messages.conf
@@ -452,7 +452,8 @@
430: unblock
431: unban
432: change the sex of
-
+433: This character has been banned until
+434: Char-server has been asked to %s the character '%.*s'.
// Homunculus messages
450: You already have a homunculus
diff --git a/sql-files/main.sql b/sql-files/main.sql
index cd50c10fa..60b21285d 100644
--- a/sql-files/main.sql
+++ b/sql-files/main.sql
@@ -110,6 +110,7 @@ CREATE TABLE IF NOT EXISTS `char` (
`slotchange` SMALLINT(3) unsigned NOT NULL default '0',
`char_opt` INT( 11 ) unsigned NOT NULL default '0',
`font` TINYINT( 3 ) UNSIGNED NOT NULL DEFAULT '0',
+ `unban_time` int(11) unsigned NOT NULL default '0',
PRIMARY KEY (`char_id`),
UNIQUE KEY `name_key` (`name`),
KEY `account_id` (`account_id`),
@@ -669,6 +670,7 @@ INSERT INTO `sql_updates` (`timestamp`) VALUES (1383167577);
INSERT INTO `sql_updates` (`timestamp`) VALUES (1383205740);
INSERT INTO `sql_updates` (`timestamp`) VALUES (1383955424);
INSERT INTO `sql_updates` (`timestamp`) VALUES (1384545461);
+INSERT INTO `sql_updates` (`timestamp`) VALUES (1384588175);
--
-- Table structure for table `sstatus`
diff --git a/sql-files/upgrades/2013-11-16--07-49.sql b/sql-files/upgrades/2013-11-16--07-49.sql
new file mode 100644
index 000000000..9f4b80452
--- /dev/null
+++ b/sql-files/upgrades/2013-11-16--07-49.sql
@@ -0,0 +1,3 @@
+#1384588175
+ALTER TABLE `char` ADD COLUMN `unban_time` int(11) unsigned NOT NULL default '0';
+INSERT INTO `sql_updates` (`timestamp`) VALUES (1384588175); \ No newline at end of file
diff --git a/sql-files/upgrades/index.txt b/sql-files/upgrades/index.txt
index 4fed9ff9b..1e76ee41b 100644
--- a/sql-files/upgrades/index.txt
+++ b/sql-files/upgrades/index.txt
@@ -12,4 +12,5 @@
2013-10-31--07-49.sql
2013-11-09--00-03.sql
2013-11-15--00-06.sql
-2013-11-15--19-57.sql \ No newline at end of file
+2013-11-15--19-57.sql
+2013-11-16--07-49.sql \ No newline at end of file
diff --git a/src/char/char.c b/src/char/char.c
index ce4fcbe53..ce05f32f4 100644
--- a/src/char/char.c
+++ b/src/char/char.c
@@ -105,7 +105,8 @@ 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;
@@ -1003,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 ) {
@@ -1011,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 "
@@ -1020,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)
@@ -1060,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);
@@ -1070,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);
}
@@ -1930,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));
@@ -1937,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
@@ -2226,7 +2261,7 @@ int parse_fromlogin(int fd) {
// acknowledgement of account authentication request
case 0x2713:
- if (RFIFOREST(fd) < 25)
+ if (RFIFOREST(fd) < 29)
return 0;
{
int account_id = RFIFOL(fd,2);
@@ -2237,7 +2272,8 @@ 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);
+ RFIFOSKIP(fd,29);
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 )
@@ -2245,17 +2281,24 @@ 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;
+ }
+ 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;
}
}
}
@@ -2268,7 +2311,6 @@ int parse_fromlogin(int fd) {
// 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;
memcpy(sd->email, RFIFOP(fd,6), 40);
sd->expiration_time = (time_t)RFIFOL(fd,46);
sd->group_id = RFIFOB(fd,50);
@@ -2281,10 +2323,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);
@@ -2298,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
@@ -3135,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;
{
@@ -3144,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);
@@ -3154,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
@@ -3180,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(&timestamp);
+ 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*)&timestamp, 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;
@@ -3794,7 +3894,7 @@ static void char_delete2_cancel(int fd, struct char_session_data* sd)
int parse_char(int fd)
{
- int i, ch;
+ int i;
char email[40];
unsigned short cmd;
int map_fd;
@@ -3872,8 +3972,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
@@ -3889,6 +3988,14 @@ 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;
+ }
idb_remove(auth_db, account_id);
char_auth_ok(fd, sd);
}
@@ -3924,9 +4031,11 @@ int parse_char(int fd)
int char_id;
uint32 subnet_map_ip;
struct auth_node* node;
+ int server_id = 0;
int slot = RFIFOB(fd,2);
RFIFOSKIP(fd,3);
+
#if PACKETVER >= 20110309
if( *pincode->enabled ){ // hack check
struct online_char_data* character;
@@ -3940,6 +4049,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) )
@@ -3956,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 */
@@ -4095,6 +4226,7 @@ int parse_char(int fd)
/* Others I found [Ind] */
/* 0x02 = Symbols in Character Names are forbidden */
/* 0x03 = You are not elegible to open the Character Slot. */
+ /* 0x0B = This service is only available for premium users. */
switch (i) {
case -1: WFIFOB(fd,2) = 0x00; break;
case -2: WFIFOB(fd,2) = 0xFF; break;
@@ -4115,9 +4247,7 @@ 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] = i; // the char_id of the new char
}
#if PACKETVER >= 20120307
RFIFOSKIP(fd,31);
@@ -4176,9 +4306,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){
@@ -4592,7 +4720,7 @@ int check_connect_login_server(int tid, int64 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);
@@ -4852,8 +4980,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) {
@@ -4940,6 +5068,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
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/mmo.h b/src/common/mmo.h
index 369f5c894..309203aa8 100644
--- a/src/common/mmo.h
+++ b/src/common/mmo.h
@@ -853,6 +853,14 @@ enum ammo_type {
A_THROWWEAPON //9
};
+enum e_char_server_type {
+ CST_NORMAL = 0,
+ CST_MAINTENANCE = 1,
+ CST_OVER18 = 2,
+ CST_PAYING = 3,
+ CST_P2P = 4,
+};
+
/* packet size constant for itemlist */
#if MAX_INVENTORY > MAX_STORAGE && MAX_INVENTORY > MAX_CART
#define MAX_ITEMLIST MAX_INVENTORY
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(&timestamp));
+ 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 73e89d4ff..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"
@@ -65,6 +66,7 @@ struct auth_node {
char sex;
uint32 version;
uint8 clienttype;
+ int group_id;
};
static DBMap* auth_db; // int account_id -> struct auth_node*
@@ -270,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(&timestamp));
- memset(str + len, '\0', size - len);
- return str;
-}
-
-
//--------------------------------------------
// Test to know if an IP come from LAN or WAN.
//--------------------------------------------
@@ -408,7 +398,7 @@ int parse_fromchar(int fd)
//ShowStatus("Char-server '%s': authentication of the account %d accepted (ip: %s).\n", server[id].name, account_id, ip);
// send ack
- WFIFOHEAD(fd,25);
+ WFIFOHEAD(fd,29);
WFIFOW(fd,0) = 0x2713;
WFIFOL(fd,2) = account_id;
WFIFOL(fd,6) = login_id1;
@@ -418,7 +408,8 @@ int parse_fromchar(int fd)
WFIFOL(fd,16) = request_id;
WFIFOL(fd,20) = node->version;
WFIFOB(fd,24) = node->clienttype;
- WFIFOSET(fd,25);
+ WFIFOL(fd,25) = node->group_id;
+ WFIFOSET(fd,29);
// each auth entry can only be used once
idb_remove(auth_db, account_id);
@@ -426,7 +417,7 @@ int parse_fromchar(int fd)
else
{// authentication not found
ShowStatus("Char-server '%s': authentication of the account %d REFUSED (ip: %s).\n", server[id].name, account_id, ip);
- WFIFOHEAD(fd,25);
+ WFIFOHEAD(fd,29);
WFIFOW(fd,0) = 0x2713;
WFIFOL(fd,2) = account_id;
WFIFOL(fd,6) = login_id1;
@@ -436,7 +427,8 @@ int parse_fromchar(int fd)
WFIFOL(fd,16) = request_id;
WFIFOL(fd,20) = 0;
WFIFOB(fd,24) = 0;
- WFIFOSET(fd,25);
+ WFIFOL(fd,25) = 0;
+ WFIFOSET(fd,29);
}
}
break;
@@ -1206,6 +1198,7 @@ void login_auth_ok(struct login_session_data* sd)
node->ip = ip;
node->version = sd->version;
node->clienttype = sd->clienttype;
+ node->group_id = sd->group_id;
idb_put(auth_db, sd->account_id, node);
{
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
index cdd257195..a2bea32a8 100644
--- a/src/map/atcommand.c
+++ b/src/map/atcommand.c
@@ -549,16 +549,16 @@ ACMD(who) {
int display_type = 1;
int map_id = -1;
- if (strstr(command, "map") != NULL) {
+ if (stristr(info->command, "map") != NULL) {
if (sscanf(message, "%15s %23s", map_name, player_name) < 1 || (map_id = map->mapname2mapid(map_name)) < 0)
map_id = sd->bl.m;
} else {
sscanf(message, "%23s", player_name);
}
- if (strstr(command, "2") != NULL)
+ if (stristr(info->command, "2") != NULL)
display_type = 2;
- else if (strstr(command, "3") != NULL)
+ else if (stristr(info->command, "3") != NULL)
display_type = 3;
level = pc->get_group_level(sd);
@@ -1002,17 +1002,17 @@ ACMD(kami)
memset(atcmd_output, '\0', sizeof(atcmd_output));
- if(*(command + 5) != 'c' && *(command + 5) != 'C') {
+ if(*(info->command + 4) != 'c' && *(info->command + 4) != 'C') {
if (!message || !*message) {
clif->message(fd, msg_txt(980)); // Please enter a message (usage: @kami <message>).
return false;
}
sscanf(message, "%199[^\n]", atcmd_output);
- if (strstr(command, "l") != NULL)
+ if (stristr(info->command, "l") != NULL)
clif->broadcast(&sd->bl, atcmd_output, strlen(atcmd_output) + 1, BC_DEFAULT, ALL_SAMEMAP);
else
- intif->broadcast(atcmd_output, strlen(atcmd_output) + 1, (*(command + 5) == 'b' || *(command + 5) == 'B') ? BC_BLUE : BC_YELLOW);
+ intif->broadcast(atcmd_output, strlen(atcmd_output) + 1, (*(info->command + 4) == 'b' || *(info->command + 4) == 'B') ? BC_BLUE : BC_YELLOW);
} else {
if(!message || !*message || (sscanf(message, "%lx %199[^\n]", &color, atcmd_output) < 2)) {
clif->message(fd, msg_txt(981)); // Please enter color and message (usage: @kamic <color> <message>).
@@ -1099,7 +1099,7 @@ ACMD(item)
memset(item_name, '\0', sizeof(item_name));
- if (!strcmpi(command+1,"itembound") && (!message || !*message || (
+ if (!strcmpi(info->command,"itembound") && (!message || !*message || (
sscanf(message, "\"%99[^\"]\" %d %d", item_name, &number, &bound) < 2 &&
sscanf(message, "%99s %d %d", item_name, &number, &bound) < 2
))) {
@@ -1123,7 +1123,7 @@ ACMD(item)
return false;
}
- if(!strcmpi(command+1,"itembound") && !(bound >= IBT_MIN && bound <= IBT_MAX) ) {
+ if(!strcmpi(info->command,"itembound") && !(bound >= IBT_MIN && bound <= IBT_MAX) ) {
clif->message(fd, msg_txt(298)); // Invalid bound type
return false;
}
@@ -1171,7 +1171,7 @@ ACMD(item2)
memset(item_name, '\0', sizeof(item_name));
- if (!strcmpi(command+1,"itembound2") && (!message || !*message || (
+ if (!strcmpi(info->command,"itembound2") && (!message || !*message || (
sscanf(message, "\"%99[^\"]\" %d %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4, &bound) < 10 &&
sscanf(message, "%99s %d %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4, &bound) < 10 ))) {
clif->message(fd, msg_txt(296)); // Please enter all parameters (usage: @itembound2 <item name/ID> <quantity>
@@ -1189,7 +1189,7 @@ ACMD(item2)
if (number <= 0)
number = 1;
- if( !strcmpi(command+1,"itembound2") && !(bound >= IBT_MIN && bound <= IBT_MAX) ) {
+ if( !strcmpi(info->command,"itembound2") && !(bound >= IBT_MIN && bound <= IBT_MAX) ) {
clif->message(fd, msg_txt(298)); // Invalid bound type
return false;
}
@@ -1204,7 +1204,7 @@ ACMD(item2)
int loop, get_count, i;
loop = 1;
get_count = number;
- if( !strcmpi(command+1,"itembound2") )
+ if( !strcmpi(info->command,"itembound2") )
bound = 1;
if( !itemdb->isstackable2(item_data) ) {
if( bound && (item_data->type == IT_PETEGG || item_data->type == IT_PETARMOR) ) {
@@ -1930,9 +1930,9 @@ ACMD(monster)
if (battle_config.atc_spawn_quantity_limit && number > battle_config.atc_spawn_quantity_limit)
number = battle_config.atc_spawn_quantity_limit;
- if (strcmp(command+1, "monstersmall") == 0)
+ if (strcmpi(info->command, "monstersmall") == 0)
size = SZ_MEDIUM; // This is just gorgeous [mkbu95]
- else if (strcmp(command+1, "monsterbig") == 0)
+ else if (strcmpi(info->command, "monsterbig") == 0)
size = SZ_BIG;
else
size = SZ_SMALL;
@@ -1997,7 +1997,7 @@ ACMD(killmonster) {
map_id = sd->bl.m;
}
- drop_flag = strcmp(command+1, "killmonster2");
+ drop_flag = strcmpi(info->command, "killmonster2");
map->foreachinmap(atcommand->atkillmonster_sub, map_id, BL_MOB, -drop_flag);
@@ -2358,7 +2358,7 @@ ACMD(param) {
return false;
}
- ARR_FIND( 0, ARRAYLENGTH(param), i, strcmpi(command+1, param[i]) == 0 );
+ ARR_FIND( 0, ARRAYLENGTH(param), i, strcmpi(info->command, param[i]) == 0 );
if( i == ARRAYLENGTH(param) || i > MAX_STATUS_TYPE) { // normally impossible...
clif->message(fd, msg_txt(1013)); // Please enter a valid value (usage: @str/@agi/@vit/@int/@dex/@luk <+/-adjustment>).
@@ -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 <party_name>).
+ safesnprintf(msg, sizeof(msg), msg_txt(1028), max_spiritballs); // Please enter an amount (usage: @spiritball <number: 0-%d>).
clif->message(fd, msg);
return false;
}
@@ -6070,7 +6070,7 @@ ACMD(npctalk)
{
char name[NAME_LENGTH],mes[100],temp[100];
struct npc_data *nd;
- bool ifcolor=(*(command + 8) != 'c' && *(command + 8) != 'C')?0:1;
+ bool ifcolor=(*(info->command + 7) != 'c' && *(info->command + 7) != 'C')?0:1;
unsigned long color=0;
if (sd->sc.count && //no "chatting" while muted.
@@ -7754,7 +7754,7 @@ ACMD(cash)
return false;
}
- if( !strcmpi(command+1,"cash") ) {
+ if( !strcmpi(info->command,"cash") ) {
if( value > 0 ) {
if( (ret=pc->getcash(sd, value, 0)) >= 0){
// If this option is set, the message is already sent by pc function
@@ -7813,9 +7813,9 @@ ACMD(clone) {
return true;
}
- if (strcmpi(command+1, "clone") == 0)
+ if (strcmpi(info->command, "clone") == 0)
flag = 1;
- else if (strcmpi(command+1, "slaveclone") == 0) {
+ else if (strcmpi(info->command, "slaveclone") == 0) {
flag = 2;
if(pc_isdead(sd)){
clif->message(fd, msg_txt(129+flag*2));
@@ -7983,15 +7983,15 @@ ACMD(itemlist)
int size;
StringBuf buf;
- if( strcmp(command+1, "storagelist") == 0 ) {
+ if( strcmpi(info->command, "storagelist") == 0 ) {
location = "storage";
items = sd->status.storage.items;
size = MAX_STORAGE;
- } else if( strcmp(command+1, "cartlist") == 0 ) {
+ } else if( strcmpi(info->command, "cartlist") == 0 ) {
location = "cart";
items = sd->status.cart;
size = MAX_CART;
- } else if( strcmp(command+1, "itemlist") == 0 ) {
+ } else if( strcmpi(info->command, "itemlist") == 0 ) {
location = "inventory";
items = sd->status.inventory;
size = MAX_INVENTORY;
@@ -8512,7 +8512,7 @@ ACMD(reloadquestdb) {
}
ACMD(addperm) {
int perm_size = pcg->permission_count;
- bool add = (strcmpi(command+1, "addperm") == 0) ? true : false;
+ bool add = (strcmpi(info->command, "addperm") == 0) ? true : false;
int i;
if( !message || !*message ) {
@@ -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 <accid>.l <name>.24B <type>.w { <year>.w <month>.w <day>.w <hour>.w <minute>.w <second>.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 <accid>.l <name>.24B <type>.w <answer>.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(0<ret_status && ret_status<=9)
clif->message(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(&timestamp));
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(&timestamp));
+ 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 <packet len>.W <message>.?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);