Browse Source

Fixed character rename feature (#1943)

This feature was broken for clients after 2011-11-01.
Added 2 new options for allowing it when the character is inside a party or guild.
The server now delivers the corresponding error messages if the player is inside a party or guild.
Lemongrass3110 8 years ago
parent
commit
7cf081c24b
5 changed files with 130 additions and 43 deletions
  1. 9 0
      conf/char_athena.conf
  2. 24 5
      src/char/char.c
  3. 2 0
      src/char/char.h
  4. 94 37
      src/char/char_clif.c
  5. 1 1
      src/char/char_clif.h

+ 9 - 0
conf/char_athena.conf

@@ -238,6 +238,15 @@ char_movetoused: yes
 // Allow users to move characters as often as they like?
 // Allow users to move characters as often as they like?
 char_moves_unlimited: no
 char_moves_unlimited: no
 
 
+// Character renaming
+// Allow users to rename a character while being in a party?
+// Default: no
+char_rename_party: no
+
+// Allow users to rename a character while being in a guild?
+// Default: no
+char_rename_guild: no
+
 // Should we check if sql-tables are correct on server startup ?
 // Should we check if sql-tables are correct on server startup ?
 char_checkdb: yes
 char_checkdb: yes
 
 

+ 24 - 5
src/char/char.c

@@ -1247,16 +1247,31 @@ int char_rename_char_sql(struct char_session_data *sd, uint32 char_id)
 	if( !char_mmo_char_fromsql(char_id, &char_dat, false) ) // Only the short data is needed.
 	if( !char_mmo_char_fromsql(char_id, &char_dat, false) ) // Only the short data is needed.
 		return 2;
 		return 2;
 
 
+	// If the new name is exactly the same as the old one
+	if( !strcmp( sd->new_name, char_dat.name ) )
+		return 0;
+
 	if( char_dat.rename == 0 )
 	if( char_dat.rename == 0 )
 		return 1;
 		return 1;
 
 
+	if( !charserv_config.char_config.char_rename_party && char_dat.party_id ){
+		return 6;
+	}
+
+	if( !charserv_config.char_config.char_rename_guild && char_dat.guild_id ){
+		return 5;
+	}
+
 	Sql_EscapeStringLen(sql_handle, esc_name, sd->new_name, strnlen(sd->new_name, NAME_LENGTH));
 	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' LIMIT 1", schema_config.char_db, esc_name) )
-	{
-		Sql_ShowDebug(sql_handle);
-		return 4;
+	switch( char_check_char_name( sd->new_name, esc_name ) ){
+		case 0:
+			break;
+		case -1:
+			// character already exists
+			return 4;
+		default:
+			return 8;
 	}
 	}
 
 
 	if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `name` = '%s', `rename` = '%d' WHERE `char_id` = '%d'", schema_config.char_db, esc_name, --char_dat.rename, char_id) )
 	if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `name` = '%s', `rename` = '%d' WHERE `char_id` = '%d'", schema_config.char_db, esc_name, --char_dat.rename, char_id) )
@@ -2879,6 +2894,10 @@ bool char_config_read(const char* cfgName, bool normal){
 			charserv_config.char_config.char_del_option = atoi(w2);
 			charserv_config.char_config.char_del_option = atoi(w2);
 		} else if (strcmpi(w1, "char_del_restriction") == 0) {
 		} else if (strcmpi(w1, "char_del_restriction") == 0) {
 			charserv_config.char_config.char_del_restriction = atoi(w2);
 			charserv_config.char_config.char_del_restriction = atoi(w2);
+		} else if (strcmpi(w1, "char_rename_party") == 0) {
+			charserv_config.char_config.char_rename_party = (bool)config_switch(w2);
+		} else if (strcmpi(w1, "char_rename_guild") == 0) {
+			charserv_config.char_config.char_rename_guild = (bool)config_switch(w2);
 		} else if(strcmpi(w1,"db_path")==0) {
 		} else if(strcmpi(w1,"db_path")==0) {
 			safestrncpy(schema_config.db_path, w2, sizeof(schema_config.db_path));
 			safestrncpy(schema_config.db_path, w2, sizeof(schema_config.db_path));
 		} else if (strcmpi(w1, "fame_list_alchemist") == 0) {
 		} else if (strcmpi(w1, "fame_list_alchemist") == 0) {

+ 2 - 0
src/char/char.h

@@ -119,6 +119,8 @@ struct Char_Config {
 	int char_name_option; // Option to know which letters/symbols are authorised in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor]
 	int char_name_option; // Option to know which letters/symbols are authorised in the name of a character (0: all, 1: only those in char_name_letters, 2: all EXCEPT those in char_name_letters) by [Yor]
 	int char_del_option;	// Character deletion type, email = 1, birthdate = 2 (default)
 	int char_del_option;	// Character deletion type, email = 1, birthdate = 2 (default)
 	int char_del_restriction;	// Character deletion restriction (0: none, 1: if the character is in a party, 2: if the character is in a guild, 3: if the character is in a party or a guild)
 	int char_del_restriction;	// Character deletion restriction (0: none, 1: if the character is in a party, 2: if the character is in a guild, 3: if the character is in a party or a guild)
+	bool char_rename_party;	// Character renaming in a party
+	bool char_rename_guild;	// Character renaming in a guild
 };
 };
 
 
 #define TRIM_CHARS "\255\xA0\032\t\x0A\x0D " //The following characters are trimmed regardless because they cause confusion and problems on the servers. [Skotlex]
 #define TRIM_CHARS "\255\xA0\032\t\x0A\x0D " //The following characters are trimmed regardless because they cause confusion and problems on the servers. [Skotlex]

+ 94 - 37
src/char/char_clif.c

@@ -1055,29 +1055,33 @@ int chclif_parse_keepalive(int fd){
 	return 1;
 	return 1;
 }
 }
 
 
-// R 08fc <char ID>.l <new name>.24B
-// R 028d <account ID>.l <char ID>.l <new name>.24B
-int chclif_parse_reqrename(int fd, struct char_session_data* sd, int cmd){
-	int i, cid = 0;
+// Tells the client if the name was accepted or not
+// 028e <result>.W (HC_ACK_IS_VALID_CHARNAME)
+// result:
+//		0 = name is not OK
+//		1 = name is OK
+void chclif_reqrename_response( int fd, struct char_session_data* sd, bool name_valid ){
+	WFIFOHEAD(fd, 4);
+	WFIFOW(fd, 0) = 0x28e;
+	WFIFOW(fd, 2) = name_valid;
+	WFIFOSET(fd, 4);
+}
+
+// Request for checking the new name on character renaming
+// 028d <account ID>.l <char ID>.l <new name>.24B (CH_REQ_IS_VALID_CHARNAME)
+int chclif_parse_reqrename( int fd, struct char_session_data* sd ){
+	int i, cid, aid;
 	char name[NAME_LENGTH];
 	char name[NAME_LENGTH];
 	char esc_name[NAME_LENGTH*2+1];
 	char esc_name[NAME_LENGTH*2+1];
 
 
-	if(cmd == 0x8fc){
-		FIFOSD_CHECK(30)
-		cid =RFIFOL(fd,2);
-		safestrncpy(name, RFIFOCP(fd,6), NAME_LENGTH);
-		RFIFOSKIP(fd,30);
-	}
-	else if(cmd == 0x28d) {
-		int aid;
-		FIFOSD_CHECK(34);
-		aid = RFIFOL(fd,2);
-		cid =RFIFOL(fd,6);
-		safestrncpy(name, RFIFOCP(fd,10), NAME_LENGTH);
-		RFIFOSKIP(fd,34);
-		if( aid != sd->account_id )
-			return 1;
-	}
+	FIFOSD_CHECK(34);
+	aid = RFIFOL(fd,2);
+	cid = RFIFOL(fd,6);
+	safestrncpy(name, RFIFOCP(fd,10), NAME_LENGTH);
+	RFIFOSKIP(fd,34);
+
+	if( aid != sd->account_id )
+		return 1;
 
 
 	ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
 	ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
 	if( i == MAX_CHARS )
 	if( i == MAX_CHARS )
@@ -1090,12 +1094,10 @@ int chclif_parse_reqrename(int fd, struct char_session_data* sd, int cmd){
 		safestrncpy(sd->new_name, name, NAME_LENGTH);
 		safestrncpy(sd->new_name, name, NAME_LENGTH);
 	}
 	}
 	else
 	else
-			i = 0;
+		i = 0;
+
+	chclif_reqrename_response(fd, sd, i > 0);
 
 
-	WFIFOHEAD(fd, 4);
-	WFIFOW(fd,0) = 0x28e;
-	WFIFOW(fd,2) = i;
-	WFIFOSET(fd,4);
 	return 1;
 	return 1;
 }
 }
 
 
@@ -1158,13 +1160,70 @@ void chclif_block_character( int fd, struct char_session_data* sd){
 	}
 	}
 }
 }
 
 
-// 0x28f <char_id>.L
+// Sends the response to a rename request to the client.
+// 0290 <result>.W (HC_ACK_CHANGE_CHARNAME)
+// 08fd <result>.L (HC_ACK_CHANGE_CHARACTERNAME)
+// result:
+//		0: Successful
+//		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.
+//		5: In order to change the character name, you must leave the guild.
+//		6: In order to change the character name, you must leave the party.
+//		7: Length exceeds the maximum size of the character name you want to change.
+//		8: Name contains invalid characters. Character name change failed.
+//		9: The name change is prohibited. Character name change failed.
+//		10: Character name change failed, due an unknown error.
+void chclif_rename_response(int fd, struct char_session_data* sd, int16 response) {
+#if PACKETVER >= 20111101
+	WFIFOHEAD(fd, 6);
+	WFIFOW(fd, 0) = 0x8fd;
+	WFIFOL(fd, 2) = response;
+	WFIFOSET(fd, 6);
+#else
+	WFIFOHEAD(fd, 4);
+	WFIFOW(fd, 0) = 0x290;
+	WFIFOW(fd, 2) = response;
+	WFIFOSET(fd, 4);
+#endif
+}
+
+// Request to change a character name
+// 028f <char_id>.L (CH_REQ_CHANGE_CHARNAME)
+// 08fc <char_id>.L <new name>.24B (CH_REQ_CHANGE_CHARACTERNAME)
 int chclif_parse_ackrename(int fd, struct char_session_data* sd){
 int chclif_parse_ackrename(int fd, struct char_session_data* sd){
-	// 0: Successful
-	// 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.
+#if PACKETVER >= 20111101
+	FIFOSD_CHECK(30)
+	{
+		int i, cid;
+		char name[NAME_LENGTH], esc_name[NAME_LENGTH * 2 + 1];
+
+		cid = RFIFOL(fd, 2);
+		safestrncpy(name, RFIFOCP(fd, 6), NAME_LENGTH);
+		RFIFOSKIP(fd, 30);
+
+		ARR_FIND(0, MAX_CHARS, i, sd->found_char[i] == cid);
+		if (i == MAX_CHARS)
+			return 1;
+
+		normalize_name(name, TRIM_CHARS);
+		Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
+
+		safestrncpy(sd->new_name, name, NAME_LENGTH);
+
+		// Start the renaming process
+		i = char_rename_char_sql(sd, cid);
+
+		chclif_rename_response(fd, sd, i);
+
+		// If the renaming was successful, we need to resend the characters
+		if( i == 0 )
+			chclif_mmo_char_send(fd, sd);
+
+		return 1;
+	}
+#else
 	FIFOSD_CHECK(6)
 	FIFOSD_CHECK(6)
 	{
 	{
 		int i;
 		int i;
@@ -1176,12 +1235,10 @@ int chclif_parse_ackrename(int fd, struct char_session_data* sd){
 			return 1;
 			return 1;
 		i = char_rename_char_sql(sd, cid);
 		i = char_rename_char_sql(sd, cid);
 
 
-		WFIFOHEAD(fd, 4);
-		WFIFOW(fd,0) = 0x290;
-		WFIFOW(fd,2) = i;
-		WFIFOSET(fd,4);
+		chclif_rename_response(fd, sd, i);
 	}
 	}
 	return 1;
 	return 1;
+#endif
 }
 }
 
 
 int chclif_ack_captcha(int fd){
 int chclif_ack_captcha(int fd){
@@ -1262,9 +1319,9 @@ int chclif_parse(int fd) {
 			// client keep-alive packet (every 12 seconds)
 			// client keep-alive packet (every 12 seconds)
 			case 0x187: next=chclif_parse_keepalive(fd); break;
 			case 0x187: next=chclif_parse_keepalive(fd); break;
 			// char rename
 			// char rename
-			case 0x8fc: next=chclif_parse_reqrename(fd,sd,cmd); break; //request <date/version?>
-			case 0x28d: next=chclif_parse_reqrename(fd,sd,cmd); break; //request <date/version?>
+			case 0x28d: next=chclif_parse_reqrename(fd,sd); break; //Check new desired name
 			case 0x28f: next=chclif_parse_ackrename(fd,sd); break; //Confirm change name.
 			case 0x28f: next=chclif_parse_ackrename(fd,sd); break; //Confirm change name.
+			case 0x8fc: next=chclif_parse_ackrename(fd,sd); break; //Rename request without confirmation
 			// captcha
 			// captcha
 			case 0x7e5: next=chclif_parse_reqcaptcha(fd); break; // captcha code request (not implemented)
 			case 0x7e5: next=chclif_parse_reqcaptcha(fd); break; // captcha code request (not implemented)
 			case 0x7e7: next=chclif_parse_chkcaptcha(fd); break; // captcha code check (not implemented)
 			case 0x7e7: next=chclif_parse_chkcaptcha(fd); break; // captcha code check (not implemented)

+ 1 - 1
src/char/char_clif.h

@@ -48,7 +48,7 @@ int chclif_parse_charselect(int fd, struct char_session_data* sd,uint32 ipl);
 int chclif_parse_createnewchar(int fd, struct char_session_data* sd,int cmd);
 int chclif_parse_createnewchar(int fd, struct char_session_data* sd,int cmd);
 int chclif_parse_delchar(int fd,struct char_session_data* sd, int cmd);
 int chclif_parse_delchar(int fd,struct char_session_data* sd, int cmd);
 int chclif_parse_keepalive(int fd);
 int chclif_parse_keepalive(int fd);
-int chclif_parse_reqrename(int fd, struct char_session_data* sd, int cmd);
+int chclif_parse_reqrename(int fd, struct char_session_data* sd);
 int chclif_parse_ackrename(int fd, struct char_session_data* sd);
 int chclif_parse_ackrename(int fd, struct char_session_data* sd);
 int chclif_ack_captcha(int fd);
 int chclif_ack_captcha(int fd);
 int chclif_parse_reqcaptcha(int fd);
 int chclif_parse_reqcaptcha(int fd);