void fs2netd_options_config_init()
{
	if ( !strlen(Multi_options_g.game_tracker_ip) ) {
		ml_printf("FS2NetD MSG:  Address for game tracker not specified, using default instead (%s).", FS2NETD_DEFAULT_SERVER);
		strncpy( Multi_options_g.game_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN );
	}

	if ( !strlen(Multi_options_g.user_tracker_ip) ) {
		ml_printf("FS2NetD MSG:  Address for user tracker not specified, using default instead (%s).", FS2NETD_DEFAULT_SERVER);
		strncpy( Multi_options_g.user_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN );
	}

	if ( !strlen(Multi_options_g.tracker_port) ) {
		ml_printf("FS2NetD MSG:  Port for game/user trackers not specified, using default instead (%u).", FS2NETD_DEFAULT_PORT);
		strncpy( Multi_options_g.tracker_port, FS2NETD_DEFAULT_PORT, STD_NAME_LEN );
	}

	if ( !strlen(Multi_options_g.pxo_ip) ) {
		ml_printf("FS2NetD MSG:  Address for chat server not specified, using default instead (%s).", FS2NETD_DEFAULT_CHAT_SERVER);
		strncpy( Multi_options_g.pxo_ip, FS2NETD_DEFAULT_CHAT_SERVER, MULTI_OPTIONS_STRING_LEN );
	}

	if ( !strlen(Multi_options_g.pxo_banner_url) ) {
		ml_printf("FS2NetD MSG:  URL for banners not specified, using default instead (%s).", FS2NETD_DEFAULT_BANNER_URL);
		strncpy( Multi_options_g.pxo_banner_url, FS2NETD_DEFAULT_BANNER_URL, MULTI_OPTIONS_STRING_LEN );
	}
}
void fs2netd_connect()
{
	int rc = 0;

	// don't bother with this if we aren't on FS2NetD
	if ( !Om_tracker_flag ) {
		return;
	}

	if ( !(Game_mode & GM_MULTIPLAYER) ) {
		return;
	}

	if (Is_connected) {
		return;
	}


	if ( !PXO_port ) {
		Assert( strlen(Multi_options_g.game_tracker_ip) );
		Assert( strlen(Multi_options_g.tracker_port) );
	
		if ( strlen(Multi_options_g.game_tracker_ip) ) {
			strncpy( PXO_Server, Multi_options_g.game_tracker_ip, sizeof(PXO_Server) - 1 );
		} else {
			ml_printf("FS2NetD ERROR:  No server specified in multi.cfg!  Using default instead (%s)!", FS2NETD_DEFAULT_SERVER);
			strncpy( PXO_Server, FS2NETD_DEFAULT_SERVER, sizeof(PXO_Server) - 1 );
		}

		if ( strlen(Multi_options_g.tracker_port) ) {
			long tmp = strtol(Multi_options_g.tracker_port, (char**)NULL, 10);

			if ( (tmp < 1024) || (tmp > USHRT_MAX) ) {
				ml_printf("FS2NetD ERROR:  The port specified in multi.cfg, '%i', is outside of the required range, %i through %i!", tmp, 1024, USHRT_MAX);
				ml_printf("Fs2NetD ERROR:  Setting port to default value (%s) ...", FS2NETD_DEFAULT_PORT);
				PXO_port = (ushort) strtol(FS2NETD_DEFAULT_PORT, (char**)NULL, 10);
			} else {
				PXO_port = (ushort)tmp;
			}
		} else {
			PXO_port = (ushort) strtol(FS2NETD_DEFAULT_PORT, (char**)NULL, 10);
		}
	}

	In_process = true;

	if (Is_standalone) {
		do { rc = fs2netd_connect_do(); } while (!rc);
	} else {
		popup_till_condition(fs2netd_connect_do, XSTR("&Cancel", 779), XSTR("Connecting into FS2NetD", -1));
	}

	In_process = false;
}
int fs2netd_get_pilot_info(const char *callsign, player *out_plr, bool first_call)
{
	if ( !Logged_in ) {
		return -2;
	}

	if ( (out_plr == NULL) || (callsign == NULL) || !(strlen(callsign)) ) {
		return -2;
	}

	static player new_plr;

	if (first_call) {
		new_plr.reset();
		strncpy( new_plr.callsign, callsign, CALLSIGN_LEN );

		// initialize the stats to default values
		init_scoring_element( &new_plr.stats );

		out_plr->reset();

		Local_timeout = timer_get_seconds() + 30;

		In_process = true;

		ml_printf("FS2NetD MSG: Requesting pilot stats for '%s' ...", callsign);
	}

	int rc = FS2NetD_GetPlayerData(callsign, &new_plr, false, first_call);

	// some sort of failure
	if (rc > 0) {
		In_process = false;
		Local_timeout = -1;
		return -2;
	}

	// if timeout passes then bail on failure
	if ( timer_get_seconds() > Local_timeout ) {
		In_process = false;
		Local_timeout = -1;
		return -2;
	}

	if (rc == 0) {
		memcpy( out_plr, &new_plr, sizeof(player) );
		In_process = false;
		Local_timeout = -1;
	}

	// we should only be returning -1 (processing) or 0 (got data successfully)
	return rc;
}
Beispiel #4
0
// write out some info about stuff
void multi_log_write_update()
{
	int diff = (int)difftime(time(NULL), Multi_log_open_systime);
	int hours, mins, seconds;

	// figure out some time values
	hours = diff / 3600;
	mins = (diff - (hours * 3600)) / 60;
	seconds = (diff - (hours * 3600) - (mins * 60));

	// print it out
	ml_printf("Server has been active for %d hours, %d minutes, and %d seconds", hours, mins, seconds);
}
Beispiel #5
0
file_record* GetMissionsList(int &numMissions, const char *masterserver, UDP_Socket &Socket, int port, int timeout)
{
	fs2open_file_check fc_packet;
	fc_packet.pid = PCKT_MISSIONS_RQST;

	timeout = timeout * CLK_TCK;
	int starttime = clock();

	std::string sender = masterserver;

	// send Packet
	if (Socket.SendPacket((char *)&fc_packet, sizeof(fs2open_file_check), sender, port) == -1)
		return NULL;

	char PacketBuffer[16384]; // 16K should be enough i think..... I HOPE!
	fs2open_pxo_missreply *misreply_ptr = (fs2open_pxo_missreply *) PacketBuffer;
	
	file_record *frecs = NULL;
	numMissions = 0;

	while ((clock() - starttime) <= timeout)
	{

		if (Socket.GetPacket(PacketBuffer, 16384, sender) != -1)
		{
			if (misreply_ptr->pid != PCKT_MISSIONS_REPLY)
			{
				continue; // skip and ignore this packet
			}
			

			if ((misreply_ptr->num_files * sizeof(file_record)) + (sizeof(int) * 2) > 16384)
			{
				// WE'RE IN TROUBLE!

				ml_printf("Network (FS2OpenPXO): PCKT_MISSIONS_REPLY was larger than 16k!!!\n");
				return NULL;
			}
			frecs = new file_record[misreply_ptr->num_files];
			memcpy(frecs, PacketBuffer + 8, sizeof(file_record) * misreply_ptr->num_files); // packet buffer will be two ints then the array;
			numMissions = misreply_ptr->num_files;
			return frecs;

		}


	}

	return NULL;

}
// setup kill matrix data
void multi_df_setup_kill_matrix()
{
	int idx, s_idx;
	multi_df_score *s;

	Multi_df_score_count = 0;

	// add players as necessary
	for(idx=0; idx<MAX_PLAYERS; idx++){
		if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].m_player != NULL)){
			// stuff data for this guy
			s = &Multi_df_score[Multi_df_score_count++];

			ml_printf("Dogfight debrief stats for %s", Net_players[idx].m_player->callsign);
			for(s_idx=0; s_idx<MAX_PLAYERS; s_idx++){
				ml_printf("%d", Net_players[idx].m_player->stats.m_dogfight_kills[s_idx]);
			}

			s->stats = Net_players[idx].m_player->stats;
			strcpy_s(s->callsign, Net_players[idx].m_player->callsign);			
			s->np_index = idx;
		}
	}
}
int fs2netd_get_valid_missions_do()
{
	if (Local_timeout == -1) {
		Local_timeout = timer_get_seconds() + 30;
	}

	// get the available CRCs from the server if we need to
	if ( FS2NetD_file_list.empty() ) {
		int rc = FS2NetD_GetMissionsList(FS2NetD_file_list, do_full_packet);

		do_full_packet = false;

		// communications error
		if (rc < 0) {
			Local_timeout = -1;
			return 4;
		}

		// no missions
		if ( rc && FS2NetD_file_list.empty() ) {
			Local_timeout = -1;
			return 2;
		}

		// if timeout passes then bail on crc failure
		if ( timer_get_seconds() > Local_timeout ) {
			Local_timeout = -1;
			return 1;
		}
	}
	// we should have the CRCs, or there were no missions, so process them
	else {
		static char **file_names = NULL;
		static int idx = 0, count = 0;

		bool found = false;
		int file_index = 0;
		char valid_status = MVALID_STATUS_UNKNOWN;
		char full_name[MAX_FILENAME_LEN], wild_card[10];
		char val_text[MAX_FILENAME_LEN+15];
		uint checksum = 0;

		if (file_names == NULL) {
			// allocate filename space	
			file_names = (char**) vm_malloc_q( sizeof(char*) * 1024 ); // 1024 files should be safe!

			if (file_names == NULL) {
				Local_timeout = -1;
				return 3;
			}

			memset( wild_card, 0, sizeof(wild_card) );
			strcpy_s( wild_card, NOX("*") );
			strcat_s( wild_card, FS_MISSION_FILE_EXT );

			idx = count = cf_get_file_list(1024, file_names, CF_TYPE_MISSIONS, wild_card);
		}

		// drop idx first thing
		idx--;

		// we should be done validating, or just not have nothing to validate
		if (idx < 0) {
			for (idx = 0; idx < count; idx++) {
				if (file_names[idx] != NULL) {
					vm_free(file_names[idx]);
					file_names[idx] = NULL;
				}
			}

			vm_free(file_names);
			file_names = NULL;

			idx = count = 0;

			Local_timeout = -1;
			return 4;
		}


		// verify all filenames that we know about with their CRCs
		// NOTE: that this is done for one file per frame, since this is inside of a popup
		memset( full_name, 0, MAX_FILENAME_LEN );
		strncpy( full_name, cf_add_ext(file_names[idx], FS_MISSION_FILE_EXT), sizeof(full_name) - 1 );

		memset( val_text, 0, sizeof(val_text) );
		snprintf( val_text, sizeof(val_text) - 1, "Validating:  %s", full_name );

		if (Is_standalone) {
			if ( std_gen_is_active() ) {
				std_gen_set_text(val_text, 1);
			}
		} else {
			popup_change_text(val_text);
		}

		cf_chksum_long(full_name, &checksum);

		// try and find the file
		file_index = multi_create_lookup_mission(full_name);

		found = false;

		if (file_index >= 0) {
			for (SCP_vector<file_record>::iterator fr = FS2NetD_file_list.begin(); fr != FS2NetD_file_list.end() && !found; ++fr) {
				if ( !stricmp(full_name, fr->name) ) {
					if (fr->crc32 == checksum) {
						found = true;
						valid_status = MVALID_STATUS_VALID;
					} else {
						valid_status = MVALID_STATUS_INVALID;
					}

					Multi_create_mission_list[file_index].valid_status = valid_status;
				}
			}

			if (found) {
				ml_printf("FS2NetD Mission Validation: %s  =>  Valid!", full_name);
			} else {
				ml_printf("FS2NetD Mission Validation: %s  =>  INVALID! -- 0x%08x", full_name, checksum);
			}
		}
	}

	return 0;
}
static void fs2netd_handle_messages()
{
	int buffer_size = 0, buffer_offset = 0;
	int bytes_read = 0;
	char tbuf[256];
	char buffer[8192];
	ubyte pid = 0;
	int itemp;

	while ( FS2NetD_DataReady() && (bytes_read < (int)sizeof(buffer)) ) {
		int read_size = FS2NetD_GetData(buffer+bytes_read, sizeof(buffer)-bytes_read);

		if (read_size <= 0) {
			break;
		}

		bytes_read += read_size;

		Sleep(20);
	}

	if ( (bytes_read == 0) || (bytes_read < BASE_PACKET_SIZE) ) {
		return;
	}

	Last_activity = timer_get_seconds();

	buffer_offset = 0;

	while (buffer_offset+BASE_PACKET_SIZE <= bytes_read) {
		PXO_GET_DATA( pid );
		PXO_GET_INT( buffer_size );

		// packet has more data than our buffer received
		if (buffer_offset+buffer_size-BASE_PACKET_SIZE > bytes_read) {
			break;
		}

		// processing time!
		switch (pid) {
			case PCKT_PING: {
				PXO_GET_INT( itemp );

			//	ml_printf("FS2NetD received PING");

				FS2NetD_Pong(itemp);

				break;
			}

			case PCKT_PONG: {
				PXO_GET_INT( itemp );

				ml_printf("FS2NetD PONG: %d ms", timer_get_milliseconds() - itemp);

				// reset timestamp, since the ping was successful
				Ping_timestamp = -1;

				break;
			}

			case PCKT_NETOWRK_WALL: {
				PXO_GET_STRING( tbuf );
				ml_printf("FS2NetD WALL received MSG: %s", tbuf);

				switch (Netgame.game_state) {
					case NETGAME_STATE_FORMING:
					case NETGAME_STATE_BRIEFING:
					case NETGAME_STATE_MISSION_SYNC:
					case NETGAME_STATE_DEBRIEF:
						multi_display_chat_msg(tbuf, 0, 0);
						break;

					case NETGAME_STATE_IN_MISSION: // gotta make it paused
						//multi_pause_request(1); 
						//send_game_chat_packet(Net_player, str, MULTI_MSG_ALL, NULL);
						HUD_printf(tbuf);
						break;

					default:
						// do-nothing
						break;
				}

				break;
			}

			case PCKT_CHAT_CHAN_COUNT_REPLY: {
				PXO_GET_STRING( tbuf );
				PXO_GET_INT( itemp );

				if ( (itemp < 0) || (itemp > USHRT_MAX) ) {
					itemp = 0;
				}

				multi_pxo_channel_count_update(tbuf, itemp);

				break;
			}

			case PCKT_VALID_SID_REPLY: {
				ubyte login_status = 0;

				PXO_GET_DATA( login_status );

				if (login_status != 1) {
					ml_printf("FS2NetD IDENT: Got invalid login check!");
					fs2netd_reset_connection();
				}

				break;
			}

			case PCKT_DUP_LOGIN_REPLY: {
				ubyte dupe_status = 0;

				PXO_GET_DATA( dupe_status );

				Duplicate_login_detected = (dupe_status != 0);

				break;
			}

			case PCKT_SLIST_REPLY: {
				int numServers = 0;
				int svr_flags;
				ushort svr_port;
				char svr_ip[16];
				active_game ag;

				PXO_GET_USHORT( numServers );

				if (numServers == 0) {
					break;
				}

				for (int i = 0; i < numServers; i++) {
					PXO_GET_INT( svr_flags );
					PXO_GET_USHORT( svr_port );
					PXO_GET_STRING( svr_ip );

					if ( !psnet_is_valid_ip_string(svr_ip) ) {
						ml_printf("FS2NetD SLIST: Invalid ip string (%s)!", svr_ip);
					} else {
						memset( &ag, 0, sizeof(active_game) );

						ag.server_addr.type = NET_TCP;
						ag.server_addr.port = (short) svr_port;

						if (ag.server_addr.port <= 0) {
							ag.server_addr.port = DEFAULT_GAME_PORT;
						}

						psnet_string_to_addr(&ag.server_addr, svr_ip);

						// query this server
						send_server_query(&ag.server_addr);
					}
				}

				break;
			}

			default: {
				break;
			}
		}
	}
}
int fs2netd_login_do()
{
	if (Multi_tracker_id < 0) {
		if ( Is_standalone && std_gen_is_active() ) {
			std_gen_set_text("Verifying username and password", 1);
		} else {
			popup_change_text( XSTR("Verifying username and password", 1576) );
		}

		memset(Multi_tracker_id_string, 0, sizeof(Multi_tracker_id_string));

		if (Local_timeout == -1) {
			Local_timeout = timer_get_seconds() + 15;
		}

		// if timeout passes then bail on SID failure
		if ( timer_get_seconds() > Local_timeout ) {
			ml_string("FS2NetD MSG: Login failure due to timeout!");
			Local_timeout = -1;
			return 2;
		}

		const char *user = Multi_tracker_login;
		const char *passwd = Multi_tracker_passwd;

		if (Is_standalone) {
			if ( strlen(Multi_options_g.std_pxo_login) ) {
				user = Multi_options_g.std_pxo_login;
			}

			if ( strlen(Multi_options_g.std_pxo_password) ) {
				passwd = Multi_options_g.std_pxo_password;
			}
		}

		Multi_tracker_id = FS2NetD_Login(user, passwd, do_full_packet);

		// if we have already been through once then only deal with the recieve packet next time
		do_full_packet = false;

		// invalid login
		if (Multi_tracker_id == -2) {
			Multi_tracker_id = -1;
			Local_timeout = -1;
			return 1;
		}

		if (Multi_tracker_id >= 0) {
			ml_printf("FS2NetD MSG: Login '%s' is valid, session ID is %d!", user, Multi_tracker_id);
			do_full_packet = true;
			Local_timeout = -1;
		}
	} else {
		if ( Is_standalone && std_gen_is_active() ) {
			std_gen_set_text("Getting pilot stats", 1);
		} else {
			popup_change_text( XSTR("Getting pilot stats", 1577) );
		}

		if (Local_timeout == -1) {
			Local_timeout = timer_get_seconds() + 30;
		}

		// if timeout passes then bail on stats failure
		if ( timer_get_seconds() > Local_timeout ) {
		
			Local_timeout = -1;
			return 2;
		}

		if (do_full_packet) {
			ml_printf("FS2NetD MSG: Requesting login pilot stats for '%s' ...", Players[Player_num].callsign);
		}

		int rescode = FS2NetD_GetPlayerData(Players[Player_num].callsign, &Players[Player_num], true, do_full_packet);

		do_full_packet = false;

		if ( rescode != -1 ) {
			Local_timeout = -1;
			return (rescode + 3);
		}
	}

	return 0;
}
bool fs2netd_login()
{
	bool retval = true;
	int rc;

	// don't bother with this if we aren't on FS2NetD
	if ( !Om_tracker_flag ) {
		return false;
	}

	if ( !(Game_mode & GM_MULTIPLAYER) ) {
		return false;
	}

	if ( Logged_in && (Multi_tracker_id >= 0) ) {
		return true;
	}

	Logged_in = false;

	Multi_tracker_id = -1;
	memset( Multi_tracker_id_string, 0, sizeof(Multi_tracker_id_string) );

	// verify that our connection settings are sane
	fs2netd_options_config_init();

	// if we're a standalone, show a dialog saying "validating tables"
	if (Is_standalone) {
		std_create_gen_dialog("Logging into FS2NetD");
		std_gen_set_text("Connecting...", 1);
	}

	fs2netd_connect();

	if ( !Is_connected ) {
		if ( !Is_standalone ) {
			popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Failed to connect to FS2NetD server!", 1578));
		} else {
			std_gen_set_text("Connect FAILED!", 1);
			Sleep(2000);
			std_destroy_gen_dialog();
		}

		return false;
	}

	char error_str[256];
	char std_error_str[64];

	do_full_packet = true;

	In_process = true;

	if (Is_standalone) {
		do { rc = fs2netd_login_do(); } while (!rc);
	} else {
		rc = popup_till_condition(fs2netd_login_do, XSTR("&Cancel", 779), XSTR("Logging into FS2NetD", 1579));
	}

	In_process = false;
	Local_timeout = -1;

	memset( error_str, 0, sizeof(error_str) );
	memset( std_error_str, 0, sizeof(std_error_str) );

	switch (rc) {
		// the action was cancelled
		case 0:
			ml_string("FS2NetD MSG: Login process canceled by user.");
			retval = false;
			break;

		// didn't get a session id
		case 1: {
			const char *user = Multi_tracker_login;
			const char *passwd = Multi_tracker_passwd;

			if (Is_standalone) {
				if ( strlen(Multi_options_g.std_pxo_login) ) {
					user = Multi_options_g.std_pxo_login;
				}

				if ( strlen(Multi_options_g.std_pxo_password) ) {
					passwd = Multi_options_g.std_pxo_password;
				}
			}

			ml_printf("FS2NetD ERROR: Login %s/%s is invalid!", user, passwd);

			if (strlen(user) == 0) {
				strcpy_s(error_str, "Login failed! No username supplied. Go to options -> multi options and add one");
				strcpy_s(std_error_str, "Login failed! No username!");
			}
			else if (strlen(passwd) == 0) {
				strcpy_s(error_str, "Login failed! No password supplied. Go to options -> multi options and add one");
				strcpy_s(std_error_str, "Login failed! No password!");
			}
			else {
				strcpy_s(error_str, "Login failed!");
				strcpy_s(std_error_str, "Login failed!");
			}
			retval = false;
			break;
		}

		// unknown failure fetching pilot data
		case 2:
			ml_string("FS2NetD ERROR: UNKNOWN ERROR when fetching pilot data");
			strcpy_s(error_str, "An Unknown Error (probably a timeout) occured when trying to retrieve your pilot data.");
			strcpy_s(std_error_str, "Unknown Error (timeout?)");
			retval = false;
			break;

		// success!!
		case 3:
			ml_string("FS2NetD MSG: Got Pilot data");
			retval = true;
			break;

		// success!!  pilot was created
		case 4:
			ml_string("FS2NetD MSG: Created New Pilot");
			strcpy_s(error_str, "New Pilot has been created.");
			strcpy_s(std_error_str, "New Pilot has been created.");
			retval = true;
			break;

		// invalid pilot name
		case 5:
			ml_string("FS2NetD ERROR: Invalid Pilot!");
			strcpy_s(error_str, "Invalid pilot name - A serious error has occured, Contact the FS2NetD Administrator!");
			strcpy_s(std_error_str, "Invalid pilot name!");
			retval = false;
			break;

		// the session id was invalid
		case 6:
			ml_string("FS2NetD ERROR: Invalid SID!");
			strcpy_s(error_str, "Invalid SID - A serious error has occured, Contact the FS2NetD Administrator!");
			strcpy_s(std_error_str, "Invalid SID");
			retval = false;
			break;

		default:
			ml_string("FS2NetD ERROR: Unknown return case for GetPlayerData()");
			strcpy_s(error_str, "Unknown return case from GetPlayerData(). Contact the FS2NetD Administrator!");
			retval = false;
			break;
	}

	if ( !Is_standalone && strlen(error_str) ) {
		popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, error_str);
	} else if ( Is_standalone && std_gen_is_active() && strlen(std_error_str) ) {
		std_gen_set_text(std_error_str, 1);
		Sleep(2000);
	}

	if (retval) {
		Logged_in = true;
		sprintf(Multi_tracker_id_string, "%d", Multi_tracker_id);
	} else {
		// clear and reset connection, for the next time we try...
		fs2netd_disconnect();
	}

	if (Is_standalone) {
		std_destroy_gen_dialog();
	}

	return retval;
}
int FS2NetD_GetMissionsList(SCP_vector<file_record> &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_update_valid_tables()
{
	int rc;
	int hacked = 0;

	// if there are no tables to check with then bail
	if ( Table_valid_status.empty() ) {
		return -1;
	}

	// if we're not on FS2NetD then don't bother with this function
	if ( !Om_tracker_flag && (Game_mode & GM_MULTIPLAYER) ) {
		return -1;
	}

	// maybe try to init first
	fs2netd_connect();

	// if we didn't connect to FS2NetD then bail out now
	if ( !Is_connected ) {
		return -1;
	}

	// if we're a standalone, show a dialog saying "validating tables"
	if (Game_mode & GM_STANDALONE_SERVER) {
		std_create_gen_dialog("Validating tables");
		std_gen_set_text("Querying FS2NetD:", 1);
	}

	do_full_packet = 1;

	In_process = true;

	if (Is_standalone) {
		do { rc = fs2netd_update_valid_tables_do(); } while (!rc);
	} else {
		rc = popup_till_condition(fs2netd_update_valid_tables_do, XSTR("&Cancel", 779), XSTR("Starting table validation", -1));
	}

	In_process = false;

	switch (rc) {
		// canceled by popup
		case 0:
			return -1;

		// timed out
		case 1: {
			if ( !Is_standalone ) {
				popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Table validation timed out!", -1));
			}

			return -1;
		}

		// no tables
		case 2: {
			if ( !Is_standalone ) {
				popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("No tables are available from the server for validation!", -1));
			}

			return -1;
		}
	}

	// output the status of table validity to multi.log
	for (uint i = 0; i < Table_valid_status.size(); i++) {
		if (Table_valid_status[i].valid) {
			ml_printf("FS2NetD Table Check: '%s' -- Valid!", Table_valid_status[i].name);
		} else {
			ml_printf("FS2NetD Table Check: '%s' -- INVALID (0x%x)!", Table_valid_status[i].name, Table_valid_status[i].crc32);
			hacked = 1;
		}
	}

	// if we're a standalone, kill the validate dialog
	if (Game_mode & GM_STANDALONE_SERVER) {
		std_destroy_gen_dialog();
	}

	return hacked;
}
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;
}
void fs2netd_options_config_init()
{
	if (PXO_options_loaded) {
		return;
	}

	if ( !strlen(Multi_options_g.game_tracker_ip) ) {
		ml_printf("NOTICE: Address for game tracker not specified, using default instead (%s).", FS2NETD_DEFAULT_SERVER);
		strncpy( Multi_options_g.game_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN );
	} else if ( !strcmp("gt.pxo.net", Multi_options_g.game_tracker_ip) ) {
		ml_printf("NOTICE: Incompatible game tracker IP detected (gt.pxo.net), using default instead (%s)!", FS2NETD_DEFAULT_SERVER);
		strncpy( Multi_options_g.game_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN );
	}

	if ( !strlen(Multi_options_g.user_tracker_ip) ) {
		ml_printf("NOTICE: Address for user tracker not specified, using default instead (%s).", FS2NETD_DEFAULT_SERVER);
		strncpy( Multi_options_g.user_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN );
	} else if ( !strcmp("ut.pxo.net", Multi_options_g.user_tracker_ip) ) {
		ml_printf("NOTICE: Incompatible user tracker IP detected (ut.pxo.net), using default instead (%s)!", FS2NETD_DEFAULT_SERVER);
		strncpy( Multi_options_g.user_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN );
	}

	if ( !strlen(Multi_options_g.tracker_port) ) {
		ml_printf("NOTICE: Port for game/user trackers not specified, using default instead (%s).", FS2NETD_DEFAULT_PORT);
		strncpy( Multi_options_g.tracker_port, FS2NETD_DEFAULT_PORT, STD_NAME_LEN );
	} else {
		long port_tmp = strtol(Multi_options_g.tracker_port, (char**)NULL, 10);

		if ( (port_tmp < 1024) || (port_tmp > USHRT_MAX) ) {
			ml_printf("NOTICE: The port specified for game/user trackers, '%i', is outside of the required range, %i through %i!", port_tmp, 1024, USHRT_MAX);
			ml_printf("NOTICE: Port for game/user trackers is invalid, using default instead (%s).", FS2NETD_DEFAULT_PORT);
			strncpy( Multi_options_g.tracker_port, FS2NETD_DEFAULT_PORT, STD_NAME_LEN );
		}
	}

	if ( !strlen(Multi_options_g.pxo_ip) ) {
		ml_printf("NOTICE: Address for chat server not specified, using default instead (%s).", FS2NETD_DEFAULT_CHAT_SERVER);
		strncpy( Multi_options_g.pxo_ip, FS2NETD_DEFAULT_CHAT_SERVER, MULTI_OPTIONS_STRING_LEN );
	} else if ( !strcmp("chat.pxo.net", Multi_options_g.pxo_ip) ) {
		ml_printf("NOTICE: Incompatible chat server IP detected (chat.pxo.net), using default instead (%s)!", FS2NETD_DEFAULT_CHAT_SERVER);
		strncpy( Multi_options_g.pxo_ip, FS2NETD_DEFAULT_CHAT_SERVER, MULTI_OPTIONS_STRING_LEN );
	}

	if ( !strlen(Multi_options_g.pxo_banner_url) ) {
		ml_printf("NOTICE: URL for banners not specified, using default instead (%s).", FS2NETD_DEFAULT_BANNER_URL);
		strncpy( Multi_options_g.pxo_banner_url, FS2NETD_DEFAULT_BANNER_URL, MULTI_OPTIONS_STRING_LEN );
	} else if ( !strcmp("http://www.pxo.net/files/banners", Multi_options_g.pxo_banner_url) ) {
		ml_printf("NOTICE: Incompatible banner URL detected (chat.pxo.net), using default instead (%s)!", FS2NETD_DEFAULT_BANNER_URL);
		strncpy( Multi_options_g.pxo_banner_url, FS2NETD_DEFAULT_BANNER_URL, MULTI_OPTIONS_STRING_LEN );
	}

	PXO_options_loaded = true;
}
int fs2netd_login_do()
{
	if (PXO_SID == -1) {
		if ( Is_standalone && std_gen_is_active() ) {
			std_gen_set_text("Verifying username and password", 1);
		} else {
			popup_change_text( XSTR("Verifying username and password", -1) );
		}

		if (timeout == -1) {
			timeout = timer_get_fixed_seconds() + (15 * F1_0);
		}

		// if timeout passes then bail on SID failure
		if ( timer_get_fixed_seconds() > timeout ) {
			timeout = -1;
			return 2;
		}

		PXO_SID = FS2NetD_Login(Multi_tracker_login, Multi_tracker_passwd, do_full_packet);

		// if we have already been through once then only deal with the recieve packet next time
		do_full_packet = 0;

		// invalid login
		if (PXO_SID == -2) {
			timeout = -1;
			return 1;
		}

		if (PXO_SID >= 0) {
			ml_printf("FS2NetD MSG: Login %s is valid, session ID is %i!", Multi_tracker_login, PXO_SID);
			do_full_packet = 1;
			timeout = -1;
		}
	} else {
		if ( Is_standalone && std_gen_is_active() ) {
			std_gen_set_text("Getting pilot stats", 1);
		} else {
			popup_change_text( XSTR("Getting pilot stats", -1) );
		}

		if (timeout == -1) {
			timeout = timer_get_fixed_seconds() + (30 * F1_0);
		}

		// if timeout passes then bail on stats failure
		if ( timer_get_fixed_seconds() > timeout ) {
			timeout = -1;
			return 2;
		}

		int rescode = FS2NetD_GetPlayerData(PXO_SID, Players[Player_num].callsign, &Players[Player_num], true, do_full_packet);

		do_full_packet = 0;

		if ( rescode != -1 ) {
			timeout = -1;
			return (rescode + 3);
		}
	}

	return 0;
}
void fs2netd_do_frame()
{
	int rc, buffer_size, buffer_offset;
	char buffer[300], str[256];
	ubyte pid = 0;
	int itemp;
	static fix NextPing = -1, NextHeartBeat = -1;
	static fix GotPong = -1;
	bool reset = false;

	// don't bother with this if we aren't on FS2NetD
	if ( !Om_tracker_flag ) {
		return;
	}

	if ( !(Game_mode & GM_MULTIPLAYER) ) {
		return;
	}

	// not connected to server
	if ( !Is_connected ) {
		return;
	}

	// in a previous processing loop, so don't do a frame until that has completed
	if ( In_process ) {
		return;
	}


	// if we didn't get a PONG within 4 minutes the server connection must have dropped
	if ( (GotPong != -1) && ((NextPing - GotPong) > (240 * F1_0)) ) {
		ml_printf("FS2NetD WARNING:  Lost connection to server!");
		FS2NetD_Disconnect();

		Is_connected = false;
		Logged_in = false;
		PXO_SID = -1;

		NextHeartBeat = -1;
		NextPing = -1;
		GotPong = -1;

		// try to reinit the server connection
		fs2netd_login();

		// make sure that we are good to go
		if ( !Is_connected ) {
			if (!Is_standalone) {
				gamesnd_play_iface(SND_GENERAL_FAIL);
				popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_BIG | PF_TITLE_RED, 1, POPUP_OK, "ERROR:\nLost connection to the FS2NetD server!");
			}
	
			return;
		} else {
			ml_printf("FS2NetD NOTICE:  Connection to server has been reestablished!");
		}
	}

	// send out ping every 60 seconds
	if ( (NextPing == -1) || (timer_get_fixed_seconds() >= NextPing) ) {
		// if we have seen a long period of time between pings then reset the pong time too
		if ( (timer_get_fixed_seconds() - NextPing) > (120 * F1_0) ) {
			reset = true;
		}

		NextPing = timer_get_fixed_seconds() + (60 * F1_0);

		// we go ahead and set the initial GotPong here, even though we haven't gotten a pong yet
		if ( (GotPong == -1) || reset ) {
			GotPong = NextPing;
			reset = false;
		}

		FS2NetD_Ping();

		ml_printf("FS2NetD sent PING");
	}

	// send out server heartbeat every 2 minutes, unless forced to by an update
	if ( (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Multi_create_force_heartbeat || (NextHeartBeat == -1) || (timer_get_fixed_seconds() >= NextHeartBeat)) ) {
		Multi_create_force_heartbeat = 0;
		NextHeartBeat = timer_get_fixed_seconds() + (120 * F1_0);

		FS2NetD_SendHeartBeat(Netgame.name, Netgame.mission_name, Netgame.title, Netgame.type_flags, Netgame.server_addr.port, multi_num_players());

		ml_printf("FS2NetD sent HeartBeat");
	}

	// Check for GWall messages - ping replies, etc
	if ( (rc = FS2NetD_GetData(buffer, sizeof(buffer))) != -1 ) {
		int rc_total = rc;
		buffer_offset = 0;

		while (rc_total > buffer_offset) {
			// make sure we have enough data to try and process
			if (rc_total < BASE_PACKET_SIZE) {
				break;
			}

			PXO_GET_DATA( pid );
			PXO_GET_INT( buffer_size );

			while ( (rc_total < buffer_size) && ((sizeof(buffer) - rc_total) > 0) ) {
				if ( (rc = FS2NetD_GetData(buffer+rc_total, sizeof(buffer) - rc_total)) != -1 ) {
					rc_total += rc;
				} else {
					break;
				}
			}

			if (buffer_size <= 0) {
				break;
			}

			// we don't have the full packet, so bail
			if (rc_total < buffer_size) {
				break;
			}

			// processing time!
			switch (pid) {
				case PCKT_PING: {
					PXO_GET_INT( itemp );

					ml_printf("FS2NetD received PING");

					FS2NetD_Pong(itemp);
					break;
				}

				case PCKT_PONG: {
					PXO_GET_INT( itemp );

					ml_printf("FS2NetD received PONG: %d ms", timer_get_milliseconds() - itemp);

					GotPong = timer_get_fixed_seconds();
					break;
				}

				case PCKT_NETOWRK_WALL: {
					PXO_GET_STRING( str );
					ml_printf("FS2NetD WALL received MSG: %s", str);
	
					switch (Netgame.game_state) {
						case NETGAME_STATE_FORMING:
						case NETGAME_STATE_BRIEFING:
						case NETGAME_STATE_MISSION_SYNC:
						case NETGAME_STATE_DEBRIEF:
							multi_display_chat_msg(str, 0, 0);
							break;

						/* -- Won't Happen - multi_do_frame() is not called during paused state 
							  so the game will not even receive the data during it
						case NETGAME_STATE_PAUSED: // EASY!
							send_game_chat_packet(Net_player, str, MULTI_MSG_ALL, NULL);
							break;
						*/

						case NETGAME_STATE_IN_MISSION: // gotta make it paused
							//multi_pause_request(1); 
							//send_game_chat_packet(Net_player, str, MULTI_MSG_ALL, NULL);
							HUD_printf(str);
							break;

						default:
							// do-nothing
							break;
					}

					break;
				}

				default: {
					ml_printf("Unexpected FS2NetD Packet - PID = %x", pid);
					break;
				}
			}

			buffer_offset += (buffer_size - BASE_PACKET_SIZE);
		}
	}
}
bool fs2netd_login()
{
	bool retval = true;
	int rc;

	// don't bother with this if we aren't on FS2NetD
	if ( !Om_tracker_flag ) {
		return false;
	}

	if ( !(Game_mode & GM_MULTIPLAYER) ) {
		return false;
	}

	if ( Logged_in ) {
		if ( (PXO_SID != -1) && !(FS2NetD_CheckValidSID(PXO_SID)) ) {
			PXO_SID = -1;
		} else {
			return true;
		}
	}

	fs2netd_connect();

	if ( !Is_connected ) {
		if ( !Is_standalone ) {
			popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Failed to connect to FS2NetD server!", -1));
		}

		return false;
	}

	char error_str[256];

	do_full_packet = 1;

	In_process = true;

	if (Is_standalone) {
		do { rc = fs2netd_login_do(); } while (!rc);
	} else {
		rc = popup_till_condition(fs2netd_login_do, XSTR("&Cancel", 779), XSTR("Logging into FS2NetD", -1));
	}

	In_process = false;

	memset( error_str, 0, sizeof(error_str) );

	switch (rc) {
		// the action was cancelled
		case 0:
			retval = false;
			break;

		// didn't get a session id
		case 1:
			ml_printf("FS2NetD ERROR:  Login %s/%s is invalid!", Multi_tracker_login, Multi_tracker_passwd);
			strcpy(error_str, "Login failed!");
			retval = false;
			break;

		// unknown failure fetching pilot data
		case 2:
			ml_printf("FS2NetD ERROR:  UNKNOWN ERROR when fetching pilot data");
			strcpy(error_str, "An Unknown Error (probably a timeout) occured when trying to retrieve your pilot data.");
			retval = false;
			break;

		// success!!
		case 3:
			ml_printf("FS2NetD MSG:  Got Pilot data");
			retval = true;
			break;

		// success!!  pilot was created
		case 4:
			ml_printf("FS2NetD MSG:  Created New Pilot");
			strcpy(error_str, "New Pilot has been created.");
			retval = true;
			break;

		// invalid pilot name
		case 5:
			ml_printf("FS2NetD ERROR:  Invalid Pilot!");
			strcpy(error_str, "Invalid Pilot name - A serious error has occured, Contact the FS2NetD Administrator!");
			retval = false;
			break;

		// the session id was invalid
		case 6:
			ml_printf("FS2NetD ERROR:  Invalid SID!");
			strcpy(error_str, "Invalid SID - A serious error has occured, Contact the FS2NetD Administrator!");
			retval = false;
			break;

		default:
			ml_printf("FS2NetD ERROR:  Unknown return case for GetPlayerData()");
			strcpy(error_str, "Unkown return case from GetPlayerData(). Contact the FS2NetD Administrator!");
			retval = false;
			break;
	}

	if ( !Is_standalone && strlen(error_str) ) {
		popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, error_str);
	}

	if (retval) {
		Logged_in = true;
	}

	return retval;
}
int FS2NetD_GetBanList(SCP_vector<SCP_string> &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_update_valid_tables()
{
	int rc;
	int hacked = 0;

	if ( !Logged_in ) {
		return -1;
	}

	// if there are no tables to check with then bail
	if ( Table_valid_status.empty() ) {
		return -1;
	}

	// if we're a standalone, show a dialog saying "validating tables"
	if (Game_mode & GM_STANDALONE_SERVER) {
		std_create_gen_dialog("Validating tables");
		std_gen_set_text("Querying FS2NetD:", 1);
	}

	do_full_packet = true;

	In_process = true;

	if (Is_standalone) {
		do { rc = fs2netd_update_valid_tables_do(); } while (!rc);
	} else {
		rc = popup_till_condition(fs2netd_update_valid_tables_do, XSTR("&Cancel", 779), XSTR("Starting table validation", 1592));
	}

	In_process = false;
	Local_timeout = -1;

	switch (rc) {
		// canceled by popup
		case 0:
			return -1;

		// timed out
		case 1: {
			if ( !Is_standalone ) {
				popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("Table validation timed out!", 1593));
			}

			return -1;
		}

		// no tables
		case 2: {
			if ( !Is_standalone ) {
				popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR("No tables are available from the server for validation!", 1594));
			}

			return -1;
		}
	}

	// output the status of table validity to multi.log
	for (SCP_vector<crc_valid_status>::iterator tvs = Table_valid_status.begin(); tvs != Table_valid_status.end(); ++tvs) {
		if (tvs->valid) {
			ml_printf("FS2NetD Table Check: '%s' -- Valid!", tvs->name);
		} else {
			ml_printf("FS2NetD Table Check: '%s' -- INVALID (0x%x)!", tvs->name, tvs->crc32);
			hacked = 1;
		}
	}

	// if we're a standalone, kill the validate dialog
	if (Game_mode & GM_STANDALONE_SERVER) {
		std_destroy_gen_dialog();
	}

	return hacked;
}
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<crc_valid_status>::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<crc_valid_status>::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_options_config_init()
{
	if (PXO_options_loaded) {
		return;
	}

	if ( !strlen(Multi_options_g.game_tracker_ip) ) {
		ml_printf("NOTICE: Address for game tracker not specified, using default instead (%s).", FS2NETD_DEFAULT_SERVER);
		strncpy( Multi_options_g.game_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN );
	} else if ( !strcmp("gt.pxo.net", Multi_options_g.game_tracker_ip) ) {
		ml_printf("NOTICE: Incompatible game tracker IP detected (gt.pxo.net), using default instead (%s)!", FS2NETD_DEFAULT_SERVER);
		strncpy( Multi_options_g.game_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN );
	}

	if ( !strlen(Multi_options_g.user_tracker_ip) ) {
		ml_printf("NOTICE: Address for user tracker not specified, using default instead (%s).", FS2NETD_DEFAULT_SERVER);
		strncpy( Multi_options_g.user_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN );
	} else if ( !strcmp("ut.pxo.net", Multi_options_g.user_tracker_ip) ) {
		ml_printf("NOTICE: Incompatible user tracker IP detected (ut.pxo.net), using default instead (%s)!", FS2NETD_DEFAULT_SERVER);
		strncpy( Multi_options_g.user_tracker_ip, FS2NETD_DEFAULT_SERVER, MULTI_OPTIONS_STRING_LEN );
	}

	if ( !strlen(Multi_options_g.tracker_port) ) {
		if ( FS2NetD_port >= 1024 && FS2NetD_port <= USHRT_MAX ) {
			ml_printf("NOTICE: User override for game/user tracker port not specified, using game_settings.tbl override (%i).", FS2NetD_port);
			int result;
			result = sprintf(Multi_options_g.tracker_port, "%i", FS2NetD_port);
			Assertion( result > 0, "Copying port %i to tracker_port failed\n", FS2NetD_port );
		}
		else {
			if ( FS2NetD_port != 0 ) {
				ml_printf("ERROR: game_settings.tbl override for game/user tracker port '%i' must be between %i and %i.", FS2NetD_port, 1024, USHRT_MAX);
			}
			ml_printf("NOTICE: Port for game/user trackers not specified, using default instead (%s).", FS2NETD_DEFAULT_PORT);
			strncpy( Multi_options_g.tracker_port, FS2NETD_DEFAULT_PORT, STD_NAME_LEN );
		}
	} else {
		long port_tmp = strtol(Multi_options_g.tracker_port, (char**)NULL, 10);

		if ( (port_tmp < 1024) || (port_tmp > USHRT_MAX) ) {
			ml_printf("NOTICE: The port specified for game/user trackers, '%ld', is outside of the required range, %i through %i!", port_tmp, 1024, USHRT_MAX);
			ml_printf("NOTICE: Port for game/user trackers is invalid, using default instead (%s).", FS2NETD_DEFAULT_PORT);
			strncpy( Multi_options_g.tracker_port, FS2NETD_DEFAULT_PORT, STD_NAME_LEN );
		}
	}

	if ( !strlen(Multi_options_g.pxo_ip) ) {
		ml_printf("NOTICE: Address for chat server not specified, using default instead (%s).", FS2NETD_DEFAULT_CHAT_SERVER);
		strncpy( Multi_options_g.pxo_ip, FS2NETD_DEFAULT_CHAT_SERVER, MULTI_OPTIONS_STRING_LEN );
	} else if ( !strcmp("chat.pxo.net", Multi_options_g.pxo_ip) ) {
		ml_printf("NOTICE: Incompatible chat server IP detected (chat.pxo.net), using default instead (%s)!", FS2NETD_DEFAULT_CHAT_SERVER);
		strncpy( Multi_options_g.pxo_ip, FS2NETD_DEFAULT_CHAT_SERVER, MULTI_OPTIONS_STRING_LEN );
	}

	if ( !strlen(Multi_options_g.pxo_banner_url) ) {
		ml_printf("NOTICE: URL for banners not specified, using default instead (%s).", FS2NETD_DEFAULT_BANNER_URL);
		strncpy( Multi_options_g.pxo_banner_url, FS2NETD_DEFAULT_BANNER_URL, MULTI_OPTIONS_STRING_LEN );
	} else if ( !strcmp("http://www.pxo.net/files/banners", Multi_options_g.pxo_banner_url) ) {
		ml_printf("NOTICE: Incompatible banner URL detected (chat.pxo.net), using default instead (%s)!", FS2NETD_DEFAULT_BANNER_URL);
		strncpy( Multi_options_g.pxo_banner_url, FS2NETD_DEFAULT_BANNER_URL, MULTI_OPTIONS_STRING_LEN );
	}

	PXO_options_loaded = true;
}