Browse Source

Added character rename function.(topic:225576)(written by pakpil and fixed by me)

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@14084 54d463be-8e91-2dee-dedb-b68131a5f0ec
Inkfish 15 năm trước cách đây
mục cha
commit
a2b652aa87
6 tập tin đã thay đổi với 216 bổ sung28 xóa
  1. 7 0
      Changelog-Trunk.txt
  2. 9 3
      conf/char_athena.conf
  3. 162 25
      src/char_sql/char.c
  4. 36 0
      src/char_sql/int_guild.c
  5. 1 0
      src/char_sql/int_guild.h
  6. 1 0
      src/common/mmo.h

+ 7 - 0
Changelog-Trunk.txt

@@ -3,8 +3,15 @@ Date	Added
 AS OF SVN REV. 5091, WE ARE NOW USING TRUNK.  ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK.
 IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
 
+09/10/10
+	* Added character rename function. [Inkfish]
 09/10/06
 	* Fixed rental items can be moved into guild storage from cart. [Inkfish]
+09/10/04
+	* Implemented new packets for rental items (from Jobbie and Natz and fixed by me). [zephyrus]
+	* Rental items now can be moved to storage and cart. [zephyrus]
+	* Autotrade cannot be used when user is dead, and it's not dispelled if user dies to prevent abuse. [zephyrus]
+	* Fixed pc_checkitem to do a proper item check and remove. [zephyrus]
 09/10/02
 	* Fixed a damage underflow.(bugreport:3624) [Inkfish]
 	* Marionette Control now has an infinite duration.(bugreport:2542) [Inkfish]

+ 9 - 3
conf/char_athena.conf

@@ -156,13 +156,19 @@ char_name_option: 1
 // Note: Don't add spaces unless you mean to add 'space' to the list.
 char_name_letters: abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
 
-// Character rename option. When set to yes, the server will send an extended 
-// char-info packet, informing whether the character can be renamed or not.
-// NOTE: This functionality is not implemented.
+// Character rename option. 
+// When set to yes, the server will send an extended char-info packet, 
+// informing whether the character can be renamed or not.
 // NOTE: This option is for compatibility with kRO sakray 2006-10-23 and newer.
 //       !Do not use it for any other type of client since it will crash them!
 char_rename: yes
 
+// Specifies the maximun number of times you can change
+// the char name, applied individually to each character. 
+// It's necessary that "char_rename" be set to yes. 
+// 0 = unlimited
+char_max_rename: 1
+
 // How many Characters are allowed per Account ? (0 = disabled) [SQL Only!]
 chars_per_account: 0
 

+ 162 - 25
src/char_sql/char.c

@@ -100,6 +100,7 @@ char unknown_char_name[NAME_LENGTH] = "Unknown"; // Name to use when the request
 #define TRIM_CHARS "\032\t\x0A\x0D " //The following characters are trimmed regardless because they cause confusion and problems on the servers. [Skotlex]
 char char_name_letters[1024] = ""; // list of letters/symbols used to authorise or not a name of a character. by [Yor]
 bool char_rename = true;
+int char_max_rename = 1;
 
 int char_per_account = 0; //Maximum charas per account (default unlimited) [Sirius]
 int char_del_level = 0; //From which level u can delete character [Lupus]
@@ -130,6 +131,7 @@ struct char_session_data {
 	int gmlevel;
 	uint32 version;
 	uint8 clienttype;
+	char new_name[NAME_LENGTH];
 };
 
 int char_num, char_max;
@@ -477,7 +479,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p)
 			"`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d',"
 			"`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d',"
 			"`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
-			"`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d'"
+			"`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d', `rename`='%d'"
 			" WHERE  `account_id`='%d' AND `char_id` = '%d'",
 			char_db, p->base_level, p->job_level,
 			p->base_exp, p->job_exp, p->zeny,
@@ -486,7 +488,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p)
 			p->option, p->party_id, p->guild_id, p->pet_id, p->hom_id,
 			p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom,
 			mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y,
-			mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y,
+			mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, p->rename,
 			p->account_id, p->char_id) )
 		{
 			Sql_ShowDebug(sql_handle);
@@ -835,7 +837,7 @@ int mmo_chars_fromsql(struct char_session_data* sd, uint8* buf)
 		"`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`"
+		"`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`rename`"
 		" FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", char_db, sd->account_id, MAX_CHARS)
 	||	SQL_ERROR == SqlStmt_Execute(stmt)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 0,  SQLDT_INT,    &p.char_id, 0, NULL, NULL)
@@ -870,6 +872,7 @@ int mmo_chars_fromsql(struct char_session_data* sd, uint8* buf)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 29, SQLDT_SHORT,  &p.head_top, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 30, SQLDT_SHORT,  &p.head_mid, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 31, SQLDT_SHORT,  &p.head_bottom, 0, NULL, NULL)
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 32, SQLDT_SHORT,	&p.rename, 0, NULL, NULL)
 	)
 	{
 		SqlStmt_ShowDebug(stmt);
@@ -884,6 +887,8 @@ int mmo_chars_fromsql(struct char_session_data* sd, uint8* buf)
 	for( ; i < MAX_CHARS; i++ )
 		sd->found_char[i] = -1;
 
+	memset(sd->new_name,0,sizeof(sd->new_name));
+
 	SqlStmt_Free(stmt);
 	return j;
 }
@@ -925,7 +930,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
 		"`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`,"
 		"`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`hair`,"
 		"`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`,"
-		"`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`"
+		"`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`"
 		" FROM `%s` WHERE `char_id`=? LIMIT 1", char_db)
 	||	SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
 	||	SQL_ERROR == SqlStmt_Execute(stmt)
@@ -976,7 +981,9 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 44, SQLDT_INT,    &p->father, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 45, SQLDT_INT,    &p->mother, 0, NULL, NULL)
 	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 46, SQLDT_INT,    &p->child, 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 47, SQLDT_INT,    &p->fame, 0, NULL, NULL) )
+	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 47, SQLDT_INT,    &p->fame, 0, NULL, NULL)
+	||  SQL_ERROR == SqlStmt_BindColumn(stmt, 48, SQLDT_SHORT,	&p->rename, 0, NULL, NULL)
+	)
 	{
 		SqlStmt_ShowDebug(stmt);
 		SqlStmt_Free(stmt);
@@ -1173,18 +1180,73 @@ int mmo_char_sql_init(void)
 }
 
 //-----------------------------------
-// Function to create a new character
+// Function to change chararcter's names
 //-----------------------------------
-int make_new_char_sql(struct char_session_data* sd, char* name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style)
+int rename_char_sql(struct char_session_data *sd, int char_id)
 {
-	char name[NAME_LENGTH];
+	StringBuf buf;
+	struct mmo_charstatus char_dat;
 	char esc_name[NAME_LENGTH*2+1];
-	unsigned int i;
-	int char_id;
 
-	safestrncpy(name, name_, NAME_LENGTH);
-	normalize_name(name,TRIM_CHARS);
-	Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
+	if( sd->new_name[0] == 0 ) // Not ready for rename
+		return 2;
+	
+	if( !mmo_char_fromsql(char_id, &char_dat, false) ) // Only the short data is needed.
+		return 2;
+
+	if( char_dat.rename >= char_max_rename && char_max_rename != 0 )
+		return 1;
+
+	Sql_EscapeStringLen(sql_handle, esc_name, sd->new_name, strnlen(sd->new_name, NAME_LENGTH));
+
+	// check if the char exist
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `name` LIKE '%s'", char_db, esc_name) ) {
+		Sql_ShowDebug(sql_handle);
+		return 4;
+	}
+
+	StringBuf_Init(&buf);
+	StringBuf_Printf(&buf, "UPDATE `%s` SET `name` = '%s'", char_db, esc_name);
+	
+	if( char_max_rename )
+	{
+		char_dat.rename++;
+		StringBuf_Printf(&buf, ", `rename` = '%d'", char_dat.rename);
+	}
+
+	StringBuf_Printf(&buf, " WHERE `char_id` = '%d'", char_id);
+
+	if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
+	{
+		Sql_ShowDebug(sql_handle);
+		StringBuf_Destroy(&buf);
+		return 3;
+	}
+
+	StringBuf_Destroy(&buf);
+
+	// Change character's name into guild_db.
+	if( char_dat.guild_id )
+		inter_guild_charname_changed(char_dat.guild_id, sd->account_id, char_id, sd->new_name);
+
+	safestrncpy(char_dat.name, sd->new_name, NAME_LENGTH);
+	memset(sd->new_name,0,sizeof(sd->new_name));
+
+	// log change
+	if( log_char )
+	{
+		if( SQL_ERROR == Sql_Query(sql_handle, "INSERT INTO `%s` (`time`, `char_msg`,`account_id`,`char_num`,`name`,`str`,`agi`,`vit`,`int`,`dex`,`luk`,`hair`,`hair_color`)"
+			"VALUES (NOW(), '%s', '%d', '%d', '%s', '0', '0', '0', '0', '0', '0', '0', '0')",
+			charlog_db, "change char name", sd->account_id, char_dat.slot, esc_name) )
+			Sql_ShowDebug(sql_handle);
+	}
+
+	return 0;
+}
+
+int check_char_name(char * name, char * esc_name)
+{
+	int i;
 
 	// check length of character name
 	if( name[0] == '\0' )
@@ -1197,27 +1259,49 @@ int make_new_char_sql(struct char_session_data* sd, char* name_, int str, int ag
 	// check for reserved names
 	if( strcmpi(name, main_chat_nick) == 0 || strcmpi(name, wisp_server_name) == 0 )
 		return -1; // nick reserved for internal server messages
-
+	
 	// Check Authorised letters/symbols in the name of the character
-	if( char_name_option == 1 ) { // only letters/symbols in char_name_letters are authorised
+	if( char_name_option == 1 )
+	{ // only letters/symbols in char_name_letters are authorised
 		for( i = 0; i < NAME_LENGTH && name[i]; i++ )
 			if( strchr(char_name_letters, name[i]) == NULL )
 				return -2;
-	} else
-	if( char_name_option == 2 ) { // letters/symbols in char_name_letters are forbidden
+	}
+	else if( char_name_option == 2 )
+	{ // letters/symbols in char_name_letters are forbidden
 		for( i = 0; i < NAME_LENGTH && name[i]; i++ )
 			if( strchr(char_name_letters, name[i]) != NULL )
 				return -2;
-	} // else, all letters/symbols are authorised (except control char removed before)
+	}
 
-	// check name (already in use?)
-	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `name` = '%s'", char_db, esc_name) ) {
+	if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `name` = '%s'", char_db, esc_name) )
+	{ // check name (already in use?)
 		Sql_ShowDebug(sql_handle);
 		return -2;
 	}
 	if( Sql_NumRows(sql_handle) > 0 )
 		return -1; //  name already exists
 
+	return 0;
+}
+
+//-----------------------------------
+// Function to create a new character
+//-----------------------------------
+int make_new_char_sql(struct char_session_data* sd, char* name_, int str, int agi, int vit, int int_, int dex, int luk, int slot, int hair_color, int hair_style)
+{
+	char name[NAME_LENGTH];
+	char esc_name[NAME_LENGTH*2+1];
+	int char_id, flag;
+
+	safestrncpy(name, name_, NAME_LENGTH);
+	normalize_name(name,TRIM_CHARS);
+	Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
+
+	flag = check_char_name(name,esc_name);
+	if( flag < 0 )
+		return flag;
+
 	//check other inputs
 	if((slot >= MAX_CHARS) // slots
 	|| (hair_style >= 24) // hair style
@@ -1512,7 +1596,7 @@ int mmo_char_tobuf(uint8* buf, struct mmo_charstatus* p)
 	WBUFB(buf,103) = min(p->luk, UCHAR_MAX);
 	WBUFW(buf,104) = p->slot;
 	if (char_rename) {
-		WBUFW(buf,106) = 1;// Rename bit (0=rename,1=no rename)
+		WBUFW(buf,106) = (p->rename >= char_max_rename && char_max_rename != 0) ? 1 : 0;// Rename bit (0=rename,1=no rename)
 		return 108;
 	} else {
 		return 106;
@@ -3142,7 +3226,7 @@ int parse_char(int fd)
 				// 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[ch] = i; // the char_id of the new char
 			}
 
 			RFIFOSKIP(fd,37);
@@ -3220,9 +3304,60 @@ int parse_char(int fd)
 		// R 028d <account ID>.l <char ID>.l <new name>.24B 
 		case 0x28d:
 			FIFOSD_CHECK(34);
-			//not implemented
-			RFIFOSKIP(fd,34);
-		break;
+			{
+				int i, aid = RFIFOL(fd,2), cid =RFIFOL(fd,6);
+				char name[NAME_LENGTH];
+ 				char esc_name[NAME_LENGTH*2+1];
+				safestrncpy(name, (char *)RFIFOP(fd,10), NAME_LENGTH);
+				RFIFOSKIP(fd,34);
+
+				if( aid != sd->account_id )
+					break;
+				ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
+				if( i == MAX_CHARS )
+					break;
+
+				normalize_name(name,TRIM_CHARS);
+				Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
+				if( !check_char_name(name,esc_name) )
+				{
+					i = 1;
+					safestrncpy(sd->new_name, name, NAME_LENGTH);
+				}
+				else 
+					i = 0;
+
+				WFIFOHEAD(fd, 4);
+				WFIFOW(fd,0) = 0x28e;
+				WFIFOW(fd,2) = i;
+				WFIFOSET(fd,4);
+			}
+			break;
+		//Confirm change name.
+		// 0x28f <char_id>.L
+		case 0x28f:
+			// 0: Sucessfull
+			// 1: This character's name has already been changed. You cannot change a character's name more than once.
+			// 2: User information is not correct.
+			// 3: You have failed to change this character's name.
+			// 4: Another user is using this character name, so please select another one.
+			FIFOSD_CHECK(6);
+			{
+				int i;
+				int cid = RFIFOL(fd,2);
+				RFIFOSKIP(fd,6);
+
+				ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
+				if( i == MAX_CHARS )
+					break;
+				i = rename_char_sql(sd, cid);
+
+				WFIFOHEAD(fd, 4);
+				WFIFOW(fd,0) = 0x290;
+				WFIFOW(fd,2) = i;
+				WFIFOSET(fd,4);
+			}
+			break;
 
 		// log in as map-server
 		case 0x2af8:
@@ -3772,6 +3907,8 @@ int char_config_read(const char* cfgName)
 			strcpy(char_name_letters, w2);
 		} else if (strcmpi(w1, "char_rename") == 0) {
 			char_rename = config_switch(w2);
+		} else if (strcmpi(w1, "char_max_rename") == 0) {
+			char_max_rename = atoi(w2);
 		} else if (strcmpi(w1, "chars_per_account") == 0) { //maxchars per account [Sirius]
 			char_per_account = atoi(w2);
 		} else if (strcmpi(w1, "char_del_level") == 0) { //disable/enable char deletion by its level condition [Lupus]

+ 36 - 0
src/char_sql/int_guild.c

@@ -1673,6 +1673,42 @@ int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender)
 	return mapif_parse_GuildMemberInfoChange(0, guild_id, account_id, char_id, GMI_GENDER, (const char*)&gender, sizeof(gender));
 }
 
+int inter_guild_charname_changed(int guild_id,int account_id, int char_id, char *name)
+{
+	struct guild *g;
+	int i, flag = 0;
+
+	g = inter_guild_fromsql(guild_id);
+	if( g == NULL )
+	{
+		ShowError("inter_guild_charrenamed: Can't find guild %d.\n", guild_id);
+		return 0;
+	}
+
+	ARR_FIND(0, g->max_member, i, g->member[i].char_id == char_id);
+	if( i == g->max_member )
+	{
+		ShowError("inter_guild_charrenamed: Can't find character %d in the guild\n", char_id);
+		return 0;
+	}
+
+	if( !strcmp(g->member[i].name, g->master) )
+	{
+		safestrncpy(g->master, name, NAME_LENGTH);
+		flag |= GS_BASIC;
+	}
+	safestrncpy(g->member[i].name, name, NAME_LENGTH);
+	g->member[i].modified = GS_MEMBER_MODIFIED;
+	flag |= GS_MEMBER;
+
+	if( !inter_guild_tosql(g, flag) )
+		return 0;			
+
+	mapif_guild_info(-1,g);
+	
+	return 0;
+}
+
 // Change a position desc
 int mapif_parse_GuildPosition(int fd,int guild_id,int idx,struct guild_position *p)
 {

+ 1 - 0
src/char_sql/int_guild.h

@@ -29,6 +29,7 @@ int inter_guild_leave(int guild_id,int account_id,int char_id);
 int mapif_parse_BreakGuild(int fd,int guild_id);
 int inter_guild_broken(int guild_id);
 int inter_guild_sex_changed(int guild_id,int account_id,int char_id, int gender);
+int inter_guild_charname_changed(int guild_id,int account_id, int char_id, char *name);
 int inter_guild_CharOnline(int char_id, int guild_id);
 int inter_guild_CharOffline(int char_id, int guild_id);
 

+ 1 - 0
src/common/mmo.h

@@ -296,6 +296,7 @@ struct mmo_charstatus {
 	struct hotkey hotkeys[MAX_HOTKEYS];
 #endif
 	bool show_equip;
+	short rename;
 };
 
 typedef enum mail_status {