// TCP_Client.cpp // TCP Client Functions for FS2Open PXO // Derek Meek // 2-14-2003 // ############## ATTENTION ########## // Licensed under the Academic Free License version 2.0 // View License at http://www.opensource.org/licenses/afl-2.0.php // ################################### #include "fs2netd/tcp_client.h" #include "fs2netd/protocol.h" #include "fs2netd/tcp_socket.h" #include "fs2netd/fs2netd_client.h" #include "network/multi_log.h" #include "network/multi.h" #include "network/multiutil.h" #include "playerman/player.h" #include "ship/ship.h" #include "io/timer.h" #include "globalincs/pstypes.h" #include #include #include extern SCP_vector Table_valid_status; int FS2NetD_CheckSingleMission(const char *m_name, uint crc32, bool do_send) { int buffer_size, buffer_offset; bool my_packet = false; char buffer[100]; if (do_send) { INIT_PACKET( PCKT_MISSION_CHECK ); PXO_ADD_STRING( m_name ); PXO_ADD_UINT( crc32 ); DONE_PACKET(); if ( FS2NetD_SendData(buffer, buffer_size) == -1 ) { return 3; } } else if ( FS2NetD_DataReady() ) { int rc; uint rc_total = 0; do { rc = FS2NetD_GetData(buffer+rc_total, sizeof(buffer)-rc_total); if (rc <= 0) { break; } rc_total += rc; Sleep(20); } while ( FS2NetD_DataReady() && (rc_total < (int)sizeof(buffer)) ); if (rc < BASE_PACKET_SIZE) { return 0; } VRFY_PACKET2( PCKT_MCHECK_REPLY ); if ( !my_packet ) { return 0; } ubyte status = 0; PXO_GET_DATA( status ); Assert( (status == 0) || (status == 1) ); // anything beyond 'true' is considered a failure of some kind if (status > 1) { status = 0; } return status+1; } return 0; } int FS2NetD_SendPlayerData(const char *player_name, player *pl, bool do_send) { int buffer_size, buffer_offset; bool my_packet = false; char buffer[16384]; if (do_send) { int i, num_type_kills = 0; // send packet... (fs2open_pilot_update) INIT_PACKET( PCKT_PILOT_UPDATE ); PXO_ADD_INT( Multi_tracker_id ); PXO_ADD_STRING( player_name ); // name PXO_ADD_STRING( Multi_tracker_login ); // user PXO_ADD_INT( pl->stats.score ); // points PXO_ADD_UINT( pl->stats.missions_flown ); // missions PXO_ADD_UINT( pl->stats.flight_time ); // flighttime PXO_ADD_INT( pl->stats.last_flown ); // LastFlight PXO_ADD_INT( pl->stats.kill_count ); // Kills PXO_ADD_INT( pl->stats.kill_count_ok ); // NonFriendlyKills PXO_ADD_INT( pl->stats.assists ); // Assists PXO_ADD_UINT( pl->stats.p_shots_fired ); // PriShots PXO_ADD_UINT( pl->stats.p_shots_hit ); // PriHits PXO_ADD_UINT( pl->stats.p_bonehead_hits ); // PriFHits PXO_ADD_UINT( pl->stats.s_shots_fired ); // SecShots PXO_ADD_UINT( pl->stats.s_shots_hit ); // SecHits PXO_ADD_UINT( pl->stats.s_bonehead_hits ); // SecFHits PXO_ADD_INT( pl->stats.rank ); // rank for (i = 0; i < MAX_SHIP_CLASSES; i++) { if (pl->stats.kills[i] > 0) { Assert( (pl->stats.kills[i] >= 0) && (pl->stats.kills[i] < USHRT_MAX) ); num_type_kills++; } } Assert( (num_type_kills >= 0) && (num_type_kills < USHRT_MAX) ); PXO_ADD_USHORT( (ushort)num_type_kills ); for (i = 0; i < MAX_SHIP_CLASSES; i++) { if (pl->stats.kills[i] > 0) { PXO_ADD_STRING( Ship_info[i].name ); PXO_ADD_USHORT( (ushort)pl->stats.kills[i] ); } } Assert( (Num_medals >= 0) && (Num_medals < USHRT_MAX) ); PXO_ADD_USHORT( (ushort)Num_medals ); for (i = 0; i < Num_medals; i++) { PXO_ADD_INT( pl->stats.medal_counts[i] ); } DONE_PACKET(); if ( FS2NetD_SendData(buffer, buffer_size) == -1 ) { return -1; } } else if ( FS2NetD_DataReady() ) { int rc; uint rc_total = 0; do { rc = FS2NetD_GetData(buffer+rc_total, sizeof(buffer)-rc_total); if (rc <= 0) { break; } rc_total += rc; Sleep(20); } while ( FS2NetD_DataReady() && (rc_total < (int)sizeof(buffer)) ); if (rc < BASE_PACKET_SIZE) { return -1; } VRFY_PACKET2( PCKT_PILOT_UREPLY ); if ( !my_packet ) { return -1; } ubyte status; PXO_GET_DATA( status ); Assert( (status == 0) || (status == 1) || (status == 2) ); if (status > 2) { status = 2; } return (int)status; } return -1; } int FS2NetD_GetPlayerData(const char *player_name, player *pl, bool can_create, bool do_send) { int buffer_size, buffer_offset; bool my_packet = false; char buffer[16384]; if (do_send) { ubyte create = (ubyte)can_create; INIT_PACKET( PCKT_PILOT_GET ); PXO_ADD_INT( (can_create) ? Multi_tracker_id : -2 ); PXO_ADD_STRING( player_name ); PXO_ADD_DATA( create ); DONE_PACKET(); if ( FS2NetD_SendData(buffer, buffer_size) == -1 ) { return -1; } } else if ( FS2NetD_DataReady() ) { const fix end_time = timer_get_fixed_seconds() + (15 * F1_0); int rc; uint rc_total = 0; ubyte reply_type = 0; int si_index = 0; ushort bogus, num_type_kills = 0, num_medals = 0; char ship_name[NAME_LENGTH]; int idx; do { rc = FS2NetD_GetData(buffer+rc_total, sizeof(buffer)-rc_total); if (rc <= 0) { break; } rc_total += rc; Sleep(20); } while ( FS2NetD_DataReady() && (rc_total < (int)sizeof(buffer)) ); if (rc < BASE_PACKET_SIZE) { return -1; } VRFY_PACKET2( PCKT_PILOT_REPLY ); if ( !my_packet ) { return -1; } if ( buffer_size > (int)sizeof(buffer) ) { ml_printf("FS2NetD WARNING: Pilot update data is larger than receive buffer! Some data will be lost!"); } // make sure that we get the entire packet while ( (rc_total < (uint)buffer_size) && (rc_total < sizeof(buffer)) && (timer_get_fixed_seconds() <= end_time) ) { if ( FS2NetD_DataReady() ) { rc = FS2NetD_GetData(buffer+rc_total, sizeof(buffer) - rc_total); if (rc <= 0) { continue; } rc_total += rc; } Sleep(20); } PXO_GET_DATA( reply_type ); // if we weren't retrieved then bail out now if (reply_type != 0) { return (int)reply_type; } PXO_GET_INT( pl->stats.score ); // points PXO_GET_UINT( pl->stats.missions_flown ); // missions PXO_GET_UINT( pl->stats.flight_time ); // flighttime PXO_GET_INT( pl->stats.last_flown ); // LastFlight PXO_GET_INT( pl->stats.kill_count ); // Kills PXO_GET_INT( pl->stats.kill_count_ok ); // NonFriendlyKills PXO_GET_INT( pl->stats.assists ); // Assists PXO_GET_UINT( pl->stats.p_shots_fired ); // PriShots PXO_GET_UINT( pl->stats.p_shots_hit ); // PriHits PXO_GET_UINT( pl->stats.p_bonehead_hits ); // PriFHits PXO_GET_UINT( pl->stats.s_shots_fired ); // SecShots PXO_GET_UINT( pl->stats.s_shots_hit ); // SecHits PXO_GET_UINT( pl->stats.s_bonehead_hits ); // SecFHits PXO_GET_INT( pl->stats.rank ); // rank PXO_GET_USHORT( num_type_kills ); for (idx = 0; idx < (int)num_type_kills; idx++) { memset( ship_name, 0, sizeof(ship_name) ); PXO_GET_STRING( ship_name ); si_index = ship_info_lookup( ship_name ); if (si_index == -1) { PXO_GET_USHORT( bogus ); } else { PXO_GET_USHORT( pl->stats.kills[si_index] ); } } PXO_GET_USHORT( num_medals ); for (idx = 0; (idx < Num_medals) && (idx < num_medals); idx++) { PXO_GET_INT( pl->stats.medal_counts[idx] ); } return (int)reply_type; } return -1; } int FS2NetD_GetBanList(SCP_vector &mask_list, bool do_send) { int buffer_size, buffer_offset; bool my_packet = false; char buffer[16384]; if (do_send) { INIT_PACKET( PCKT_BANLIST_RQST ); DONE_PACKET(); if ( FS2NetD_SendData(buffer, buffer_size) == -1 ) { return -1; } } else if ( FS2NetD_DataReady() ) { const fix end_time = timer_get_fixed_seconds() + (15 * F1_0); int rc; uint rc_total = 0; int num_files = 0; char ip_mask[32]; int idx; do { rc = FS2NetD_GetData(buffer+rc_total, sizeof(buffer)-rc_total); if (rc <= 0) { break; } rc_total += rc; Sleep(20); } while ( FS2NetD_DataReady() && (rc_total < (int)sizeof(buffer)) ); if (rc < BASE_PACKET_SIZE) { return -1; } VRFY_PACKET2( PCKT_BANLIST_RPLY ); if ( !my_packet ) { return 0; } if ( buffer_size > (int)sizeof(buffer) ) { ml_printf("FS2NetD WARNING: Banned user list data is larger than receive buffer! Some data will be lost!"); } // make sure that we get the entire packet while ( (rc_total < (uint)buffer_size) && (rc_total < sizeof(buffer)) && (timer_get_fixed_seconds() <= end_time) ) { if ( FS2NetD_DataReady() ) { rc = FS2NetD_GetData(buffer+rc_total, sizeof(buffer) - rc_total); if (rc <= 0) { continue; } rc_total += rc; } Sleep(20); } PXO_GET_INT( num_files ); for (idx = 0; idx < num_files; idx++) { PXO_GET_STRING( ip_mask ); mask_list.push_back( ip_mask ); } return 1; } return 0; } int FS2NetD_GetMissionsList(SCP_vector &m_list, bool do_send) { int buffer_size, buffer_offset; bool my_packet = false; char buffer[16384]; if (do_send) { INIT_PACKET( PCKT_MISSIONS_RQST ); DONE_PACKET(); if ( FS2NetD_SendData(buffer, buffer_size) == -1 ) { return -1; } } else if ( FS2NetD_DataReady() ) { const fix end_time = timer_get_fixed_seconds() + (15 * F1_0); int rc; uint rc_total = 0; int i, num_files = 0; file_record nrec; do { rc = FS2NetD_GetData(buffer+rc_total, sizeof(buffer)-rc_total); if (rc <= 0) { break; } rc_total += rc; Sleep(20); } while ( FS2NetD_DataReady() && (rc_total < (int)sizeof(buffer)) ); if (rc < BASE_PACKET_SIZE) { return 0; } VRFY_PACKET2( PCKT_MISSIONS_REPLY ); if ( !my_packet ) { return 0; } if ( buffer_size > (int)sizeof(buffer) ) { ml_printf("FS2NetD WARNING: Mission list data is larger than receive buffer! Some data will be lost!"); } // make sure that we get the entire packet while ( (rc_total < (uint)buffer_size) && (rc_total < sizeof(buffer)) && (timer_get_fixed_seconds() <= end_time) ) { if ( FS2NetD_DataReady() ) { rc = FS2NetD_GetData(buffer+rc_total, sizeof(buffer) - rc_total); if (rc <= 0) { continue; } rc_total += rc; } Sleep(20); } PXO_GET_INT( num_files ); for (i = 0; i < num_files; i++) { memset(&nrec, 0, sizeof(file_record)); PXO_GET_STRING( nrec.name ); PXO_GET_UINT( nrec.crc32 ); m_list.push_back( nrec ); } return 1; } return 0; } int FS2NetD_Login(const char *username, const char *password, bool do_send) { int buffer_size, buffer_offset; bool my_packet = false; char buffer[150]; if (do_send) { INIT_PACKET( PCKT_LOGIN_AUTH ); PXO_ADD_STRING( username ); PXO_ADD_STRING( password ); PXO_ADD_USHORT( Multi_options_g.port ); DONE_PACKET(); if (FS2NetD_SendData(buffer, buffer_size) == -1) { return -1; } } else if ( FS2NetD_DataReady() ) { int rc; uint rc_total = 0; ubyte login_status = 0; int sid; short pilots; do { rc = FS2NetD_GetData(buffer+rc_total, sizeof(buffer)-rc_total); if (rc <= 0) { break; } rc_total += rc; Sleep(20); } while ( FS2NetD_DataReady() && (rc_total < (int)sizeof(buffer)) ); if (rc < BASE_PACKET_SIZE) { return -1; } VRFY_PACKET2( PCKT_LOGIN_REPLY ); if ( !my_packet ) { return -1; } PXO_GET_DATA( login_status ); if ( !login_status ) { return -2; } PXO_GET_INT( sid ); PXO_GET_SHORT( pilots ); return sid; } return -1; } void FS2NetD_SendServerStart() { int buffer_size; char buffer[550]; ubyte tvar; INIT_PACKET( PCKT_SERVER_START ); PXO_ADD_STRING( Netgame.name ); PXO_ADD_STRING( Netgame.mission_name ); PXO_ADD_STRING( Netgame.title ); PXO_ADD_STRING( Netgame.campaign_name ); tvar = (ubyte)Netgame.campaign_mode; PXO_ADD_DATA( tvar ); PXO_ADD_INT( Netgame.flags ); PXO_ADD_INT( Netgame.type_flags ); PXO_ADD_SHORT( (short)multi_num_players() ); PXO_ADD_SHORT( (short)Netgame.max_players ); tvar = (ubyte)Netgame.mode; PXO_ADD_DATA( tvar ); tvar = (ubyte)Netgame.rank_base; PXO_ADD_DATA( tvar ); tvar = (ubyte)Netgame.game_state; PXO_ADD_DATA( tvar ); tvar = (ubyte)multi_get_connection_speed(); PXO_ADD_DATA( tvar ); PXO_ADD_STRING(Multi_fs_tracker_channel); DONE_PACKET(); FS2NetD_SendData(buffer, buffer_size); } void FS2NetD_SendServerUpdate() { int buffer_size; char buffer[550]; ubyte tvar; INIT_PACKET( PCKT_SERVER_UPDATE ); PXO_ADD_STRING( Netgame.mission_name ); PXO_ADD_STRING( Netgame.title ); PXO_ADD_STRING( Netgame.campaign_name ); tvar = (ubyte)Netgame.campaign_mode; PXO_ADD_DATA( tvar ); PXO_ADD_SHORT( (short)multi_num_players() ); tvar = (ubyte)Netgame.game_state; PXO_ADD_DATA( tvar ); DONE_PACKET(); FS2NetD_SendData(buffer, buffer_size); } void FS2NetD_SendServerDisconnect() { int buffer_size; char buffer[BASE_PACKET_SIZE]; INIT_PACKET( PCKT_SERVER_DISCONNECT ); DONE_PACKET(); FS2NetD_SendData(buffer, buffer_size); } void FS2NetD_RequestServerList() { int buffer_size; char buffer[BASE_PACKET_SIZE+sizeof(int)+sizeof(int)+sizeof(int)+MAX_PATH]; int all = 0xFFFFFFFF; bool filtered = false; if ( strlen(Multi_fs_tracker_filter) ) { filtered = true; } // send request packet INIT_PACKET( (filtered) ? PCKT_SLIST_REQUEST_FILTER : PCKT_SLIST_REQUEST ); PXO_ADD_INT( all ); // type PXO_ADD_INT( all ); // status if (filtered) { PXO_ADD_STRING(Multi_fs_tracker_filter); } DONE_PACKET(); FS2NetD_SendData(buffer, buffer_size); } void FS2NetD_Ping() { int buffer_size; char buffer[BASE_PACKET_SIZE+sizeof(int)]; INIT_PACKET( PCKT_PING ); int time = timer_get_milliseconds(); PXO_ADD_INT( time ); DONE_PACKET(); FS2NetD_SendData(buffer, buffer_size); } void FS2NetD_Pong(int tstamp) { int buffer_size; char buffer[BASE_PACKET_SIZE+sizeof(int)]; INIT_PACKET( PCKT_PONG ); PXO_ADD_INT( tstamp ); DONE_PACKET(); FS2NetD_SendData(buffer, buffer_size); } int FS2NetD_CheckValidID() { int buffer_size; char buffer[BASE_PACKET_SIZE+sizeof(int)]; // create and send request packet INIT_PACKET( PCKT_VALID_SID_RQST ); PXO_ADD_INT( Multi_tracker_id ); DONE_PACKET(); if (FS2NetD_SendData(buffer, buffer_size) == -1) { return -1; } return 0; } int FS2NetD_ValidateTableList(bool do_send) { int buffer_size, buffer_offset; bool my_packet = false; char buffer[4096]; ushort num_tables = 0; if (do_send) { // create and send the request packet INIT_PACKET( PCKT_TABLES_RQST ); num_tables = (ushort)Table_valid_status.size(); PXO_ADD_USHORT( num_tables ); for (SCP_vector::iterator tvs = Table_valid_status.begin(); tvs != Table_valid_status.end(); ++tvs) { PXO_ADD_STRING(tvs->name ); PXO_ADD_UINT( tvs->crc32 ); } DONE_PACKET(); if (FS2NetD_SendData(buffer, buffer_size) == -1) { return -1; } } else if ( FS2NetD_DataReady() ) { int rc; const fix end_time = timer_get_fixed_seconds() + (15 * F1_0); ubyte tbl_valid_status = 0; uint rc_total = 0; do { rc = FS2NetD_GetData(buffer+rc_total, sizeof(buffer)-rc_total); if (rc <= 0) { break; } rc_total += rc; Sleep(20); } while ( FS2NetD_DataReady() && (rc_total < (int)sizeof(buffer)) ); if (rc < BASE_PACKET_SIZE) { return -1; } VRFY_PACKET2( PCKT_TABLES_REPLY ); if ( !my_packet ) { return -1; } // make sure that we get the entire packet while ( (rc_total < (uint)buffer_size) && (rc_total < sizeof(buffer)) && (timer_get_fixed_seconds() <= end_time) ) { if ( FS2NetD_DataReady() ) { rc = FS2NetD_GetData(buffer+rc_total, sizeof(buffer) - rc_total); if (rc <= 0) { continue; } rc_total += rc; } Sleep(20); } PXO_GET_USHORT( num_tables ); if ( !num_tables ) { return -1; } if ( num_tables > (int)Table_valid_status.size() ) { ml_printf("FS2NetD WARNING: Table list contains %i tables, but we only requested %i! Invalid data!", num_tables, Table_valid_status.size()); return -1; } for (SCP_vector::iterator tvs = Table_valid_status.begin(); tvs != Table_valid_status.end(); ++tvs) { PXO_GET_DATA( tbl_valid_status ); Assert( (tbl_valid_status == 0) || (tbl_valid_status == 1) ); tvs->valid = tbl_valid_status; } return 2; } return 0; } void FS2NetD_GameCountUpdate(const char *chan_name) { int buffer_size; char buffer[MAX_PATH+BASE_PACKET_SIZE+10]; INIT_PACKET( PCKT_CHAT_CHAN_COUNT_RQST ); PXO_ADD_STRING( chan_name ); DONE_PACKET(); FS2NetD_SendData(buffer, buffer_size); } void FS2NetD_CheckDuplicateLogin() { int buffer_size; char buffer[BASE_PACKET_SIZE + sizeof(int) + sizeof(ubyte) + (MAX_PLAYERS * sizeof(int)) + 10]; int ids_count = 0; int *ids = new int[MAX_PLAYERS]; int idx; if ( !ids ) { return; } for (idx = 0; idx < MAX_PLAYERS; idx++) { if ( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) ) { if ( (Net_players[idx].tracker_player_id >= 0) && (Net_players[idx].tracker_player_id != Multi_tracker_id) ) { ids[ids_count] = Net_players[idx].tracker_player_id; ids_count++; } } } if ( !ids_count ) { delete [] ids; return; } INIT_PACKET( PCKT_DUP_LOGIN_RQST ); PXO_ADD_INT( Multi_tracker_id ); Assert( MAX_PLAYERS <= 255 ); ubyte tvar = (ubyte)ids_count; PXO_ADD_DATA( tvar ); for (idx = 0; idx < ids_count; idx++) { PXO_ADD_INT( ids[idx] ); } DONE_PACKET(); FS2NetD_SendData(buffer, buffer_size); delete [] ids; }