Added support for map selection (#7546)

Fixes #6632

Thanks to @Xypr0, @Toshiro90, @alisonrag and @aleos89
This commit is contained in:
Lemongrass3110
2023-01-11 22:42:54 +01:00
committed by GitHub
parent 3464292a31
commit 5044776eb7
2 changed files with 195 additions and 5 deletions

View File

@@ -30,6 +30,10 @@
using namespace rathena;
// Reuseable global packet buffer to prevent too many allocations
// Take socket.cpp::socket_max_client_packet into consideration
static int8 packet_buffer[UINT16_MAX];
std::vector<struct s_point_str> accessible_maps{
s_point_str{ MAP_PRONTERA, 273, 354 },
s_point_str{ MAP_GEFFEN, 120, 100 },
@@ -816,6 +820,165 @@ void chclif_send_map_data( int fd, std::shared_ptr<struct mmo_charstatus> cd, ui
WFIFOSET(fd,size);
}
int chclif_parse_select_accessible_map( int fd, struct char_session_data* sd, uint32 ipl ){
#if PACKETVER >= 20100714
struct PACKET_CH_SELECT_ACCESSIBLE_MAPNAME p;
FIFOSD_CHECK( sizeof( p ) );
memcpy( &p, RFIFOP( fd, 0 ), sizeof( p ) );
RFIFOSKIP( fd, sizeof( p ) );
char* data;
// Check if the character exists and is not scheduled for deletion
if( SQL_SUCCESS != Sql_Query( sql_handle, "SELECT `char_id` FROM `%s` WHERE `account_id`='%d' AND `char_num`='%d' AND `delete_date` = 0", schema_config.char_db, sd->account_id, p.slot )
|| SQL_SUCCESS != Sql_NextRow( sql_handle )
|| SQL_SUCCESS != Sql_GetData( sql_handle, 0, &data, nullptr ) ){
// Not found?? May be forged packet.
Sql_ShowDebug( sql_handle );
Sql_FreeResult( sql_handle );
chclif_reject( fd, 0 ); // rejected from server
return 1;
}
uint32 char_id = atoi( data );
Sql_FreeResult( sql_handle );
// Prevent select a char while retrieving guild bound items
if( sd->flag&1 ){
chclif_reject( fd, 0 ); // rejected from server
return 1;
}
/* client doesn't let it get to this point if you're banned, so its a forged packet */
if( sd->found_char[p.slot] == char_id && sd->unban_time[p.slot] > time(NULL) ) {
chclif_reject( fd, 0 ); // rejected from server
return 1;
}
/* set char as online prior to loading its data so 3rd party applications will realise the sql data is not reliable */
char_set_char_online( -2, char_id, sd->account_id );
struct mmo_charstatus char_dat;
if( !char_mmo_char_fromsql( char_id, &char_dat, true ) ) {
/* failed? set it back offline */
char_set_char_offline( char_id, sd->account_id );
/* failed to load something. REJECT! */
chclif_reject( fd, 0 ); /* jump off this boat */
return 1;
}
// Have to switch over to the DB instance otherwise data won't propagate [Kevin]
std::shared_ptr<struct mmo_charstatus> cd = util::umap_find( char_get_chardb(), char_id );
if( charserv_config.log_char ){
char esc_name[NAME_LENGTH*2+1];
Sql_EscapeStringLen( sql_handle, esc_name, char_dat.name, strnlen( char_dat.name, NAME_LENGTH ) );
if( SQL_ERROR == Sql_Query( sql_handle, "INSERT INTO `%s`(`time`, `account_id`,`char_num`,`name`) VALUES (NOW(), '%d', '%d', '%s')", schema_config.charlog_db, sd->account_id, p.slot, esc_name ) ){
Sql_ShowDebug( sql_handle );
}
}
ShowInfo( "Selected char: (Account %d: %d - %s)\n", sd->account_id, p.slot, char_dat.name );
// Check if there is really no mapserver for the last point where the player was
int32 mapserver = char_search_mapserver( cd->last_point.map, -1, -1 );
// It was not an unavailable map
if( mapserver >= 0 ){
chclif_reject( fd, 0 ); // rejected from server
return 1;
}
if( static_cast<size_t>( p.mapnumber ) >= accessible_maps.size() ){
chclif_reject( fd, 0 ); // rejected from server
return 1;
}
s_point_str& accessible_map = accessible_maps[p.mapnumber];
safestrncpy( cd->last_point.map, accessible_map.map, sizeof( cd->last_point.map ) );
cd->last_point.x = accessible_map.x;
cd->last_point.y = accessible_map.y;
mapserver = char_search_mapserver( cd->last_point.map, -1, -1 );
// No mapserver found for our accessible map
if( mapserver < 0 ){
chclif_reject( fd, 0 ); // rejected from server
return 1;
}
int map_fd;
// Send NEW auth packet [Kevin]
// FIXME: is this case even possible? [ultramage]
if( ( map_fd = map_server[mapserver].fd ) < 1 || session[map_fd] == nullptr ){
ShowError( "parse_char: Attempting to write to invalid session %d! Map Server #%d disconnected.\n", map_fd, mapserver );
map_server[mapserver].fd = -1;
memset( &map_server[mapserver], 0, sizeof( struct mmo_map_server ) );
chclif_send_auth_result( fd, 1 ); // Send server closed.
return 1;
}
chclif_send_map_data( fd, cd, ipl, mapserver );
// create temporary auth entry
std::shared_ptr<struct auth_node> node = std::make_shared<struct auth_node>();
node->account_id = sd->account_id;
node->char_id = cd->char_id;
node->login_id1 = sd->login_id1;
node->login_id2 = sd->login_id2;
node->sex = sd->sex;
node->expiration_time = sd->expiration_time;
node->group_id = sd->group_id;
node->ip = ipl;
char_get_authdb()[node->account_id] = node;
return 1;
#else
return 0;
#endif
}
void chclif_accessible_maps( int fd ){
#if PACKETVER >= 20100714
struct PACKET_HC_NOTIFY_ACCESSIBLE_MAPNAME* p = (struct PACKET_HC_NOTIFY_ACCESSIBLE_MAPNAME*)packet_buffer;
p->packetType = HEADER_HC_NOTIFY_ACCESSIBLE_MAPNAME;
p->packetLength = sizeof( *p );
int count = 0;
for( s_point_str& accessible_map : accessible_maps ){
int32 mapserver = char_search_mapserver( accessible_map.map, -1, -1 );
if( mapserver < 0 ){
p->maps[count].status = 1;
}else{
p->maps[count].status = 0;
}
mapindex_getmapname_ext( accessible_map.map, p->maps[count].map );
p->packetLength += sizeof( p->maps[0] );
count++;
}
WFIFOHEAD( fd, p->packetLength );
memcpy( WFIFOP( fd, 0 ), p, p->packetLength );
WFIFOSET( fd, p->packetLength );
#else
chclif_send_auth_result( fd, 1 ); // 01 = Server closed
#endif
}
int chclif_parse_charselect(int fd, struct char_session_data* sd,uint32 ipl){
FIFOSD_CHECK(3)
{
@@ -830,11 +993,7 @@ int chclif_parse_charselect(int fd, struct char_session_data* sd,uint32 ipl){
ARR_FIND( 0, ARRAYLENGTH(map_server), server_id, session_isValid(map_server[server_id].fd) && !map_server[server_id].maps.empty() );
// Map-server not available, tell the client to wait (client wont close, char select will respawn)
if (server_id == ARRAYLENGTH(map_server)) {
WFIFOHEAD(fd, 24);
WFIFOW(fd, 0) = 0x840;
WFIFOW(fd, 2) = 24;
strncpy(WFIFOCP(fd, 4), "0", 20); // we can't send it empty (otherwise the list will pop up)
WFIFOSET(fd, 24);
chclif_accessible_maps( fd );
return 1;
}
@@ -891,6 +1050,13 @@ int chclif_parse_charselect(int fd, struct char_session_data* sd,uint32 ipl){
// if map is not found, we check major cities
if( i < 0 ){
#if PACKETVER >= 20100714
// Let the user select a map
chclif_accessible_maps( fd );
return 0;
#else
// Try to select a map for the user
unsigned short j;
//First check that there's actually a map server online.
ARR_FIND( 0, ARRAYLENGTH(map_server), j, session_isValid(map_server[j].fd) && !map_server[j].maps.empty() );
@@ -916,6 +1082,7 @@ int chclif_parse_charselect(int fd, struct char_session_data* sd,uint32 ipl){
chclif_send_auth_result(fd,1); // 01 = Server closed
return 1;
}
#endif
}
//Send NEW auth packet [Kevin]
@@ -1424,6 +1591,9 @@ int chclif_parse(int fd) {
// character movement request
case 0x8d4: next=chclif_parse_moveCharSlot(fd,sd); break;
case 0x9a1: next=chclif_parse_req_charlist(fd,sd); break;
case HEADER_CH_SELECT_ACCESSIBLE_MAPNAME:
next = chclif_parse_select_accessible_map( fd, sd, ipl );
break;
// unknown packet received
default:
ShowError("parse_char: Received unknown packet " CL_WHITE "0x%x" CL_RESET " from ip '" CL_WHITE "%s" CL_RESET "'! Disconnecting!\n", RFIFOW(fd,0), ip2str(ipl, NULL));