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;
}
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_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_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;
}
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;
			}
		}
	}
}
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);
		}
	}
}