/* This contains the network play functions and data */ #include #include "SDL_net.h" #include "SDL_endian.h" #include "Maelstrom_Globals.h" #include "netplay.h" #include "protocol.h" int gNumPlayers; int gOurPlayer; int gDeathMatch; UDPsocket gNetFD; static int GotPlayer[MAX_PLAYERS]; static IPaddress PlayAddr[MAX_PLAYERS]; static IPaddress ServAddr; static int FoundUs, UseServer; static Uint32 NextFrame; UDPpacket *OutBound[2]; static int CurrOut; /* This is the data offset of a SYNC packet */ #define PDATA_OFFSET (1+1+sizeof(Uint32)+sizeof(Uint32)) /* We keep one packet backlogged for retransmission */ #define OutBuf OutBound[CurrOut]->data #define OutLen OutBound[CurrOut]->len #define LastBuf OutBound[!CurrOut]->data #define LastLen OutBound[!CurrOut]->len static unsigned char *SyncPtrs[2][MAX_PLAYERS]; static unsigned char SyncBufs[2][MAX_PLAYERS][BUFSIZ]; static int SyncLens[2][MAX_PLAYERS]; static int ThisSyncs[2]; static int CurrIn; static SDLNet_SocketSet SocketSet; /* We cache one packet if the other player is ahead of us */ #define SyncPtr SyncPtrs[CurrIn] #define SyncBuf SyncBufs[CurrIn] #define SyncLen SyncLens[CurrIn] #define ThisSync ThisSyncs[CurrIn] #define NextPtr SyncPtrs[!CurrIn] #define NextBuf SyncBufs[!CurrIn] #define NextLen SyncLens[!CurrIn] #define NextSync ThisSyncs[!CurrIn] #define TOGGLE(var) var = !var int InitNetData(void) { int i; /* Initialize the networking subsystem */ if ( SDLNet_Init() < 0 ) { error("NetLogic: Couldn't initialize networking!\n"); return(-1); } atexit(SDLNet_Quit); /* Create the outbound packets */ for ( i=0; i<2; ++i ) { OutBound[i] = SDLNet_AllocPacket(BUFSIZ); if ( OutBound[i] == NULL ) { error("Out of memory (creating network buffers)\n"); return(-1); } } /* Initialize network game variables */ FoundUs = 0; gOurPlayer = -1; gDeathMatch = 0; UseServer = 0; for ( i=0; idata[0] = SYNC_MSG; OutBound[1]->data[0] = SYNC_MSG; /* Type field, frame sequence, current random seed */ OutBound[0]->len = PDATA_OFFSET; OutBound[1]->len = PDATA_OFFSET; CurrOut = 0; ThisSyncs[0] = 0; ThisSyncs[1] = 0; CurrIn = 0; return(0); } void HaltNetData(void) { SDLNet_Quit(); } int AddPlayer(char *playerstr) { int playernum; int portnum; char *host=NULL, *port=NULL; /* Extract host and port information */ if ( (port=strchr(playerstr, ':')) != NULL ) *(port++) = '\0'; if ( (host=strchr(playerstr, '@')) != NULL ) *(host++) = '\0'; /* Find out which player we are referring to */ if (((playernum = atoi(playerstr)) <= 0) || (playernum > MAX_PLAYERS)) { error( "Argument to '-player' must be in integer between 1 and %d inclusive.\r\n", MAX_PLAYERS); PrintUsage(); } /* Do some error checking */ if ( GotPlayer[--playernum] ) { error("Player %d specified multiple times!\r\n", playernum+1); return(-1); } if ( port ) { portnum = atoi(port); } else { portnum = NETPLAY_PORT+playernum; } if ( host ) { /* Resolve the remote address */ SDLNet_ResolveHost(&PlayAddr[playernum], host, portnum); if ( PlayAddr[playernum].host == INADDR_NONE ) { error("Couldn't resolve host name for %s\r\n", host); return(-1); } } else { /* No host specified, local player */ if ( FoundUs ) { error( "More than one local player! (players %d and %d specified as local players)\r\n", gOurPlayer+1, playernum+1); return(-1); } else { gOurPlayer = playernum; FoundUs = 1; SDLNet_ResolveHost(&PlayAddr[playernum], NULL, portnum); } } /* We're done! */ GotPlayer[playernum] = 1; return(0); } int SetServer(char *serverstr) { int portnum; char *host=NULL, *port=NULL; /* Extract host and port information */ if ( (host=strchr(serverstr, '@')) == NULL ) { error( "Server host must be specified in the -server option.\r\n"); PrintUsage(); } else *(host++) = '\0'; if ( (port=strchr(serverstr, ':')) != NULL ) *(port++) = '\0'; /* We should know how many players we have now */ if (((gNumPlayers = atoi(serverstr)) <= 0) || (gNumPlayers > MAX_PLAYERS)) { error( "The number of players must be an integer between 1 and %d inclusive.\r\n", MAX_PLAYERS); PrintUsage(); } /* Resolve the remote address */ if ( port ) { portnum = atoi(port); } else { portnum = NETPLAY_PORT-1; } SDLNet_ResolveHost(&ServAddr, host, portnum); if ( ServAddr.host == INADDR_NONE ) { error("Couldn't resolve host name for %s\r\n", host); return(-1); } /* We're done! */ UseServer = 1; return(0); } /* This MUST be called after command line options have been processed. */ int CheckPlayers(void) { int i; int port; /* Check to make sure we have all the players */ if ( ! UseServer ) { for ( i=0, gNumPlayers=0; i gNumPlayers ) { error("You cannot be player %d in a %d player game.\r\n", gOurPlayer+1, gNumPlayers); return(-1); } if ( (gNumPlayers == 1) && gDeathMatch ) { error("Warning: No deathmatch in a single player game!\r\n"); gDeathMatch = 0; } /* Oh heck, create the UDP socket here... */ port = SDL_SwapBE16(PlayAddr[gOurPlayer].port); gNetFD = SDLNet_UDP_Open(port); if ( gNetFD == NULL ) { error("Couldn't create bound network socket"); return(-1); } SocketSet = SDLNet_AllocSocketSet(1); if ( SocketSet == NULL ) { error("Couldn't create socket watch set"); return(-1); } SDLNet_UDP_AddSocket(SocketSet, gNetFD); /* Now, so we can send to ourselves... */ PlayAddr[gOurPlayer] = *SDLNet_UDP_GetPeerAddress(gNetFD, -1); if ( ! PlayAddr[gOurPlayer].host ) { SDLNet_ResolveHost(&PlayAddr[gOurPlayer], "127.0.0.1", port); } /* Bind all of our players to the channels */ if ( ! UseServer ) { for ( i=0; i= (BUFSIZ-2) ) return; //error("Queued key 0x%.2x for frame %d\r\n", Type, NextFrame); OutBuf[OutLen++] = Op; OutBuf[OutLen++] = Type; } /* This function is called every frame, and is used to flush the network buffers, sending sync and keystroke packets. It is called AFTER the keyboard is polled, and BEFORE GetSyncBuf() is called by the player objects. Note: We assume that FastRand() isn't called by an interrupt routine, otherwise we lose consistency. */ int SyncNetwork(void) { UDPpacket sent; Uint32 seed, frame; unsigned char buf[BUFSIZ]; int index, nleft; /* Set the next inbound packet buffer */ TOGGLE(CurrIn); /* Set the frame number */ frame = NextFrame; SDLNet_Write32(frame, &OutBuf[1]); seed = GetRandSeed(); SDLNet_Write32(seed, &OutBuf[1+sizeof(frame)]); /* Send the packet to all the players */ SDLNet_UDP_Send(gNetFD, 0, OutBound[CurrOut]); for ( nleft=0, index=0; index 0 ) { for ( int i=1; i