Skip to content

Commit

Permalink
Introducing Character Ban Support.
Browse files Browse the repository at this point in the history
@charban/@charunban, can temporarily block any accounts as opposed to the usual account-wide block.
Special Thanks to Haruna, Yommy!

Signed-off-by: shennetsind <ind@henn.et>
  • Loading branch information
shennetsind committed Nov 16, 2013
1 parent 45ef229 commit aee2f63
Show file tree
Hide file tree
Showing 14 changed files with 241 additions and 79 deletions.
3 changes: 2 additions & 1 deletion conf/messages.conf
Expand Up @@ -452,7 +452,8 @@
430: unblock

This comment has been minimized.

Copy link
@shennetsind

shennetsind Nov 16, 2013

Author Member

"@charban/@charunban, can temporarily block any accounts as opposed to the usual account-wide block."
fail! can temporarily block any characters*** as opposed to the usual account-wide block.

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

Expand Down
2 changes: 2 additions & 0 deletions sql-files/main.sql
Expand Up @@ -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`),
Expand Down Expand Up @@ -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`
Expand Down
3 changes: 3 additions & 0 deletions 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);
3 changes: 2 additions & 1 deletion sql-files/upgrades/index.txt
Expand Up @@ -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
2013-11-15--19-57.sql
2013-11-16--07-49.sql
215 changes: 159 additions & 56 deletions src/char/char.c
Expand Up @@ -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 ) {
Expand All @@ -1012,16 +1013,18 @@ 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 "
"`char_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`,"
"`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)
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}

Expand Down Expand Up @@ -1931,13 +1936,42 @@ 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));
WFIFOW(fd,0) = 0x99d;
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
{
Expand All @@ -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);
Expand All @@ -3160,79 +3197,136 @@ 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
//FIXME: need to move this check to login server [ultramage]
// 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;
Expand Down Expand Up @@ -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 */
Expand Down
1 change: 1 addition & 0 deletions src/char/char.h
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/common/showmsg.h
Expand Up @@ -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_ */
11 changes: 11 additions & 0 deletions src/common/utils.c
Expand Up @@ -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;
Expand Down

0 comments on commit aee2f63

Please sign in to comment.