/* * Descent 3 * Copyright (C) 2024 Parallax Software * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . --- HISTORICAL COMMENTS FOLLOW --- * $Logfile: /DescentIII/Main/networking/networking.cpp $ * $Revision: 1.7 $ * $Date: 2004/12/05 04:38:23 $ * $Author: ryan $ * * * * $Log: networking.cpp,v $ * Revision 1.7 2004/12/05 04:38:23 ryan * Disable IPX support. * * Revision 1.6 2004/12/05 04:00:20 ryan * MacOS X patches. * * Revision 1.5 2004/02/25 00:04:06 ryan * Removed loki_utils dependency and ported to MacOS X (runs, but incomplete). * * Revision 1.4 2001/01/13 21:48:46 icculus * patched to (re)compile on win32. * * Revision 1.3 2000/06/29 06:41:24 icculus * mad commits. * * Revision 1.2 2000/06/24 01:15:15 icculus * patched to compile. * * Revision 1.1.1.1 2000/04/18 00:00:41 icculus * initial checkin * * * 116 1/30/00 3:33p Jason * Expanded network receive buffer * * 115 10/21/99 3:01p Kevin * Macintosh willdial fixes * * 113 9/17/99 2:44p Kevin * preferred net buffer size == 16k * * 112 8/31/99 5:30p Jason * network statistics code * * 111 8/24/99 2:10p Kevin * Some debugging & Macintosh fixes * * 110 8/23/99 5:15p Kevin * Macintosh Byte ordering fixes * * 109 8/21/99 3:14a Jeff * real async lookup in Linux * * 108 8/10/99 11:00a Kevin * decreased receive buffer size * * 107 8/02/99 10:24p Kevin * fixed some mac stuff * * 106 7/29/99 10:33a Kevin * More Mac * * 105 7/26/99 12:05p Kevin * Macintosh stuff * * 104 5/23/99 5:55p Jeff * mprintf for easy debugging * * 103 5/19/99 5:21p Kevin * changed return value of nw_GetHostAddressFromNumbers() * * 102 5/19/99 1:12p Kevin * fixed an mprintf and added a saftey check * * 101 5/06/99 3:27a Kevin * directplay fixes * * 100 4/30/99 5:07p Kevin * misc dedicated server, networking and low memory enhancements * * 99 4/25/99 3:19p Kevin * removed really bad call to Error() when the reliable buffer overflows. * It is possible for this to happen when one client disconnects and * shouldn't cause the server to shutdown. * * 98 4/22/99 6:32p Kevin * Hopefully fixed linux gehostbyname problem * * 97 4/19/99 7:57p Jeff * pack structures for Linux version * * 96 4/19/99 4:02a Jeff * fixed select calls for Linux * * 95 4/17/99 5:56a Jeff * ported to Linux * * 94 4/15/99 2:58a Jeff * added some defines and includes needed for linux ipx * * 93 4/14/99 1:38a Jeff * fixed case mismatched #includes * * 92 4/12/99 3:16p Kevin * Added a bunch of stuff for the linux version * * 91 3/08/99 3:59p Kevin * Idle processing of network packets fixed * * 90 2/24/99 12:48p Kevin * Added urgent flag to nw_SendReliable. It causes the packet to go out * that frame. * * 89 2/23/99 3:09p Jason * changed queue time to tenth of a second, as a test. 4 tenths of a * second might be too long, especially on a LAN. * * 88 2/08/99 11:12p Kevin * Fixed ip selection & RAS code * * 87 2/08/99 4:08p Kevin * Fixed a stupid bug I introduced last week * * 86 2/05/99 7:24p Kevin * Added NAGLE type packet buffering to reliable networking code * * 85 1/26/99 9:45p Jeff * moved tcplog from networking to mono lib * * 84 1/08/99 4:51p Jeff * don't put the null char at the end of the buffer when sending the * TCPPrintf * * 83 1/08/99 2:58p Kevin * Added TCP mprintf support so you can log to a remote machine. * * 82 1/07/99 11:51a Kevin * Added support for joining servers on alternate ports and hosting behind * a proxy/firewall * * 81 1/06/99 3:08p Jason * changed recommended pps * * 80 1/04/99 12:22p Jeff * commented out assert no longer needed (nw_GetHostAddressFromNumbers) * * 79 12/28/98 10:02a Kevin * removed mprintfs * * 78 12/23/98 6:38p Kevin * All UDP data (except gamespy) now uses one (registered) port number * * 77 12/14/98 3:07p Kevin * Added +ip to specify IP to bind to (gamespy command line arg) * * 76 12/03/98 9:29a Kevin * Added better ip selection code * * 75 11/06/98 4:46p Jason * added runtime error for being unable to send reliable packet * * 74 10/29/98 5:08p Jason * added an Int3 for when the unthinkable actually happens * * 73 10/19/98 7:51p Kevin * performance testing * * 72 10/16/98 11:55a Kevin * Made dlls loadable in a hog * * 71 10/14/98 7:20p Kevin * More dsp changes... * * 70 10/12/98 8:39p Kevin * removed mprintf's and fixed some smallish bugs * * 69 10/12/98 4:10p Kevin * Made sockets reuasable so if the game locks you don't have to reboot * * 68 10/08/98 12:30p Nate * For Kevin. Fixed stupidity bug I just introduced * * 67 10/08/98 9:59a Kevin * made change to async gethostbyname * * 66 10/06/98 6:07p Jason * added protective layer for reliable packets * * 65 9/30/98 10:36a Kevin * Added command line launching of URLs and command line directory * specification * * 64 9/28/98 4:22p Kevin * Redesigned game list menus * * 63 9/28/98 11:02a Kevin * added Networking defer, and fixed some UI issues * * 62 9/28/98 9:53a Kevin * Fixing misc UI problems, and fixed some bugs that VC 6 found * * 61 9/22/98 2:29p Kevin * moved ships allowed code out of dll and into main app. Also added * powerup exclusions * * 60 9/04/98 1:51p Kevin * implemented asyncronous gethostbyname * * 59 9/03/98 11:12a Kevin * fixed bug related to the size of reliable packet header... * * 58 8/31/98 10:15a Kevin * Misc. multi-UI fixes * * 57 8/24/98 5:04p Kevin * Made msn files have the option to not be playable in multiplayer * * 56 8/24/98 10:55a Kevin * new directplay stuff * * 55 8/19/98 11:50a Kevin * Got DirectPlay IPX working, and localized connection DLLs * * 54 8/17/98 11:00a Kevin * Moved DLLs into subdirectories * * 53 8/14/98 4:54p Kevin * More directplay stuff * * 52 8/13/98 6:32p Kevin * Initial implementation of directplay API * * 51 8/11/98 11:18a Kevin * Cleaned up unused code and unused function * * 50 8/11/98 10:46a Kevin * removed TCPIP only code from reliable code * * 49 8/11/98 10:28a Kevin * Added a bunch of return code checking to help throttle bandwidth * * 48 8/10/98 4:32p Kevin * added RAS code to correctly Identify IP address * * 47 8/10/98 12:56p Kevin * Removed blocking writes * * 46 8/07/98 7:02p Kevin * * 45 8/07/98 7:02p Kevin * dumb bug fix for below * * 44 8/07/98 6:44p Kevin * made unreliable sockets non blocking * * 43 8/05/98 2:25p Kevin * improved network address reporting * * 42 8/05/98 12:07p Kevin * minor fixes to reliable * * 41 7/29/98 6:11p Kevin * took out sequence hack * * 40 7/29/98 5:31p Kevin * added rcs header * */ #ifdef WIN32 #include #include #include #include #include #include "directplay.h" #include "dplay.h" #include "dplobby.h" #include "module.h" typedef int socklen_t; #endif #include #include #if defined(POSIX) #if defined(__LINUX__) #include #endif #include #include #include #include #include #include #include #define TRUE true #define FALSE false #define LPSTR char * #endif #include "descent.h" #include "appdatabase.h" #include "pserror.h" #include "log.h" #include "networking.h" #include "ddio.h" #include "mem.h" #include "game.h" #include "args.h" #include "byteswap.h" #include "ctlconfig.h" #ifndef WIN32 bool Use_DirectPlay = false; #endif #define MAX_CONNECT_TRIES 50 #define MAX_RECEIVE_BUFSIZE 32768 //(1<<16) // 16 K, eh? int iMaxReceiveBufsize = MAX_RECEIVE_BUFSIZE; #ifdef WIN32 DWORD(DLLFUNCCALL *pRasEnumConnections)(LPRASCONN lprasconn, LPDWORD lpcb, LPDWORD lpcConnections) = NULL; DWORD(DLLFUNCCALL *pRasGetConnectStatus)(HRASCONN hrasconn, LPRASCONNSTATUS lprasconnstatus) = NULL; DWORD(DLLFUNCCALL *pRasGetProjectionInfo) (HRASCONN hrasconn, RASPROJECTION rasprojection, LPVOID lpprojection, LPDWORD lpcb) = NULL; #endif int Dialup_connection = 0; int nw_ServerSocket = -1; int nw_ClientSocket = -1; network_protocol NetworkProtocol = NP_NONE; int Sockets_initted = 0; int Network_initted = 0; uint32_t Net_fixed_ip = INADDR_NONE; // sockets for IPX and TCP SOCKET TCP_socket; SOCKET TCP_reliable_socket; SOCKET TCP_listen_socket; // the sockets that the game will use when selecting network type static SOCKET *Unreliable_socket; static SOCKET *Reliable_socket; static SOCKET *Listen_socket; // our current running statistics static tNetworkStatus NetStatistics; BOOL TCP_active = FALSE; BOOL IPX_active = FALSE; BOOL DP_active = FALSE; // Direct Play active // BOOL TCP_MT_active = FALSE; // This structure contains the local computer info network_address My_addr; struct network_checksum_packet { int sequence_number; uint16_t flags; uint16_t checksum; uint8_t data[MAX_PACKET_SIZE]; }; // definition for a non-checksum packet struct network_naked_packet { int sequence_number; uint16_t flags; uint8_t data[MAX_PACKET_SIZE]; }; // structure definition for our packet buffers struct network_packet_buffer { int sequence_number; int len; network_address from_addr; uint8_t data[MAX_PACKET_SIZE]; }; #define MAX_PACKET_BUFFERS 96 static network_packet_buffer Packet_buffers[MAX_PACKET_BUFFERS]; // buffer to hold packets sent to us static int16_t Packet_free_list[MAX_PACKET_BUFFERS]; // contains id's of free packet buffers static int Num_packet_buffers; static int Largest_packet_index = 0; int Next_packet_id; int Last_packet_id; static CFILE *NetDebugFile = NULL; // An array of callbacks static NetworkReceiveCallback Netcallbacks[16]; #define R_NET_SEQUENCE_NONE 0 #define R_NET_SEQUENCE_CONNECTING 1 #define R_NET_SEQUENCE_CONNECTED 2 #define R_NET_SEQUENCE_FAILED 3 #define R_NET_PACKET_QUEUE_TIME .1f static int Net_connect_socket_id = INVALID_SOCKET; static int Net_connect_sequence = R_NET_SEQUENCE_NONE; // ------------------------------------------------------------------------------------------------------ // PACKET BUFFERING FUNCTIONS // // a sequence number of -1 will indicate that this packet is not valid static network_packet_buffer Psnet_buffers[MAX_PACKET_BUFFERS]; static int Psnet_seq_number = 0; static int Psnet_lowest_id = 0; static int Psnet_highest_id = 0; // Reliable UDP stuff //******************************* #ifdef WIN32 #pragma pack(push, r_udp) #endif #pragma pack(1) struct reliable_header { uint8_t type; // packet type uint8_t compressed; // uint16_t seq; // sequence packet 0-65535 used for ACKing also uint16_t data_len; // length of data float send_time; // Time the packet was sent, if an ACK the time the packet being ACK'd was sent. uint8_t data[NETBUFFERSIZE]; // Packet data }; #define RELIABLE_PACKET_HEADER_ONLY_SIZE (sizeof(reliable_header) - NETBUFFERSIZE) #define MAX_PING_HISTORY 10 struct reliable_net_sendbuffer { uint8_t buffer[NETBUFFERSIZE]; }; struct reliable_net_rcvbuffer { uint8_t buffer[NETBUFFERSIZE]; }; static float first_sent_iamhere = 0; static float last_sent_iamhere = 0; static uint32_t serverconn = 0xFFFFFFFF; #ifdef WIN32 #pragma pack(pop, r_udp) #else #pragma pack() #endif struct reliable_socket { float timesent[MAXNETBUFFERS]; int16_t send_len[MAXNETBUFFERS]; int16_t recv_len[MAXNETBUFFERS]; float last_packet_received; // For a given connection, this is the last packet we received float last_packet_sent; float pings[MAX_PING_HISTORY]; uint32_t num_ping_samples; float mean_ping; float last_sent; // The last time we sent a packet (used for NAGLE emulation) int waiting_packet_number; // Which packet has data in it that is waiting for the interval to send uint16_t status; // Status of this connection uint16_t oursequence; // This is the next sequence number the application is expecting uint16_t theirsequence; // This is the next sequence number the peer is expecting uint16_t rsequence[MAXNETBUFFERS]; // This is the sequence number of the given packet uint8_t ping_pos; network_address net_addr; // A D3 network address structure network_protocol connection_type; // IPX, IP, modem, etc. reliable_net_rcvbuffer *rbuffers[MAXNETBUFFERS]; SOCKADDR addr; // SOCKADDR of our peer reliable_net_sendbuffer *sbuffers[MAXNETBUFFERS]; // This is an array of pointers for quick sorting uint16_t ssequence[MAXNETBUFFERS]; // This is the sequence number of the given packet uint8_t send_urgent; }; static reliable_socket reliable_sockets[MAXRELIABLESOCKETS]; //******************************* #if defined(POSIX) #include #endif int make_nonblocking(SOCKET sock) { #ifdef WIN32 unsigned long arg = 1; return ioctlsocket(sock, FIONBIO, &arg); #else // WIN32 return fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK); #endif } void CloseNetworking() { if (Sockets_initted != 1) return; if (NetDebugFile) { cfprintf(NetDebugFile, "NetDebug.log closed at %f seconds.\n", timer_GetTime()); cfclose(NetDebugFile); NetDebugFile = NULL; } LOG_DEBUG << "Shutting down networking..."; #ifdef WIN32 WSACancelBlockingCall(); dp_ShutdownDirectPlay(); #endif if (TCP_socket != INVALID_SOCKET) { shutdown(TCP_socket, 1); #ifdef WIN32 closesocket(TCP_socket); #else close(TCP_socket); #endif } #ifdef WIN32 if (WSACleanup()) { LOG_ERROR << "Error closing wsock!"; } #endif Network_initted = 0; Sockets_initted = 0; NetworkProtocol = NP_NONE; } // Inits the sockets layer to activity void nw_InitNetworking(int iReadBufSizeOverride) { if (iReadBufSizeOverride == -1) { iMaxReceiveBufsize = MAX_RECEIVE_BUFSIZE; } else { iMaxReceiveBufsize = iReadBufSizeOverride; } // reset our stats so we are ready to use them nw_GetNetworkStats(NULL); #ifdef WIN32 WSADATA ws_data; WORD ver = MAKEWORD(1, 1); #endif static char szconntype[100]; int len = 99; Database->read("NetworkConnection", szconntype, &len); if (stricmp(szconntype, "DIALUP") == 0) { Dialup_connection = 1; } else { Dialup_connection = 0; } int iparg; if (FindArg("-netdebug")) { NetDebugFile = (CFILE *)cfopen("netdebug.log", "wa"); ASSERT(NetDebugFile); cfprintf(NetDebugFile, "NetDebug.log opened at %f seconds\n", timer_GetTime()); } iparg = FindArg("-useip"); if (!iparg) { iparg = FindArg("+ip"); } if (iparg) { Net_fixed_ip = inet_addr(GameArgs[iparg + 1]); if (Net_fixed_ip == INADDR_NONE) { Net_fixed_ip = INADDR_NONE; } } #ifdef WIN32 if (!dp_DidLobbyLaunchGame()) { static char exewithpath[_MAX_PATH * 2]; static char exefile[_MAX_PATH * 2]; static char ourargs[_MAX_PATH * 2]; static char exedir[_MAX_PATH * 2]; static char exeext[_MAX_PATH]; static char *fixdir; // Tell direct play about this game char *p = GetCommandLine(); LOG_DEBUG.printf("Command line: %s", p); int parmlen = strlen(p); int a; for (a = 0; a < parmlen; ++a) { if (p[a] == ' ') { break; } } if (a < parmlen) { strcpy(ourargs, p + a + 1); } else { strcpy(ourargs, ""); } strncpy(exewithpath, p, a); exewithpath[a] = NULL; ddio_SplitPath(exewithpath, exedir, exefile, exeext); if (exedir[0] == '\"') { fixdir = exedir + 1; } else { fixdir = exedir; } if (exeext[strlen(exeext) - 1] == '\"') { exeext[strlen(exeext) - 1] = NULL; } strcat(exefile, exeext); // dp_RegisterLobbyApplication("Descent 3",exefile,fixdir,ourargs,Base_directory,"Descent 3"); } #endif #ifdef WIN32 int error = WSAStartup(ver, &ws_data); #else int error = 0; #endif if (error != 0) { LOG_ERROR.printf("There was an error initializing networking! Error=%d", error); return; } else { LOG_DEBUG << "Network initted successfully!"; Network_initted = 1; atexit(CloseNetworking); } } // Sets the size of buffers void nw_SetSocketOptions(SOCKET sock) { int broadcast; int ret, cursize, bufsize, trysize; socklen_t cursizesize; // Set the mode of the socket to allow broadcasting. We need to be able to broadcast // when a game is searched for in IPX mode. broadcast = 1; setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (LPSTR)&broadcast, sizeof(broadcast)); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (LPSTR)&broadcast, sizeof(broadcast)); int error = make_nonblocking(sock); if (error == SOCKET_ERROR) { LOG_ERROR.printf("Unable to make socket non-blocking -- %d", WSAGetLastError()); } // try and increase the size of my receive buffer bufsize = iMaxReceiveBufsize; // set the current size of the receive buffer cursizesize = sizeof(int); getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (LPSTR)&cursize, &cursizesize); for (trysize = bufsize; trysize >= cursize; trysize >>= 1) { ret = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (LPSTR)&trysize, sizeof(trysize)); if (ret == SOCKET_ERROR) { int wserr; wserr = WSAGetLastError(); if ((wserr == WSAENOPROTOOPT) || (wserr == WSAEINVAL)) break; } else break; } getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (LPSTR)&cursize, &cursizesize); LOG_DEBUG.printf("Receive buffer set to %d", cursize); /* // set the current size of the send buffer bufsize = MAX_RECEIVE_BUFSIZE/4; cursizesize = sizeof(int); getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (LPSTR)&cursize, &cursizesize); for ( trysize = bufsize; trysize >= cursize; trysize >>= 1 ) { ret = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (LPSTR)&trysize, sizeof(trysize)); if ( ret == SOCKET_ERROR ) { int wserr; wserr = WSAGetLastError(); if ( (wserr == WSAENOPROTOOPT) || (wserr == WSAEINVAL) ) break; } else break; } getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (LPSTR)&cursize, &cursizesize); mprintf(0, "Send buffer set to %d\n", cursize); */ } uint16_t nw_ListenPort = 0; // Inits the sockets that the application will be using void nw_InitSockets(uint16_t port) { nw_ListenPort = port; // UDP/TCP socket structure SOCKADDR_IN sock_addr; // Now do tcp! TCP_active = 0; TCP_socket = INVALID_SOCKET; // TCP_reliable_socket = INVALID_SOCKET; TCP_listen_socket = INVALID_SOCKET; // Initialize the UDP socket TCP_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Initialize all reliable sockets, IPX and TCP nw_InitReliableSocket(); if (TCP_socket != INVALID_SOCKET) { // bind the socket memset(&sock_addr, 0, sizeof(SOCKADDR_IN)); sock_addr.sin_family = AF_INET; uint32_t my_ip; my_ip = nw_GetThisIP(); memcpy(&sock_addr.sin_addr.s_addr, &my_ip, sizeof(uint32_t)); sock_addr.sin_port = htons(port); if (bind(TCP_socket, (SOCKADDR *)&sock_addr, sizeof(sock_addr)) == SOCKET_ERROR) { LOG_WARNING.printf("Couldn't bind TCP socket (%d)! Invalidating TCP", WSAGetLastError()); goto tcp_done; } nw_SetSocketOptions(TCP_socket); TCP_active = 1; } else { LOG_ERROR.printf("Cannot create TCP socket (%d)!", WSAGetLastError()); } tcp_done: int ret; uint32_t isocktrue = 1; setsockopt(TCP_socket, SOL_SOCKET, SO_REUSEADDR, (LPSTR)&isocktrue, sizeof(isocktrue)); ret = setsockopt(TCP_socket, SOL_SOCKET, SO_BROADCAST, (LPSTR)&isocktrue, sizeof(uint32_t)); if (ret == SOCKET_ERROR) { int wserr; wserr = WSAGetLastError(); if ((wserr == WSAENOPROTOOPT) || (wserr == WSAEINVAL)) { LOG_ERROR << "Unable to make socket broadcastable!"; Int3(); // Get Kevin } } Sockets_initted = 1; if (TCP_active) { LOG_DEBUG << "TCP Initialized"; } nw_psnet_buffer_init(); nw_RegisterCallback((NetworkReceiveCallback)nw_HandleUnreliableData, NWT_UNRELIABLE); } // Copies my address into the passed argument void nw_GetMyAddress(network_address *addr) { socklen_t len; SOCKADDR_IN in_addr; memset(&My_addr, 0, sizeof(network_address)); if (TCP_active) { // assign the TCP_* sockets to the socket values used elsewhere Unreliable_socket = &TCP_socket; Reliable_socket = &TCP_reliable_socket; Listen_socket = &TCP_listen_socket; // get the socket name for the TCP_socket, and put it into My_addr len = sizeof(SOCKADDR_IN); if (getsockname(*Unreliable_socket, (SOCKADDR *)&in_addr, &len) == SOCKET_ERROR) { LOG_ERROR.printf("Unable to get sock name for TCP unreliable socket (%s)", WSAGetLastError()); return; } memcpy(My_addr.address, &in_addr.sin_addr, 4); // My_addr.port = in_addr.sin_port; My_addr.port = ntohs(in_addr.sin_port); } *addr = My_addr; } // Returns internet address format from string address format...ie "204.243.217.14" // turns into 1414829242 uint32_t nw_GetHostAddressFromNumbers(char *str) { // ASSERT (NetworkProtocol==NP_TCP); return inet_addr(str); } // Fills in the string with the string address from the internet address void nw_GetNumbersFromHostAddress(network_address *address, char *str) { // ASSERT(NetworkProtocol==NP_TCP); ASSERT(str); struct in_addr addr; if (address->connection_type == NP_TCP) { memcpy(&addr, address->address, sizeof(struct in_addr)); sprintf(str, "IP: %s:%d", inet_ntoa(addr), address->port); } #ifdef WIN32 else if (Use_DirectPlay && (address->connection_type == NP_DIRECTPLAY)) { DPID id; memcpy(&id, address->address, sizeof(DPID)); sprintf(str, "DirectPlay: 0x%x", id); } #endif } #define CLOSE_TIMEOUT_TIME 3 // 3 seconds // returns the ip address of this computer uint32_t nw_GetThisIP() { SOCKADDR_IN local_address; if (Net_fixed_ip != INADDR_NONE) { return Net_fixed_ip; } // Init local address to zero local_address.sin_addr.s_addr = INADDR_ANY; if (Dialup_connection) { #ifdef WIN32 local_address.sin_addr.s_addr = psnet_ras_status(); #else local_address.sin_addr.s_addr = INADDR_ANY; #endif } if ((!Dialup_connection) || (!local_address.sin_addr.s_addr)) { // Removed the fancy way, it worked worse than the easy way (some adsl/cable people had problems. /* // Get the local host name ret = gethostname(local, 255 ); if (ret != SOCKET_ERROR ) { // Resolve host name for local address hostent = gethostbyname((LPSTR)local); if ( hostent ) local_address.sin_addr.s_addr = *((u_long FAR *)(hostent->h_addr)); } */ local_address.sin_addr.s_addr = INADDR_ANY; } return local_address.sin_addr.s_addr; } // Calculates a unique uint16_t checksum for a stream of data uint16_t nw_CalculateChecksum(void *vptr, int len) { uint8_t *ptr = (uint8_t *)vptr; uint32_t sum1, sum2; sum1 = sum2 = 0; while (len--) { sum1 += *ptr++; if (sum1 >= 255) sum1 -= 255; sum2 += sum1; } sum2 %= 255; return (uint16_t)((sum1 << 8) + sum2); } // Sends data on an unreliable socket int nw_Send(network_address *who_to, void *data, int len, int flags) { if (len == 0) { LOG_DEBUG << "Attempting to send 0 byte network packet in nw_Send()"; Int3(); } if (NetDebugFile) { uint8_t *ptr = (uint8_t *)data; cfprintf(NetDebugFile, "nw_Send packet of type %d at %f seconds.\n", ptr[0], timer_GetTime()); } return nw_SendWithID(NWT_UNRELIABLE, (uint8_t *)data, len, who_to); } // MTS: only used in this file? void nw_HandleUnreliableData(uint8_t *data, int len, network_address *from_addr) { nw_psnet_buffer_packet((uint8_t *)data, len, from_addr); } // MTS: unused? // routine to "free" a packet buffer void nw_FreePacket(int id) { Packet_buffers[id].sequence_number = -1; Packet_free_list[--Num_packet_buffers] = (int16_t)id; if (Largest_packet_index == id) while ((--Largest_packet_index > 0) && (Packet_buffers[Largest_packet_index].sequence_number == -1)) ; } // nw_Recieve will call the above function to read data out of the socket. It will then determine // which of the buffers we should use and pass to the routine which called us int nw_Receive(void *data, network_address *from_addr) { // call the routine to read data out of the socket (which stuffs it into the packet buffers) if (Use_DirectPlay) { #ifdef WIN32 dp_DirectPlayDispatch(); #endif } else { // nw_ReceiveFromSocket(); nw_DoReceiveCallbacks(); } int buffer_size; // try and get a free buffer and return its size if (nw_psnet_buffer_get_next((uint8_t *)data, &buffer_size, from_addr)) { return buffer_size; } return 0; } int ExtraBufferTempHack = 0; // Temp hack to figure out this buffer overflow thing // Return codes: //-1 socket not connected // 0 No packet ready to receive // >0 Buffer filled with the number of bytes recieved int nw_ReceiveReliable(SOCKET socketid, uint8_t *buffer, int max_len) { int i; if (Use_DirectPlay) { #ifdef WIN32 dp_DirectPlayDispatch(); // try and get a free buffer and return its size if (nw_psnet_buffer_get_next_by_packet_id((uint8_t *)buffer, &max_len, socketid)) { return max_len; } return 0; #endif } reliable_socket *rsocket = NULL; // nw_WorkReliable(); nw_DoReceiveCallbacks(); if (socketid >= MAXRELIABLESOCKETS) { LOG_ERROR.printf("Invalid socket id passed to nw_NewReceiveReliable() -- %d", socketid); return -1; } rsocket = &reliable_sockets[socketid]; if ((RNF_CONNECTED != rsocket->status) && (RNF_LIMBO != rsocket->status)) { LOG_ERROR.printf("Can't receive packet because it isn't connected in nw_ReceiveReliable(). socket = %d", socketid); return 0; } // If the buffer position is the position we are waiting for, fill in // the buffer we received in the call to this function and return true for (i = 0; i < MAXNETBUFFERS; i++) { if ((rsocket->rsequence[i] == rsocket->oursequence) && (rsocket->rbuffers[i])) { memcpy(buffer, rsocket->rbuffers[i]->buffer, rsocket->recv_len[i]); mem_free(rsocket->rbuffers[i]); rsocket->rbuffers[i] = NULL; rsocket->rsequence[i] = 0; /* mprintf(0,"Found packet for upper layer in nw_ReceiveReliable() %d bytes. seq:%d.\n", rsocket->recv_len[i], rsocket->oursequence); */ rsocket->oursequence++; return rsocket->recv_len[i]; } } return 0; } // This function will look for a control packet, indicating a // desire to establish a reliable link. // When is makes a link, it will return a SOCKET, and fill in from_addr. // if there is no waiting line, it returns -1 int nw_CheckListenSocket(network_address *from_addr) { SOCKADDR_IN *ip_addr; // UDP/TCP socket structure #ifdef WIN32 DPID id; #endif if (Use_DirectPlay) { #ifdef WIN32 // look for a pending connection for (int i = 0; i < MAX_PENDING_NEW_CONNECTIONS; i++) { if (Pending_dp_conn[i] != DPID_UNKNOWN) { memset(from_addr, 0, sizeof(network_address)); memcpy(from_addr->address, &Pending_dp_conn[i], sizeof(DPID)); from_addr->connection_type = NP_DIRECTPLAY; id = Pending_dp_conn[i]; Pending_dp_conn[i] = DPID_UNKNOWN; LOG_DEBUG << "New DirectPlay connection in nw_CheckListenSocket()."; return id; } } return -1; #endif } // nw_WorkReliable(); nw_DoReceiveCallbacks(); int i; for (i = 1; i < MAXRELIABLESOCKETS; i++) { if (reliable_sockets[i].status == RNF_CONNECTING) { reliable_sockets[i].status = RNF_CONNECTED; // memcpy(from_addr,&reliable_sockets[i].addr,sizeof(SOCKADDR)); LOG_DEBUG << "New reliable connection in nw_CheckListenSocket()."; switch (reliable_sockets[i].connection_type) { case NP_TCP: ip_addr = (SOCKADDR_IN *)&reliable_sockets[i].addr; memset(from_addr, 0x00, sizeof(network_address)); from_addr->port = ntohs(ip_addr->sin_port); from_addr->connection_type = NP_TCP; #ifdef WIN32 memcpy(from_addr->address, &ip_addr->sin_addr.S_un.S_addr, 4); #else memcpy(from_addr->address, &ip_addr->sin_addr.s_addr, 4); #endif break; default: Int3(); break; } char dbg_output[50]; nw_GetNumbersFromHostAddress(from_addr, dbg_output); LOG_DEBUG.printf("Got address from: %s", dbg_output); return i; } } return INVALID_SOCKET; } int nw_SendReliable(uint32_t socketid, uint8_t *data, int length, bool urgent) { int i; int bytesout; int use_buffer = -1; reliable_socket *rsocket; reliable_header send_header; if (length == 0) { LOG_ERROR << "Attempting to send 0 byte network packet in nw_SendReliable()"; Int3(); } // mprintf(0,"Socket id passed to nw_NewSendReliable() -- %d\n",socketid); if (NetDebugFile) { cfprintf(NetDebugFile, "nw_SendReliable packet of type %d at %f seconds.\n", data[0], timer_GetTime()); } if (Use_DirectPlay) { #ifdef WIN32 network_address who_to; who_to.connection_type = NP_DIRECTPLAY; memcpy(&who_to.address, &socketid, sizeof(DPID)); return dp_DirectPlaySend(&who_to, data, length, true); #endif } ASSERT(length < sizeof(reliable_header)); // nw_WorkReliable(); nw_DoReceiveCallbacks(); if (socketid >= MAXRELIABLESOCKETS) { LOG_ERROR.printf("Invalid socket id passed to nw_NewSendReliable() -- %d", socketid); return -1; } rsocket = &reliable_sockets[socketid]; if (rsocket->status != RNF_CONNECTED) { // We can't send because this isn't a connected reliable socket. LOG_ERROR.printf("Can't send packet because of status %d in nw_SendReliable(). socket = %d", rsocket->status, socketid); return -1; } if (urgent) rsocket->send_urgent = 1; // See if there is a packet waiting to be sent if (-1 != rsocket->waiting_packet_number) { int pnum = rsocket->waiting_packet_number; ASSERT(rsocket->sbuffers[pnum]); // See if there's room for this data if (sizeof(reliable_net_sendbuffer) < (rsocket->send_len[pnum] + length)) { // Send the previous packet, then use the normal code to generate a new packet LOG_DEBUG << "Pending reliable packet buffer full, sending packet now."; rsocket->waiting_packet_number = -1; use_buffer = pnum; network_address send_address; memset(&send_address, 0, sizeof(network_address)); memcpy(send_header.data, rsocket->sbuffers[pnum], rsocket->send_len[pnum]); send_header.data_len = INTEL_SHORT(rsocket->send_len[pnum]); send_header.type = RNT_DATA; send_header.send_time = INTEL_FLOAT(timer_GetTime()); send_address.connection_type = rsocket->connection_type; if (NP_TCP == rsocket->connection_type) { SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&rsocket->addr; memcpy(send_address.address, &inaddr->sin_addr, 4); send_address.port = htons(inaddr->sin_port); send_address.connection_type = NP_TCP; } // mprintf(0,"Sending reliable packet! Sequence %d\n",send_header.seq); bytesout = nw_SendWithID(NWT_RELIABLE, (uint8_t *)&send_header, RELIABLE_PACKET_HEADER_ONLY_SIZE + rsocket->send_len[use_buffer], &send_address); if ((bytesout == SOCKET_ERROR) && (WSAEWOULDBLOCK == WSAGetLastError())) { // This will cause it to try to send again next frame. (or sooner) rsocket->timesent[use_buffer] = timer_GetTime() - (NETRETRYTIME * 4); } else { rsocket->timesent[use_buffer] = timer_GetTime(); } } else { // tack this data on the end of the previous packet // mprintf(0,"Appending to delayed packet...\n"); ASSERT(rsocket->sbuffers[pnum]); memcpy(rsocket->sbuffers[pnum]->buffer + rsocket->send_len[pnum], data, length); // int msize = mem_size(rsocket->sbuffers[pnum]); rsocket->send_len[pnum] += length; return length; } } // Add the new packet to the sending list and send it. for (i = 0; i < MAXNETBUFFERS; i++) { use_buffer = -1; if (NULL == rsocket->sbuffers[i]) { // mprintf(0,"Sending in nw_SendReliable() %d bytes seq=%d.\n",length,rsocket->theirsequence); rsocket->send_len[i] = length; rsocket->sbuffers[i] = mem_rmalloc(); memcpy(rsocket->sbuffers[i]->buffer, data, length); send_header.seq = INTEL_SHORT(rsocket->theirsequence); rsocket->ssequence[i] = rsocket->theirsequence; use_buffer = i; rsocket->waiting_packet_number = i; rsocket->theirsequence++; return length; } } LOG_WARNING.printf("Can't send packet because a buffer overflow nw_SendReliable(). socket = %d", socketid); rsocket->status = RNF_BROKEN; for (i = 0; i < MAXNETBUFFERS; i++) { if (rsocket->sbuffers[i]) { LOG_DEBUG.printf("Buffer %d: %d,%d,%d,%d,%d,%d", i, rsocket->sbuffers[i]->buffer[0], rsocket->sbuffers[i]->buffer[1], rsocket->sbuffers[i]->buffer[2], rsocket->sbuffers[i]->buffer[3], rsocket->sbuffers[i]->buffer[4], rsocket->sbuffers[i]->buffer[5]); } } // Error ("Couldn't send packet because of buffer overflow!"); // Int3(); return 0; } // MTS: only used in this file int nw_InitReliableSocket() { nw_RegisterCallback((NetworkReceiveCallback)nw_WorkReliable, NWT_RELIABLE); return 1; } // MTS: only used in this file void nw_SendReliableAck(SOCKADDR *raddr, uint32_t sig, network_protocol link_type, float time_sent) { int ret; reliable_header ack_header; ack_header.type = RNT_ACK; // mprintf(0,"Sending ACK for sig %d.\n",sig); ack_header.data_len = INTEL_SHORT((int16_t)sizeof(uint32_t)); ack_header.send_time = INTEL_FLOAT(time_sent); sig = INTEL_INT(sig); memcpy(&ack_header.data, &sig, sizeof(uint32_t)); network_address send_address; memset(&send_address, 0, sizeof(network_address)); send_address.connection_type = link_type; if (NP_TCP == link_type) { SOCKADDR_IN *inaddr = (SOCKADDR_IN *)raddr; memcpy(send_address.address, &inaddr->sin_addr, 4); send_address.port = htons(inaddr->sin_port); send_address.connection_type = NP_TCP; } ret = nw_SendWithID(NWT_RELIABLE, (uint8_t *)&ack_header, RELIABLE_PACKET_HEADER_ONLY_SIZE + sizeof(uint32_t), &send_address); } void nw_DoNetworkIdle(void) { if (!Use_DirectPlay) { nw_DoReceiveCallbacks(); nw_ReliableResend(); } } #define CONNECTSEQ 0x142 // Magic number for starting a connection, just so it isn't 0 void nw_WorkReliable(uint8_t *data, int len, network_address *naddr) { int i; int16_t max_len = NETBUFFERSIZE; static reliable_header rcv_buff; static SOCKADDR rcv_addr; uint32_t rcvid; // The id of who we actually received a packet from, as opposed to socketid parm if (NP_TCP == naddr->connection_type) { SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&rcv_addr; memcpy(&inaddr->sin_addr, &naddr->address, 4); inaddr->sin_port = htons(naddr->port); } if (Net_connect_sequence == R_NET_SEQUENCE_CONNECTING) { nw_HandleConnectResponse(data, len, naddr); return; } // Check for incoming data reliable_socket *rsocket = NULL; // Check to see if we need to send a packet out. if ((serverconn != -1) && (reliable_sockets[serverconn].status == RNF_LIMBO) && ((timer_GetTime() - last_sent_iamhere) > NETRETRYTIME)) { reliable_header conn_header; // Now send I_AM_HERE packet conn_header.type = RNT_I_AM_HERE; conn_header.seq = INTEL_SHORT((int16_t)(~CONNECTSEQ)); conn_header.data_len = INTEL_SHORT((int16_t)0); last_sent_iamhere = timer_GetTime(); network_address send_address; memset(&send_address, 0, sizeof(network_address)); send_address.connection_type = reliable_sockets[serverconn].connection_type; if (NP_TCP == send_address.connection_type) { SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&reliable_sockets[serverconn].addr; memcpy(send_address.address, &inaddr->sin_addr, 4); send_address.port = htons(inaddr->sin_port); send_address.connection_type = NP_TCP; } int ret = nw_SendWithID(NWT_RELIABLE, (uint8_t *)&conn_header, RELIABLE_PACKET_HEADER_ONLY_SIZE, &send_address); if ((ret == SOCKET_ERROR) && (WSAEWOULDBLOCK == WSAGetLastError())) { reliable_sockets[serverconn].last_packet_sent = timer_GetTime() - NETRETRYTIME; } else { reliable_sockets[serverconn].last_packet_sent = timer_GetTime(); } } network_protocol link_type = naddr->connection_type; network_address d3_rcv_addr; memcpy(&d3_rcv_addr, naddr, sizeof(network_address)); memcpy((uint8_t *)&rcv_buff, data, len); SOCKADDR_IN *rcvaddr, *rsockaddr; do { rsocket = NULL; if (len) { // Someone wants to connect, so find a slot if (rcv_buff.type == RNT_REQ_CONN) { for (i = 1; i < MAXRELIABLESOCKETS; i++) { if ((reliable_sockets[i].status == RNF_CONNECTED) || (reliable_sockets[i].status == RNF_LIMBO)) // if(memcmp(&rcv_addr,&reliable_sockets[i].addr,sizeof(SOCKADDR))==0) if (memcmp(&d3_rcv_addr, &reliable_sockets[i].net_addr, sizeof(network_address)) == 0) // d3_rcv_addr { // We already have a reliable link to this user, so we will ignore it... LOG_WARNING.printf("Received duplicate connection request. %d", i); // reliable_sockets[i].last_packet_received = timer_GetTime(); nw_SendReliableAck(&reliable_sockets[i].addr, INTEL_SHORT(rcv_buff.seq), link_type, INTEL_FLOAT(rcv_buff.send_time)); // We will change this as a hack to prevent later code from hooking us up rcv_buff.type = 0xff; continue; } } for (i = 1; i < MAXRELIABLESOCKETS; i++) { if (reliable_sockets[i].status == RNF_UNUSED) { // Add the new connection here. reliable_sockets[i].connection_type = link_type; memcpy(&reliable_sockets[i].net_addr, naddr, sizeof(network_address)); memcpy(&reliable_sockets[i].addr, &rcv_addr, sizeof(SOCKADDR)); reliable_sockets[i].ping_pos = 0; reliable_sockets[i].num_ping_samples = 0; reliable_sockets[i].status = RNF_LIMBO; reliable_sockets[i].last_packet_received = timer_GetTime(); reliable_sockets[i].last_sent = timer_GetTime(); reliable_sockets[i].waiting_packet_number = -1; reliable_sockets[i].send_urgent = 0; rsocket = &reliable_sockets[i]; rcvaddr = (SOCKADDR_IN *)&rcv_addr; // Int3(); LOG_DEBUG.printf("Connect from %s:%d", inet_ntoa(rcvaddr->sin_addr), htons(rcvaddr->sin_port)); break; } } if (i == MAXRELIABLESOCKETS) { // No more connections! LOG_WARNING << "Out of incoming reliable connection sockets"; // Int3();//See Kevin continue; } nw_SendReliableAck(&rsocket->addr, INTEL_SHORT(rcv_buff.seq), link_type, INTEL_FLOAT(rcv_buff.send_time)); } // Find out if this is a packet from someone we were expecting a packet. rcvaddr = (SOCKADDR_IN *)&rcv_addr; for (i = 1; i < MAXRELIABLESOCKETS; i++) { rsockaddr = (SOCKADDR_IN *)&reliable_sockets[i].addr; if (memcmp(&d3_rcv_addr, &reliable_sockets[i].net_addr, sizeof(network_address)) == 0) { rsocket = &reliable_sockets[i]; rcvid = i; break; } } if (NULL == rsocket) { LOG_WARNING << "Received reliable data from unconnected client."; char addrstr[200]; nw_GetNumbersFromHostAddress(&d3_rcv_addr, addrstr); LOG_DEBUG.printf("Received from %s", addrstr); continue; } rsocket->last_packet_received = timer_GetTime(); if (rsocket->status != RNF_CONNECTED) { // Get out of limbo if (rsocket->status == RNF_LIMBO) { // this is our connection to the server if ((serverconn != -1)) { if (rcv_buff.type == RNT_ACK) { int *acknum = (int *)&rcv_buff.data; *acknum = INTEL_INT(*acknum); if (*acknum == (~CONNECTSEQ & 0xffff)) { rsocket->status = RNF_CONNECTED; LOG_WARNING << "Got ACK for IAMHERE!"; } continue; } } else if (rcv_buff.type == RNT_I_AM_HERE) { rsocket->status = RNF_CONNECTING; nw_SendReliableAck(&rsocket->addr, INTEL_SHORT(rcv_buff.seq), link_type, INTEL_FLOAT(rcv_buff.send_time)); LOG_WARNING << "Got IAMHERE!"; continue; } } if ((rcv_buff.type == RNT_DATA) && (serverconn != -1)) { rsocket->status = RNF_CONNECTED; } else { // mprintf(0,"Packet from nonconnected socket -- seq: %d status: %d\n",rcv_buff.seq,rsocket->status); rsocket->last_packet_received = timer_GetTime(); continue; } } // Update the last recv variable so we don't need a heartbeat rsocket->last_packet_received = timer_GetTime(); if (rcv_buff.type == RNT_HEARTBEAT) { continue; } if (rcv_buff.type == RNT_ACK) { // Update ping time rsocket->num_ping_samples++; rsocket->pings[rsocket->ping_pos] = rsocket->last_packet_received - INTEL_FLOAT(rcv_buff.send_time); // mprintf(0,"ping time: %f\n",rsocket->pings[rsocket->ping_pos]); if (rsocket->num_ping_samples >= MAX_PING_HISTORY) { float sort_ping[MAX_PING_HISTORY]; for (int a = 0; a < MAX_PING_HISTORY; a++) sort_ping[a] = rsocket->pings[a]; qsort(sort_ping, MAX_PING_HISTORY, sizeof(float), nw_PingCompare); rsocket->mean_ping = ((sort_ping[MAX_PING_HISTORY / 2] + sort_ping[(MAX_PING_HISTORY / 2) + 1])) / 2; } rsocket->ping_pos++; if (rsocket->ping_pos >= MAX_PING_HISTORY) { rsocket->ping_pos = 0; } for (i = 0; i < MAXNETBUFFERS; i++) { uint32_t *acksig = (uint32_t *)&rcv_buff.data; *acksig = INTEL_INT(*acksig); if (rsocket) if (rsocket->sbuffers[i]) if (rsocket->ssequence[i] == *acksig) { // mprintf(0,"Received ACK %d\n",*acksig); mem_free(rsocket->sbuffers[i]); rsocket->sbuffers[i] = NULL; rsocket->ssequence[i] = 0; } } // remove that packet from the send buffer rsocket->last_packet_received = timer_GetTime(); continue; } if (rcv_buff.type == RNT_DATA_COMP) { // More2Come // Decompress it. Put it back in the buffer. Process it as RNT_DATA rcv_buff.type = RNT_DATA; } if (rcv_buff.type == RNT_DATA) { // If the data is out of order by >= MAXNETBUFFERS-1 ignore that packet for now int seqdelta; seqdelta = INTEL_SHORT(rcv_buff.seq) - rsocket->oursequence; if (seqdelta < 0) seqdelta = seqdelta * -1; if (seqdelta >= MAXNETBUFFERS - 1) { LOG_WARNING << "Received reliable packet out of order!"; // It's out of order, so we won't ack it, which will mean we will get it again soon. continue; } // else move data into the proper buffer position int savepacket = 1; if (rsocket->oursequence < (0xffff - (MAXNETBUFFERS - 1))) { if (rsocket->oursequence > INTEL_SHORT(rcv_buff.seq)) { LOG_WARNING.printf("Received old packet with seq of %d", INTEL_SHORT(rcv_buff.seq)); savepacket = 0; } } else { // Sequence is high, so prepare for wrap around if (((uint16_t)(INTEL_SHORT(rcv_buff.seq) + rsocket->oursequence)) > (MAXNETBUFFERS - 1)) { LOG_WARNING.printf("Received old packet with seq of %d", INTEL_SHORT(rcv_buff.seq)); savepacket = 0; } } for (i = 0; i < MAXNETBUFFERS; i++) { if ((NULL != rsocket->rbuffers[i]) && (rsocket->rsequence[i] == INTEL_SHORT(rcv_buff.seq))) { // Received duplicate packet! LOG_WARNING << "Received duplicate packet!"; savepacket = 0; } } if (savepacket) { for (i = 0; i < MAXNETBUFFERS; i++) { if (NULL == rsocket->rbuffers[i]) { // mprintf(0,"Got good data seq: %d\n",rcv_buff.seq); if (INTEL_SHORT(rcv_buff.data_len) > max_len) rsocket->recv_len[i] = max_len; // INTEL_SHORT(rcv_buff.data_len); else rsocket->recv_len[i] = INTEL_SHORT(rcv_buff.data_len); rsocket->rbuffers[i] = mem_rmalloc(); memcpy(rsocket->rbuffers[i]->buffer, rcv_buff.data, rsocket->recv_len[i]); rsocket->rsequence[i] = INTEL_SHORT(rcv_buff.seq); // mprintf(0,"Adding packet to receive buffer in nw_ReceiveReliable().\n"); break; } } } nw_SendReliableAck(&rsocket->addr, INTEL_SHORT(rcv_buff.seq), link_type, INTEL_FLOAT(rcv_buff.send_time)); } } } while (0); // while((IPX_has_data>0) || (UDP_has_data>0)); } // MTS: only used in this file void nw_HandleConnectResponse(uint8_t *data, int len, network_address *server_addr) { int i; static reliable_header ack_header; static reliable_header conn_header; SOCKADDR rcv_addr; memcpy(&ack_header, data, len); if (NP_TCP == server_addr->connection_type) { SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&rcv_addr; memcpy(&inaddr->sin_addr, &server_addr->address, 4); inaddr->sin_port = htons(server_addr->port); } LOG_DEBUG << "Got a connect response!"; if (ack_header.type == RNT_ACK) { int *acknum = (int *)&ack_header.data; if (INTEL_INT(*acknum) == CONNECTSEQ) { // if(memcmp(&rcv_addr,&sockaddr,sizeof(SOCKADDR))==0) { for (i = 1; i < MAXRELIABLESOCKETS; i++) { if (reliable_sockets[i].status == RNF_UNUSED) { // Add the new connection here. memset(&reliable_sockets[i], 0, sizeof(reliable_socket)); reliable_sockets[i].connection_type = server_addr->connection_type; memcpy(&reliable_sockets[i].net_addr, server_addr, sizeof(network_address)); reliable_sockets[i].last_packet_received = timer_GetTime(); memcpy(&reliable_sockets[i].addr, &rcv_addr, sizeof(SOCKADDR)); reliable_sockets[i].status = RNF_LIMBO; Net_connect_socket_id = i; reliable_sockets[i].last_sent = timer_GetTime(); reliable_sockets[i].waiting_packet_number = -1; LOG_DEBUG << "Successfully connected to server in nw_ConnectToServer()."; // Now send I_AM_HERE packet conn_header.type = RNT_I_AM_HERE; conn_header.seq = INTEL_SHORT((int16_t)(~CONNECTSEQ)); conn_header.data_len = INTEL_SHORT((int16_t)0); serverconn = i; first_sent_iamhere = timer_GetTime(); last_sent_iamhere = timer_GetTime(); int rcode = nw_SendWithID(NWT_RELIABLE, (uint8_t *)&conn_header, RELIABLE_PACKET_HEADER_ONLY_SIZE, server_addr); // int rcode = sendto(typeless_sock,(char // *)&conn_header,RELIABLE_PACKET_HEADER_ONLY_SIZE,0,addr,sizeof(SOCKADDR)); if (rcode == SOCKET_ERROR) { Net_connect_socket_id = INVALID_SOCKET; reliable_sockets[i].status = RNF_UNUSED; memset(&reliable_sockets[i], 0, sizeof(reliable_socket)); LOG_WARNING << "Unable to send packet in nw_ConnectToServer()"; Net_connect_sequence = R_NET_SEQUENCE_FAILED; return; } reliable_sockets[i].last_packet_sent = timer_GetTime(); /* float f; f = timer_GetTime(); while(((timer_GetTime() - f)<2) && (reliable_sockets[i].status!=RNF_CONNECTING)) { //nw_WorkReliable(); nw_DoReceiveCallbacks(); } */ Net_connect_sequence = R_NET_SEQUENCE_CONNECTED; return; } } LOG_WARNING << "Out of reliable socket space in nw_ConnectToServer()."; Net_connect_sequence = R_NET_SEQUENCE_FAILED; return; } // else //{ // mprintf(0,"Received a reliable packet from a server other than the current server\n"); //} } else { LOG_WARNING << "Received out of sequence ACK in nw_ConnectToServer()."; } } else { LOG_WARNING << "Received something that isn't an ACK in nw_ConnectToServer()."; } } void nw_ConnectToServer(SOCKET *socket, network_address *server_addr) { // Send out a RNT_REQ_CONN packet, and wait for it to be acked. float time_sent_req = 0; float first_sent_req = 0; static reliable_header conn_header; static reliable_header ack_header; int bytesin; struct timeval timeout; *socket = INVALID_SOCKET; if (Use_DirectPlay) { // We need a session description to do this, so we don't use this function return; } conn_header.type = RNT_REQ_CONN; conn_header.seq = INTEL_SHORT((int16_t)CONNECTSEQ); conn_header.data_len = 0; timeout.tv_sec = 0; timeout.tv_usec = 0; if ((server_addr->connection_type == NP_TCP) && (!TCP_active)) { return; } Net_connect_sequence = R_NET_SEQUENCE_CONNECTING; memset(&ack_header, 0, sizeof(reliable_header)); bytesin = 0; network_address d3_rcv_addr; memset(&d3_rcv_addr, 0, sizeof(network_address)); int ret = nw_SendWithID(NWT_RELIABLE, (uint8_t *)&conn_header, RELIABLE_PACKET_HEADER_ONLY_SIZE, server_addr); if (SOCKET_ERROR == ret) { LOG_WARNING.printf("Unable to send packet in nw_ConnectToServer()! -- %d", WSAGetLastError()); return; } first_sent_req = timer_GetTime(); time_sent_req = timer_GetTime(); // Wait until we get a response from the server or we timeout do { nw_DoReceiveCallbacks(); // Now we wait for the connection to be made.... if (Net_connect_sequence == R_NET_SEQUENCE_CONNECTED) { *socket = Net_connect_socket_id; return; } if ((timer_GetTime() - time_sent_req) > 2) { LOG_DEBUG << "Resending connect request."; int ret = nw_SendWithID(NWT_RELIABLE, (uint8_t *)&conn_header, RELIABLE_PACKET_HEADER_ONLY_SIZE, server_addr); if (ret != SOCKET_ERROR) { time_sent_req = timer_GetTime(); } else { LOG_WARNING.printf("Error sending connection request! -- %d", WSAGetLastError()); } } } while ((timer_GetTime() - first_sent_req) < NETTIMEOUT); return; } void nw_CloseSocket(SOCKET *sockp) { reliable_header diss_conn_header; #ifdef WIN32 if (DP_active) { dp_DirectPlayDestroyPlayer(*sockp); return; } #endif if (*sockp >= MAXRELIABLESOCKETS) { LOG_WARNING.printf("Invalid socket id passed to nw_NewCloseSocket() -- %d", *sockp); return; } if (reliable_sockets[*sockp].status == RNF_UNUSED) { LOG_WARNING.printf("Trying to close an unused socket (%d) -- ignoring request.", *sockp); } LOG_DEBUG.printf("Closing socket %d", *sockp); // Go through every buffer and "free it up(tm)" int i; for (i = 0; i < MAXNETBUFFERS; i++) { if (reliable_sockets[*sockp].rbuffers[i]) { mem_free(reliable_sockets[*sockp].rbuffers[i]); reliable_sockets[*sockp].rbuffers[i] = NULL; reliable_sockets[*sockp].rsequence[i] = 0; } if (reliable_sockets[*sockp].sbuffers[i]) { mem_free(reliable_sockets[*sockp].sbuffers[i]); reliable_sockets[*sockp].sbuffers[i] = NULL; reliable_sockets[*sockp].rsequence[i] = 0; } } diss_conn_header.type = RNT_DISCONNECT; diss_conn_header.seq = INTEL_SHORT((int16_t)(CONNECTSEQ)); diss_conn_header.data_len = 0; if (*sockp == serverconn) serverconn = -1; network_address send_address; memset(&send_address, 0, sizeof(network_address)); send_address.connection_type = reliable_sockets[*sockp].connection_type; if (NP_TCP == reliable_sockets[*sockp].connection_type) { SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&reliable_sockets[*sockp].addr; memcpy(send_address.address, &inaddr->sin_addr, 4); send_address.port = htons(inaddr->sin_port); send_address.connection_type = NP_TCP; } nw_SendWithID(NWT_RELIABLE, (uint8_t *)&diss_conn_header, RELIABLE_PACKET_HEADER_ONLY_SIZE, &send_address); memset(&reliable_sockets[*sockp], 0, sizeof(reliable_socket)); reliable_sockets[*sockp].status = RNF_UNUSED; } int nw_CheckReliableSocket(int socknum) { // Checks to see if a socket is connected or not. if (Use_DirectPlay) { return true; } if (socknum >= MAXRELIABLESOCKETS) { LOG_WARNING.printf("Invalid socket id passed to nw_CheckReliableSocket() -- %d", socknum); return 0; } switch (reliable_sockets[socknum].status) { case RNF_UNUSED: case RNF_BROKEN: case RNF_DISCONNECTED: return 0; default: return 1; } } // MTS: only used in this file. int nw_PingCompare(const void *arg1, const void *arg2) { float *ping1 = (float *)arg1; float *ping2 = (float *)arg2; if (*ping1 == *ping2) return 0; else if (*ping1 > *ping2) return 1; else if (*ping1 < *ping2) return -1; return 0; } // MTS: only used in this file // Warning, experimental compression below, if you want to use it, talk to Kevin. Doesn't do much currently, only // reduces 0's #define COMPRESS_KEY 0xfd int nw_Compress(void *srcdata, void *destdata, int count) { int i; uint8_t *curr_src = (uint8_t *)srcdata; uint8_t *currp = (uint8_t *)destdata; for (i = 0; i < count; i++) { // Woops, we have a char that matches our compress key, so add it as its own type if (curr_src[i] == COMPRESS_KEY) { *currp = COMPRESS_KEY; currp++; //*currp = 1; // currp++; *currp = COMPRESS_KEY; currp++; } // Look for 3 in a row else if ((curr_src[i] == 0) && (curr_src[i + 1] == 0) && (curr_src[i + 2] == 0) && (i + 3 < count)) { int repeat_count = 3; *currp = COMPRESS_KEY; currp++; while ((curr_src[i] == curr_src[i + repeat_count]) && (repeat_count < 250) && (i + repeat_count < count)) { repeat_count++; } *currp = repeat_count; currp++; //*currp = curr_src[i]; // currp++; i += (repeat_count - 1); } else { *currp = curr_src[i]; currp++; } } return currp - (uint8_t *)destdata; } // MTS: only used in this file int nw_Uncompress(void *compdata, void *uncompdata, int count) { int i; int destlen = 0; uint8_t *comp_src = (uint8_t *)compdata; uint8_t *currp = (uint8_t *)uncompdata; for (i = 0; i < count; i++) { if (*comp_src == COMPRESS_KEY) { comp_src++; if (*comp_src == COMPRESS_KEY) { currp[destlen] = COMPRESS_KEY; destlen++; } else { for (int a = 0; a < (*comp_src); a++) { currp[destlen] = 0; //*(comp_src+1); destlen++; } } i++; // comp_src++; comp_src++; } else { currp[destlen] = *comp_src; destlen++; comp_src++; } } return destlen; } // MTS: only used in this file // initialize the buffering system void nw_psnet_buffer_init() { int idx; // blast the buffer clean memset(Psnet_buffers, 0, sizeof(network_packet_buffer) * MAX_PACKET_BUFFERS); // set all buffer sequence #'s to -1 for (idx = 0; idx < MAX_PACKET_BUFFERS; idx++) { Psnet_buffers[idx].sequence_number = -1; } // initialize the sequence # Psnet_seq_number = 0; Psnet_lowest_id = -1; Psnet_highest_id = -1; } // MTS: only used in this file // buffer a packet (maintain order!) void nw_psnet_buffer_packet(uint8_t *data, int length, network_address *from) { int idx; int found_buf = 0; // find the first empty packet for (idx = 0; idx < MAX_PACKET_BUFFERS; idx++) { if (Psnet_buffers[idx].sequence_number == -1) { found_buf = 1; break; } } // if we didn't find the buffer, report an overrun if (!found_buf) { LOG_WARNING << "WARNING - Buffer overrun in psnet"; } else { // copy in the data memcpy(Psnet_buffers[idx].data, data, length); Psnet_buffers[idx].len = length; memcpy(&Psnet_buffers[idx].from_addr, from, sizeof(network_address)); Psnet_buffers[idx].sequence_number = Psnet_seq_number; // keep track of the highest id# Psnet_highest_id = Psnet_seq_number++; // set the lowest id# for the first time if (Psnet_lowest_id == -1) { Psnet_lowest_id = Psnet_highest_id; } } } // MTS: only used in this file // get the index of the next packet in order! int nw_psnet_buffer_get_next_by_packet_id(uint8_t *data, int *length, uint32_t packet_id) { int idx; int found_buf = 0; // if there are no buffers, do nothing if ((Psnet_lowest_id == -1) || (Psnet_lowest_id > Psnet_highest_id)) { return 0; } // search until we find the lowest packet index id# for (idx = 0; idx < MAX_PACKET_BUFFERS; idx++) { uint32_t *thisid; thisid = (uint32_t *)&Psnet_buffers[idx].from_addr.address; // if we found the buffer if ((Psnet_buffers[idx].sequence_number == Psnet_lowest_id) && (packet_id == *thisid)) { found_buf = 1; break; } } if (!found_buf) return 0; // copy out the buffer data memcpy(data, Psnet_buffers[idx].data, Psnet_buffers[idx].len); *length = Psnet_buffers[idx].len; // now we need to cleanup the packet list // mark the buffer as free Psnet_buffers[idx].sequence_number = -1; Psnet_lowest_id++; return 1; } // MTS: only used in this file // get the index of the next packet in order! int nw_psnet_buffer_get_next(uint8_t *data, int *length, network_address *from) { int idx; int found_buf = 0; // if there are no buffers, do nothing if ((Psnet_lowest_id == -1) || (Psnet_lowest_id > Psnet_highest_id)) { return 0; } // search until we find the lowest packet index id# for (idx = 0; idx < MAX_PACKET_BUFFERS; idx++) { // if we found the buffer if (Psnet_buffers[idx].sequence_number == Psnet_lowest_id) { found_buf = 1; break; } } // at this point, we should _always_ have found the buffer ASSERT(found_buf); // copy out the buffer data memcpy(data, Psnet_buffers[idx].data, Psnet_buffers[idx].len); *length = Psnet_buffers[idx].len; memcpy(from, &Psnet_buffers[idx].from_addr, sizeof(network_address)); // now we need to cleanup the packet list // mark the buffer as free Psnet_buffers[idx].sequence_number = -1; Psnet_lowest_id++; return 1; } #ifdef WIN32 // MTS: only used in this file // functions to get the status of a RAS connection uint32_t psnet_ras_status() { int rval; DWORD size, num_connections, i; RASCONN rasbuffer[25]; HINSTANCE ras_handle; uint32_t rasip = 0; RASPPPIP projection; int Ras_connected; Ras_connected = 0; // first, call a LoadLibrary to load the RAS api ras_handle = LoadLibrary("rasapi32.dll"); if (ras_handle == NULL) { return INADDR_ANY; } pRasEnumConnections = (DWORD(__stdcall *)(LPRASCONN, LPDWORD, LPDWORD))GetProcAddress(ras_handle, "RasEnumConnectionsA"); if (!pRasEnumConnections) { FreeLibrary(ras_handle); return INADDR_ANY; } pRasGetConnectStatus = (DWORD(__stdcall *)(HRASCONN, LPRASCONNSTATUS))GetProcAddress(ras_handle, "RasGetConnectStatusA"); if (!pRasGetConnectStatus) { FreeLibrary(ras_handle); return INADDR_ANY; } pRasGetProjectionInfo = (DWORD(__stdcall *)(HRASCONN, RASPROJECTION, LPVOID, LPDWORD))GetProcAddress(ras_handle, "RasGetProjectionInfoA"); if (!pRasGetProjectionInfo) { FreeLibrary(ras_handle); return INADDR_ANY; } size = sizeof(rasbuffer); rasbuffer[0].dwSize = sizeof(RASCONN); rval = pRasEnumConnections(rasbuffer, &size, &num_connections); if (rval) { FreeLibrary(ras_handle); return INADDR_ANY; } // JAS: My computer gets to this point, but I have no RAS connections, // so just exit if (num_connections < 1) { LOG_WARNING << "Found no RAS connections"; FreeLibrary(ras_handle); return INADDR_ANY; } LOG_DEBUG.printf("Found %d connections", num_connections); for (i = 0; i < num_connections; i++) { RASCONNSTATUS status; DWORD size; LOG_DEBUG.printf("Connection %d:", i); LOG_DEBUG.printf("Entry Name: %s", rasbuffer[i].szEntryName); LOG_DEBUG.printf("Device Type: %s", rasbuffer[i].szDeviceType); LOG_DEBUG.printf("Device Name: %s", rasbuffer[i].szDeviceName); // get the connection status status.dwSize = sizeof(RASCONNSTATUS); rval = pRasGetConnectStatus(rasbuffer[i].hrasconn, &status); if (rval != 0) { FreeLibrary(ras_handle); return INADDR_ANY; } LOG_DEBUG.printf("Status: %s", (status.rasconnstate == RASCS_Connected) ? "Connected" : "Not Connected"); // get the projection informatiom size = sizeof(projection); projection.dwSize = size; rval = pRasGetProjectionInfo(rasbuffer[i].hrasconn, RASP_PppIp, &projection, &size); if (rval != 0) { FreeLibrary(ras_handle); return INADDR_ANY; } LOG_DEBUG.printf("IP Address: %s", projection.szIpAddress); } Ras_connected = 1; FreeLibrary(ras_handle); rasip = inet_addr(projection.szIpAddress); if (rasip == INADDR_NONE) return INADDR_ANY; // The ip of the RAS connection return rasip; } #endif static async_dns_lookup aslu; static async_dns_lookup *lastaslu = NULL; #ifdef WIN32 #define CDECLCALL __cdecl #else #define CDECLCALL #endif #if defined(POSIX) int CDECLCALL gethostbynameworker(void *parm); #include "SDL.h" #include "SDL_thread.h" // rcg06192000 use SDL threads. // #include // #include // typedef int (*pthread_create_fp)(pthread_t *__thread,__const pthread_attr_t *__attr,void *(*__start_routine) (void // *),void *__arg); typedef int (*pthread_detach_fp)(pthread_t __th); typedef pthread_t (*pthread_self_fp)(void); static // pthread_create_fp dpthread_create = NULL; static pthread_detach_fp dpthread_detach = NULL; static pthread_self_fp // dpthread_self = NULL; void nw_LoadThreadLibrary(void) { // rcg06192000 use SDL threads. /* if(dpthread_create) return; void *lib; lib = dlopen("libpthread.so",RTLD_GLOBAL|RTLD_NOW); if(!lib) { Int3(); return; } dpthread_create = (pthread_create_fp)dlsym(lib,"pthread_create"); dpthread_detach = (pthread_detach_fp)dlsym(lib,"pthread_detach"); dpthread_self = (pthread_self_fp)dlsym(lib,"pthread_self"); if(!dpthread_create || !dpthread_detach || !dpthread_self) { dpthread_create = NULL; //this variable I use to see if the library is loaded Int3(); return; } // don't close the library...we need it open for future use // and we can't close it on atexit() because linux will segfault for some stupid reason */ } #else void CDECLCALL gethostbynameworker(void *parm); #endif int nw_Asyncgethostbyname(uint32_t *ip, int command, char *hostname) { if (command == NW_AGHBN_LOOKUP) { if (lastaslu) { lastaslu->abort = true; // rcg06212000 join the thread. #if defined(POSIX) SDL_WaitThread(lastaslu->threadId, NULL); free(lastaslu); #endif } // if // rcg06262000 don't need all the rigamarole since we join the thread. #if (!defined(POSIX)) async_dns_lookup *newaslu; newaslu = mem_rmalloc(); memset(&newaslu->ip, 0, sizeof(uint32_t)); newaslu->host = hostname; newaslu->done = false; newaslu->error = false; newaslu->abort = false; lastaslu = newaslu; aslu.done = false; #else memset(&aslu.ip, 0, sizeof(uint32_t)); aslu.host = hostname; aslu.done = false; aslu.error = false; aslu.abort = false; lastaslu = &aslu; aslu.done = false; #endif #ifdef WIN32 _beginthread(gethostbynameworker, 0, newaslu); #elif defined(POSIX) // rcg06192000 use SDL threads. /* nw_LoadThreadLibrary(); if(!dpthread_create) { HOSTENT *he = gethostbyname(lastaslu->host); if(he==NULL) { lastaslu->error = true; } else { memcpy(&lastaslu->ip,he->h_addr_list[0],sizeof(uint32_t)); lastaslu->done = true; memcpy(&aslu,lastaslu,sizeof(async_dns_lookup)); } }else */ { // rcg06192000 SDLified. // pthread_t thread; // dpthread_create(&thread, NULL, gethostbynameworker,newaslu); aslu.threadId = SDL_CreateThread(gethostbynameworker, "dnsworker", &aslu); } #else HOSTENT *he = gethostbyname(lastaslu->host); if (he == NULL) { lastaslu->error = true; } else { memcpy(&lastaslu->ip, he->h_addr_list[0], sizeof(uint32_t)); lastaslu->done = true; memcpy(&aslu, lastaslu, sizeof(async_dns_lookup)); } #endif return 1; } else if (command == NW_AGHBN_CANCEL) { if (lastaslu) { lastaslu->abort = true; // rcg06212000 join the thread. #if defined(POSIX) SDL_WaitThread(lastaslu->threadId, NULL); #endif } // if lastaslu = NULL; } else if (command == NW_AGHBN_READ) { if (!lastaslu) return -1; if (aslu.done) { // rcg06212000 join the thread. #if defined(POSIX) SDL_WaitThread(aslu.threadId, NULL); #endif lastaslu = NULL; memcpy(ip, &aslu.ip, sizeof(uint32_t)); return 1; } else if (aslu.error) { // rcg06212000 join the thread. #if defined(POSIX) SDL_WaitThread(aslu.threadId, NULL); #else mem_free(lastaslu); #endif lastaslu = NULL; return -1; } else return 0; } return -2; } // This is the worker thread which does the lookup. #if defined(POSIX) int CDECLCALL gethostbynameworker(void *parm) #else void CDECLCALL gethostbynameworker(void *parm) #endif { // rcg06192000 nope. // #if defined(POSIX) // dpthread_detach(dpthread_self()); // #endif async_dns_lookup *lookup = (async_dns_lookup *)parm; HOSTENT *he = gethostbyname(lookup->host); if (he == NULL) { lookup->error = true; #if defined(POSIX) return 0; #else return; #endif } else if (!lookup->abort) { memcpy(&lookup->ip, he->h_addr_list[0], sizeof(uint32_t)); LOG_DEBUG.printf("IPLOOKUP: [%s] is %d.%d.%d.%d ...", lookup->host, (lookup->ip & 0x000000FF), (lookup->ip & 0x0000FF00) >> 8, (lookup->ip & 0x00FF0000) >> 16, (lookup->ip & 0xFF000000) >> 24); // memcpy(&aslu,lookup,sizeof(async_dns_lookup)); lookup->done = true; } // rcg06252000 don't free this, 'cause we need the threadId. #if !defined(POSIX) mem_free(lookup); #endif #if defined(POSIX) return 0; #endif } int nw_ReccomendPPS() { int clientPPS = CFG_NETWORK_CLIENT_PPS_MAX; Database->read_int(CTLCONFIG_PPS_CLIENT_DB_KEY, &clientPPS); return clientPPS; } // Register the networking library to call your function back // When data containing your ID is found // Returns non-zero if succesful, Zero if this ID is already registered int nw_RegisterCallback(NetworkReceiveCallback nfp, uint8_t id) { ASSERT(id < 16); // rcg06212000 this happens all the time; let it slide. // if(Netcallbacks[id]) //{ // mprintf(0,"Trying to reregister a callback!\n"); // Int3(); // Get Kevin! //} Netcallbacks[id] = nfp; return 0; } NetworkReceiveCallback nw_UnRegisterCallback(uint8_t id) { NetworkReceiveCallback nfp; ASSERT(id < 16); nfp = Netcallbacks[id]; Netcallbacks[id] = NULL; return nfp; } int nw_SendWithID(uint8_t id, uint8_t *data, int len, network_address *who_to) { uint8_t packet_data[1500]; int send_this_packet = 1; SOCKET send_sock; SOCKADDR_IN sock_addr; // UDP/TCP socket structure int ret, send_len; uint8_t iaddr[6], *send_data; int16_t port; fd_set wfds; ASSERT(data); ASSERT(len); ASSERT(who_to); timeval timeout = {0, 0}; packet_data[0] = id; memcpy(packet_data + 1, data, len); len++; // Account for the added byte // if (NetDebugFile) // cfprintf (NetDebugFile,"nw_SendWithID packet of id=%d type %d at %f // seconds.\n",id,data[0],timer_GetTime()); // mprintf(0,"Sending packet for id %d.\n",id); #ifdef WIN32 if (Use_DirectPlay) return dp_DirectPlaySend(who_to, (uint8_t *)data, len, false); #endif // mprintf(1, "network: type %d\n", who_to->connection_type); // send_sock = *Unreliable_socket; switch (who_to->connection_type) { case NP_TCP: if (id == NWT_RELIABLE) { NetStatistics.tcp_total_packets_sent++; NetStatistics.tcp_total_bytes_sent += len; } else if (id == NWT_UNRELIABLE) { NetStatistics.udp_total_packets_sent++; NetStatistics.udp_total_bytes_sent += len; } send_sock = TCP_socket; if (!TCP_active) return 0; break; default: LOG_ERROR << "Unknown protocol type in nw_Send()"; Int3(); return 0; } /* uint8_t compdata[MAX_PACKET_SIZE*3]; uint8_t testdata[MAX_PACKET_SIZE*3]; int uncompsize; ////Int3(); Uncompressed_outgoing_data_len += len; int compsize = nw_Compress(data,compdata,len); Compressed_outgoing_data_len += compsize; uncompsize = nw_Uncompress(compdata,testdata,compsize); ASSERT(uncompsize==len); ASSERT(memcmp(data,testdata,uncompsize)==0); int my_comp_ratio = (float) ((float)Uncompressed_outgoing_data_len/(float)Compressed_outgoing_data_len); */ if (!Sockets_initted) { LOG_ERROR << "Network ==> Socket not inited in nw_Send"; return 0; } memset(iaddr, 0x00, 6); memcpy(iaddr, who_to->address, 6); port = who_to->port; if (port == 0) { LOG_ERROR.printf("Network ==> destination port %d invalid in psnet_send", port); Int3(); return 0; } send_len = len; send_data = (uint8_t *)packet_data; FD_ZERO(&wfds); FD_SET(send_sock, &wfds); int sock_writable = select(send_sock + 1, NULL, &wfds, NULL, &timeout); if (sock_writable == SOCKET_ERROR) { LOG_ERROR.printf("Error on blocking select for write %d", WSAGetLastError()); return 0; } if (!sock_writable) { // This packet gets dropped. return 0; } if (send_this_packet) { switch (who_to->connection_type) { case NP_TCP: sock_addr.sin_family = AF_INET; memcpy(&sock_addr.sin_addr.s_addr, iaddr, 4); sock_addr.sin_port = htons(port); ret = sendto(TCP_socket, (char *)send_data, send_len, 0, (SOCKADDR *)&sock_addr, sizeof(sock_addr)); break; default: Int3(); // Unknown protocol break; } // end switch } int lasterr; if (ret != SOCKET_ERROR) { return 1; } lasterr = WSAGetLastError(); if (lasterr == WSAEWOULDBLOCK) { return 0; } LOG_ERROR.printf("Couldn't send data (%d)!", lasterr); return 0; } int nw_DoReceiveCallbacks(void) { SOCKADDR_IN ip_addr; // UDP/TCP socket structure socklen_t read_len, from_len; network_address from_addr; uint8_t packet_data[1500]; nw_ReliableResend(); while (TCP_active) { // check if there is any data on the socket to be read. The amount of data that can be // atomically read is stored in len. #if 0 fd_set rfds; FD_ZERO(&rfds); FD_SET( TCP_socket, &rfds ); timeout.tv_sec = 0; timeout.tv_usec = 0; timeval timeout; if ( select( TCP_socket+1, &rfds, NULL, NULL, &timeout) == SOCKET_ERROR) { LOG_ERROR.printf("Error %d doing a socket select on IP read", WSAGetLastError()); break; } // if the read file descriptor is not set, then bail! if ( !FD_ISSET(TCP_socket, &rfds ) ) break; #endif // get data off the socket and process from_len = sizeof(SOCKADDR_IN); read_len = recvfrom(TCP_socket, (char *)packet_data, 1500, 0, (SOCKADDR *)&ip_addr, &from_len); if (read_len == SOCKET_ERROR) { int x = WSAGetLastError(); if (x != WSAEWOULDBLOCK) { LOG_WARNING.printf("Read error on IP socket. Winsock error %d", x); } break; } memset(&from_addr, 0x00, sizeof(network_address)); from_addr.connection_type = NP_TCP; from_addr.port = ntohs(ip_addr.sin_port); #ifdef WIN32 memcpy(from_addr.address, &ip_addr.sin_addr.S_un.S_addr, 4); #else memcpy(from_addr.address, &ip_addr.sin_addr.s_addr, 4); #endif uint8_t packet_id = (packet_data[0] & 0x0f); if (Netcallbacks[packet_id]) { // mprintf(0,"Calling network callback for id %d.\n",packet_id); int rlen = read_len - 1; if (packet_id == NWT_UNRELIABLE) { NetStatistics.udp_total_packets_rec++; NetStatistics.udp_total_bytes_rec += rlen; } else if (packet_id == NWT_RELIABLE) { NetStatistics.tcp_total_packets_rec++; NetStatistics.tcp_total_bytes_rec += rlen; } Netcallbacks[packet_id](packet_data + 1, rlen, &from_addr); } } return 0; } // MTS: only used in this file? // Resend any unack'd packets and send any buffered packets, heartbeats, etc. void nw_ReliableResend(void) { int i, j; int rcode = -1; reliable_socket *rsocket = NULL; // Go through each reliable socket that is connected and do any needed work. for (j = 0; j < MAXRELIABLESOCKETS; j++) { rsocket = &reliable_sockets[j]; if (serverconn == -1) { if (rsocket->status == RNF_LIMBO) if ((timer_GetTime() - rsocket->last_packet_received) > NETTIMEOUT) { LOG_WARNING.printf("Reliable (but in limbo) socket (%d) timed out in nw_WorkReliable().", j); memset(rsocket, 0, sizeof(reliable_socket)); rsocket->status = RNF_UNUSED; // Won't work if this is an outgoing connection. } } else { if ((rsocket->status == RNF_LIMBO) && ((timer_GetTime() - first_sent_iamhere) > NETTIMEOUT)) { rsocket->status = RNF_BROKEN; LOG_WARNING.printf("Reliable socket (%d) timed out in nw_WorkReliable().", j); } } if (rsocket->status == RNF_CONNECTED) { float retry_packet_time; if ((rsocket->mean_ping == 0) || (rsocket->mean_ping > (NETRETRYTIME * 4))) { retry_packet_time = NETRETRYTIME; } else { if (rsocket->mean_ping < MIN_NET_RETRYTIME) { retry_packet_time = (float)MIN_NET_RETRYTIME; // mprintf(0,"Using retransmission time of %f\n",retry_packet_time); } else { retry_packet_time = ((float)(float)rsocket->mean_ping * (float)1.25); // mprintf(0,"Using retransmission time of %f\n",retry_packet_time); } } // Iterate through send buffers. for (i = 0; i < MAXNETBUFFERS; i++) { if (((i == rsocket->waiting_packet_number) && ((((timer_GetTime() - rsocket->last_sent) > R_NET_PACKET_QUEUE_TIME)) || ((rsocket->mean_ping > 0) && (rsocket->mean_ping < R_NET_PACKET_QUEUE_TIME)) || rsocket->send_urgent)) || ((rsocket->sbuffers[i]) && ((timer_GetTime() - rsocket->timesent[i]) >= retry_packet_time))) // Send again { if (i == rsocket->waiting_packet_number) { rsocket->waiting_packet_number = -1; rsocket->last_sent = timer_GetTime(); // mprintf(0,"Sending delayed packet...\n"); } reliable_header send_header; // mprintf(0,"Resending reliable packet in nw_WorkReliable().\n"); send_header.send_time = INTEL_FLOAT(timer_GetTime()); send_header.seq = INTEL_SHORT(rsocket->ssequence[i]); memcpy(send_header.data, rsocket->sbuffers[i]->buffer, rsocket->send_len[i]); send_header.data_len = INTEL_SHORT(rsocket->send_len[i]); send_header.type = RNT_DATA; network_address send_address; memset(&send_address, 0, sizeof(network_address)); send_address.connection_type = rsocket->connection_type; if (NP_TCP == send_address.connection_type) { SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&rsocket->addr; memcpy(send_address.address, &inaddr->sin_addr, 4); send_address.port = htons(inaddr->sin_port); send_address.connection_type = NP_TCP; int len = RELIABLE_PACKET_HEADER_ONLY_SIZE + rsocket->send_len[i]; NetStatistics.tcp_total_packets_sent--; // decrement because we are going to inc // in nw_SendWithID NetStatistics.tcp_total_bytes_sent -= len; // see above NetStatistics.tcp_total_packets_resent++; NetStatistics.tcp_total_bytes_resent += len; } // mprintf(0,"Resending reliable packet! Sequence %d\n",send_header.seq); rcode = nw_SendWithID(NWT_RELIABLE, (uint8_t *)&send_header, RELIABLE_PACKET_HEADER_ONLY_SIZE + rsocket->send_len[i], &send_address); if ((rcode == SOCKET_ERROR) && (WSAEWOULDBLOCK == WSAGetLastError())) { // The packet didn't get sent, flag it to try again next frame rsocket->timesent[i] = timer_GetTime() - (NETRETRYTIME * 4); } else { rsocket->last_packet_sent = timer_GetTime(); rsocket->timesent[i] = timer_GetTime(); } } } // We've sent all the packets, now we go out of urgent mode. rsocket->send_urgent = 0; if ((rsocket->status == RNF_CONNECTED) && ((timer_GetTime() - rsocket->last_packet_sent) > NETHEARTBEATTIME)) { reliable_header send_header; // mprintf(0,"Resending reliable packet in nw_WorkReliable().\n"); send_header.send_time = INTEL_FLOAT(timer_GetTime()); send_header.seq = INTEL_SHORT((int16_t)0); send_header.data_len = INTEL_SHORT((int16_t)0); send_header.type = RNT_HEARTBEAT; rcode = -1; network_address send_address; memset(&send_address, 0, sizeof(network_address)); send_address.connection_type = rsocket->connection_type; if (NP_TCP == send_address.connection_type) { SOCKADDR_IN *inaddr = (SOCKADDR_IN *)&rsocket->addr; memcpy(send_address.address, &inaddr->sin_addr, 4); send_address.port = htons(inaddr->sin_port); send_address.connection_type = NP_TCP; int len = RELIABLE_PACKET_HEADER_ONLY_SIZE + rsocket->send_len[i]; NetStatistics.tcp_total_packets_sent--; // decrement because we are going to inc // in nw_SendWithID NetStatistics.tcp_total_bytes_sent -= len; // see above NetStatistics.tcp_total_packets_resent++; NetStatistics.tcp_total_bytes_resent += len; } rcode = nw_SendWithID(NWT_RELIABLE, (uint8_t *)&send_header, RELIABLE_PACKET_HEADER_ONLY_SIZE, &send_address); if ((rcode != SOCKET_ERROR) && (WSAEWOULDBLOCK != WSAGetLastError())) { // It must have been sent rsocket->last_packet_sent = timer_GetTime(); } } if ((rsocket->status == RNF_CONNECTED) && ((timer_GetTime() - rsocket->last_packet_received) > NETTIMEOUT)) { // This socket is hosed.....inform someone? LOG_WARNING.printf("Reliable Socket (%d) timed out in nw_WorkReliable().", j); rsocket->status = RNF_BROKEN; } } } } // fills in the buffer with network stats // pass NULL to reset the stats void nw_GetNetworkStats(tNetworkStatus *stats) { if (!stats) { // reset the stats memset(&NetStatistics, 0, sizeof(NetStatistics)); return; } memcpy(stats, &NetStatistics, sizeof(NetStatistics)); }