diff --git a/src/char/char_clif.cpp b/src/char/char_clif.cpp index d226400a6f..ccd068bfe8 100644 --- a/src/char/char_clif.cpp +++ b/src/char/char_clif.cpp @@ -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 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 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 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( 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 node = std::make_shared(); + + 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)); diff --git a/src/char/packets.hpp b/src/char/packets.hpp index 4820c5fc95..40aef54257 100644 --- a/src/char/packets.hpp +++ b/src/char/packets.hpp @@ -90,6 +90,23 @@ struct CHARACTER_INFO{ #endif } __attribute__((packed)); +struct PACKET_HC_NOTIFY_ACCESSIBLE_MAPNAME_sub{ + int32 status; + char map[MAP_NAME_LENGTH_EXT]; +} __attribute__((packed)); + +struct PACKET_HC_NOTIFY_ACCESSIBLE_MAPNAME{ + int16 packetType; + int16 packetLength; + struct PACKET_HC_NOTIFY_ACCESSIBLE_MAPNAME_sub maps[]; +} __attribute__((packed)); + +struct PACKET_CH_SELECT_ACCESSIBLE_MAPNAME{ + int16 packetType; + int8 slot; + int8 mapnumber; +} __attribute__((packed)); + #define DEFINE_PACKET_HEADER(name, id) const int16 HEADER_##name = id; #if PACKETVER_MAIN_NUM >= 20201007 || PACKETVER_RE_NUM >= 20211103 @@ -108,6 +125,9 @@ struct CHARACTER_INFO{ DEFINE_PACKET_HEADER( HC_ACCEPT_MAKECHAR, 0x6d ) #endif +DEFINE_PACKET_HEADER( HC_NOTIFY_ACCESSIBLE_MAPNAME, 0x840 ) +DEFINE_PACKET_HEADER( CH_SELECT_ACCESSIBLE_MAPNAME, 0x841 ) + #undef DEFINE_PACKET_HEADER // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute