Permalink
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
DOOM/linuxdoom-1.10/d_net.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Travis Bradshaw
The DOOM sources as originally released on December 23, 1997
767 lines (636 sloc)
15.9 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Emacs style mode select -*- C++ -*- | |
| //----------------------------------------------------------------------------- | |
| // | |
| // $Id:$ | |
| // | |
| // Copyright (C) 1993-1996 by id Software, Inc. | |
| // | |
| // This source is available for distribution and/or modification | |
| // only under the terms of the DOOM Source Code License as | |
| // published by id Software. All rights reserved. | |
| // | |
| // The source is distributed in the hope that it will be useful, | |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License | |
| // for more details. | |
| // | |
| // $Log:$ | |
| // | |
| // DESCRIPTION: | |
| // DOOM Network game communication and protocol, | |
| // all OS independend parts. | |
| // | |
| //----------------------------------------------------------------------------- | |
| static const char rcsid[] = "$Id: d_net.c,v 1.3 1997/02/03 22:01:47 b1 Exp $"; | |
| #include "m_menu.h" | |
| #include "i_system.h" | |
| #include "i_video.h" | |
| #include "i_net.h" | |
| #include "g_game.h" | |
| #include "doomdef.h" | |
| #include "doomstat.h" | |
| #define NCMD_EXIT 0x80000000 | |
| #define NCMD_RETRANSMIT 0x40000000 | |
| #define NCMD_SETUP 0x20000000 | |
| #define NCMD_KILL 0x10000000 // kill game | |
| #define NCMD_CHECKSUM 0x0fffffff | |
| doomcom_t* doomcom; | |
| doomdata_t* netbuffer; // points inside doomcom | |
| // | |
| // NETWORKING | |
| // | |
| // gametic is the tic about to (or currently being) run | |
| // maketic is the tick that hasn't had control made for it yet | |
| // nettics[] has the maketics for all players | |
| // | |
| // a gametic cannot be run until nettics[] > gametic for all players | |
| // | |
| #define RESENDCOUNT 10 | |
| #define PL_DRONE 0x80 // bit flag in doomdata->player | |
| ticcmd_t localcmds[BACKUPTICS]; | |
| ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; | |
| int nettics[MAXNETNODES]; | |
| boolean nodeingame[MAXNETNODES]; // set false as nodes leave game | |
| boolean remoteresend[MAXNETNODES]; // set when local needs tics | |
| int resendto[MAXNETNODES]; // set when remote needs tics | |
| int resendcount[MAXNETNODES]; | |
| int nodeforplayer[MAXPLAYERS]; | |
| int maketic; | |
| int lastnettic; | |
| int skiptics; | |
| int ticdup; | |
| int maxsend; // BACKUPTICS/(2*ticdup)-1 | |
| void D_ProcessEvents (void); | |
| void G_BuildTiccmd (ticcmd_t *cmd); | |
| void D_DoAdvanceDemo (void); | |
| boolean reboundpacket; | |
| doomdata_t reboundstore; | |
| // | |
| // | |
| // | |
| int NetbufferSize (void) | |
| { | |
| return (int)&(((doomdata_t *)0)->cmds[netbuffer->numtics]); | |
| } | |
| // | |
| // Checksum | |
| // | |
| unsigned NetbufferChecksum (void) | |
| { | |
| unsigned c; | |
| int i,l; | |
| c = 0x1234567; | |
| // FIXME -endianess? | |
| #ifdef NORMALUNIX | |
| return 0; // byte order problems | |
| #endif | |
| l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4; | |
| for (i=0 ; i<l ; i++) | |
| c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1); | |
| return c & NCMD_CHECKSUM; | |
| } | |
| // | |
| // | |
| // | |
| int ExpandTics (int low) | |
| { | |
| int delta; | |
| delta = low - (maketic&0xff); | |
| if (delta >= -64 && delta <= 64) | |
| return (maketic&~0xff) + low; | |
| if (delta > 64) | |
| return (maketic&~0xff) - 256 + low; | |
| if (delta < -64) | |
| return (maketic&~0xff) + 256 + low; | |
| I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic); | |
| return 0; | |
| } | |
| // | |
| // HSendPacket | |
| // | |
| void | |
| HSendPacket | |
| (int node, | |
| int flags ) | |
| { | |
| netbuffer->checksum = NetbufferChecksum () | flags; | |
| if (!node) | |
| { | |
| reboundstore = *netbuffer; | |
| reboundpacket = true; | |
| return; | |
| } | |
| if (demoplayback) | |
| return; | |
| if (!netgame) | |
| I_Error ("Tried to transmit to another node"); | |
| doomcom->command = CMD_SEND; | |
| doomcom->remotenode = node; | |
| doomcom->datalength = NetbufferSize (); | |
| if (debugfile) | |
| { | |
| int i; | |
| int realretrans; | |
| if (netbuffer->checksum & NCMD_RETRANSMIT) | |
| realretrans = ExpandTics (netbuffer->retransmitfrom); | |
| else | |
| realretrans = -1; | |
| fprintf (debugfile,"send (%i + %i, R %i) [%i] ", | |
| ExpandTics(netbuffer->starttic), | |
| netbuffer->numtics, realretrans, doomcom->datalength); | |
| for (i=0 ; i<doomcom->datalength ; i++) | |
| fprintf (debugfile,"%i ",((byte *)netbuffer)[i]); | |
| fprintf (debugfile,"\n"); | |
| } | |
| I_NetCmd (); | |
| } | |
| // | |
| // HGetPacket | |
| // Returns false if no packet is waiting | |
| // | |
| boolean HGetPacket (void) | |
| { | |
| if (reboundpacket) | |
| { | |
| *netbuffer = reboundstore; | |
| doomcom->remotenode = 0; | |
| reboundpacket = false; | |
| return true; | |
| } | |
| if (!netgame) | |
| return false; | |
| if (demoplayback) | |
| return false; | |
| doomcom->command = CMD_GET; | |
| I_NetCmd (); | |
| if (doomcom->remotenode == -1) | |
| return false; | |
| if (doomcom->datalength != NetbufferSize ()) | |
| { | |
| if (debugfile) | |
| fprintf (debugfile,"bad packet length %i\n",doomcom->datalength); | |
| return false; | |
| } | |
| if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) ) | |
| { | |
| if (debugfile) | |
| fprintf (debugfile,"bad packet checksum\n"); | |
| return false; | |
| } | |
| if (debugfile) | |
| { | |
| int realretrans; | |
| int i; | |
| if (netbuffer->checksum & NCMD_SETUP) | |
| fprintf (debugfile,"setup packet\n"); | |
| else | |
| { | |
| if (netbuffer->checksum & NCMD_RETRANSMIT) | |
| realretrans = ExpandTics (netbuffer->retransmitfrom); | |
| else | |
| realretrans = -1; | |
| fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ", | |
| doomcom->remotenode, | |
| ExpandTics(netbuffer->starttic), | |
| netbuffer->numtics, realretrans, doomcom->datalength); | |
| for (i=0 ; i<doomcom->datalength ; i++) | |
| fprintf (debugfile,"%i ",((byte *)netbuffer)[i]); | |
| fprintf (debugfile,"\n"); | |
| } | |
| } | |
| return true; | |
| } | |
| // | |
| // GetPackets | |
| // | |
| char exitmsg[80]; | |
| void GetPackets (void) | |
| { | |
| int netconsole; | |
| int netnode; | |
| ticcmd_t *src, *dest; | |
| int realend; | |
| int realstart; | |
| while ( HGetPacket() ) | |
| { | |
| if (netbuffer->checksum & NCMD_SETUP) | |
| continue; // extra setup packet | |
| netconsole = netbuffer->player & ~PL_DRONE; | |
| netnode = doomcom->remotenode; | |
| // to save bytes, only the low byte of tic numbers are sent | |
| // Figure out what the rest of the bytes are | |
| realstart = ExpandTics (netbuffer->starttic); | |
| realend = (realstart+netbuffer->numtics); | |
| // check for exiting the game | |
| if (netbuffer->checksum & NCMD_EXIT) | |
| { | |
| if (!nodeingame[netnode]) | |
| continue; | |
| nodeingame[netnode] = false; | |
| playeringame[netconsole] = false; | |
| strcpy (exitmsg, "Player 1 left the game"); | |
| exitmsg[7] += netconsole; | |
| players[consoleplayer].message = exitmsg; | |
| if (demorecording) | |
| G_CheckDemoStatus (); | |
| continue; | |
| } | |
| // check for a remote game kill | |
| if (netbuffer->checksum & NCMD_KILL) | |
| I_Error ("Killed by network driver"); | |
| nodeforplayer[netconsole] = netnode; | |
| // check for retransmit request | |
| if ( resendcount[netnode] <= 0 | |
| && (netbuffer->checksum & NCMD_RETRANSMIT) ) | |
| { | |
| resendto[netnode] = ExpandTics(netbuffer->retransmitfrom); | |
| if (debugfile) | |
| fprintf (debugfile,"retransmit from %i\n", resendto[netnode]); | |
| resendcount[netnode] = RESENDCOUNT; | |
| } | |
| else | |
| resendcount[netnode]--; | |
| // check for out of order / duplicated packet | |
| if (realend == nettics[netnode]) | |
| continue; | |
| if (realend < nettics[netnode]) | |
| { | |
| if (debugfile) | |
| fprintf (debugfile, | |
| "out of order packet (%i + %i)\n" , | |
| realstart,netbuffer->numtics); | |
| continue; | |
| } | |
| // check for a missed packet | |
| if (realstart > nettics[netnode]) | |
| { | |
| // stop processing until the other system resends the missed tics | |
| if (debugfile) | |
| fprintf (debugfile, | |
| "missed tics from %i (%i - %i)\n", | |
| netnode, realstart, nettics[netnode]); | |
| remoteresend[netnode] = true; | |
| continue; | |
| } | |
| // update command store from the packet | |
| { | |
| int start; | |
| remoteresend[netnode] = false; | |
| start = nettics[netnode] - realstart; | |
| src = &netbuffer->cmds[start]; | |
| while (nettics[netnode] < realend) | |
| { | |
| dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS]; | |
| nettics[netnode]++; | |
| *dest = *src; | |
| src++; | |
| } | |
| } | |
| } | |
| } | |
| // | |
| // NetUpdate | |
| // Builds ticcmds for console player, | |
| // sends out a packet | |
| // | |
| int gametime; | |
| void NetUpdate (void) | |
| { | |
| int nowtime; | |
| int newtics; | |
| int i,j; | |
| int realstart; | |
| int gameticdiv; | |
| // check time | |
| nowtime = I_GetTime ()/ticdup; | |
| newtics = nowtime - gametime; | |
| gametime = nowtime; | |
| if (newtics <= 0) // nothing new to update | |
| goto listen; | |
| if (skiptics <= newtics) | |
| { | |
| newtics -= skiptics; | |
| skiptics = 0; | |
| } | |
| else | |
| { | |
| skiptics -= newtics; | |
| newtics = 0; | |
| } | |
| netbuffer->player = consoleplayer; | |
| // build new ticcmds for console player | |
| gameticdiv = gametic/ticdup; | |
| for (i=0 ; i<newtics ; i++) | |
| { | |
| I_StartTic (); | |
| D_ProcessEvents (); | |
| if (maketic - gameticdiv >= BACKUPTICS/2-1) | |
| break; // can't hold any more | |
| //printf ("mk:%i ",maketic); | |
| G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]); | |
| maketic++; | |
| } | |
| if (singletics) | |
| return; // singletic update is syncronous | |
| // send the packet to the other nodes | |
| for (i=0 ; i<doomcom->numnodes ; i++) | |
| if (nodeingame[i]) | |
| { | |
| netbuffer->starttic = realstart = resendto[i]; | |
| netbuffer->numtics = maketic - realstart; | |
| if (netbuffer->numtics > BACKUPTICS) | |
| I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS"); | |
| resendto[i] = maketic - doomcom->extratics; | |
| for (j=0 ; j< netbuffer->numtics ; j++) | |
| netbuffer->cmds[j] = | |
| localcmds[(realstart+j)%BACKUPTICS]; | |
| if (remoteresend[i]) | |
| { | |
| netbuffer->retransmitfrom = nettics[i]; | |
| HSendPacket (i, NCMD_RETRANSMIT); | |
| } | |
| else | |
| { | |
| netbuffer->retransmitfrom = 0; | |
| HSendPacket (i, 0); | |
| } | |
| } | |
| // listen for other packets | |
| listen: | |
| GetPackets (); | |
| } | |
| // | |
| // CheckAbort | |
| // | |
| void CheckAbort (void) | |
| { | |
| event_t *ev; | |
| int stoptic; | |
| stoptic = I_GetTime () + 2; | |
| while (I_GetTime() < stoptic) | |
| I_StartTic (); | |
| I_StartTic (); | |
| for ( ; eventtail != eventhead | |
| ; eventtail = (++eventtail)&(MAXEVENTS-1) ) | |
| { | |
| ev = &events[eventtail]; | |
| if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE) | |
| I_Error ("Network game synchronization aborted."); | |
| } | |
| } | |
| // | |
| // D_ArbitrateNetStart | |
| // | |
| void D_ArbitrateNetStart (void) | |
| { | |
| int i; | |
| boolean gotinfo[MAXNETNODES]; | |
| autostart = true; | |
| memset (gotinfo,0,sizeof(gotinfo)); | |
| if (doomcom->consoleplayer) | |
| { | |
| // listen for setup info from key player | |
| printf ("listening for network start info...\n"); | |
| while (1) | |
| { | |
| CheckAbort (); | |
| if (!HGetPacket ()) | |
| continue; | |
| if (netbuffer->checksum & NCMD_SETUP) | |
| { | |
| if (netbuffer->player != VERSION) | |
| I_Error ("Different DOOM versions cannot play a net game!"); | |
| startskill = netbuffer->retransmitfrom & 15; | |
| deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6; | |
| nomonsters = (netbuffer->retransmitfrom & 0x20) > 0; | |
| respawnparm = (netbuffer->retransmitfrom & 0x10) > 0; | |
| startmap = netbuffer->starttic & 0x3f; | |
| startepisode = netbuffer->starttic >> 6; | |
| return; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| // key player, send the setup info | |
| printf ("sending network start info...\n"); | |
| do | |
| { | |
| CheckAbort (); | |
| for (i=0 ; i<doomcom->numnodes ; i++) | |
| { | |
| netbuffer->retransmitfrom = startskill; | |
| if (deathmatch) | |
| netbuffer->retransmitfrom |= (deathmatch<<6); | |
| if (nomonsters) | |
| netbuffer->retransmitfrom |= 0x20; | |
| if (respawnparm) | |
| netbuffer->retransmitfrom |= 0x10; | |
| netbuffer->starttic = startepisode * 64 + startmap; | |
| netbuffer->player = VERSION; | |
| netbuffer->numtics = 0; | |
| HSendPacket (i, NCMD_SETUP); | |
| } | |
| #if 1 | |
| for(i = 10 ; i && HGetPacket(); --i) | |
| { | |
| if((netbuffer->player&0x7f) < MAXNETNODES) | |
| gotinfo[netbuffer->player&0x7f] = true; | |
| } | |
| #else | |
| while (HGetPacket ()) | |
| { | |
| gotinfo[netbuffer->player&0x7f] = true; | |
| } | |
| #endif | |
| for (i=1 ; i<doomcom->numnodes ; i++) | |
| if (!gotinfo[i]) | |
| break; | |
| } while (i < doomcom->numnodes); | |
| } | |
| } | |
| // | |
| // D_CheckNetGame | |
| // Works out player numbers among the net participants | |
| // | |
| extern int viewangleoffset; | |
| void D_CheckNetGame (void) | |
| { | |
| int i; | |
| for (i=0 ; i<MAXNETNODES ; i++) | |
| { | |
| nodeingame[i] = false; | |
| nettics[i] = 0; | |
| remoteresend[i] = false; // set when local needs tics | |
| resendto[i] = 0; // which tic to start sending | |
| } | |
| // I_InitNetwork sets doomcom and netgame | |
| I_InitNetwork (); | |
| if (doomcom->id != DOOMCOM_ID) | |
| I_Error ("Doomcom buffer invalid!"); | |
| netbuffer = &doomcom->data; | |
| consoleplayer = displayplayer = doomcom->consoleplayer; | |
| if (netgame) | |
| D_ArbitrateNetStart (); | |
| printf ("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", | |
| startskill, deathmatch, startmap, startepisode); | |
| // read values out of doomcom | |
| ticdup = doomcom->ticdup; | |
| maxsend = BACKUPTICS/(2*ticdup)-1; | |
| if (maxsend<1) | |
| maxsend = 1; | |
| for (i=0 ; i<doomcom->numplayers ; i++) | |
| playeringame[i] = true; | |
| for (i=0 ; i<doomcom->numnodes ; i++) | |
| nodeingame[i] = true; | |
| printf ("player %i of %i (%i nodes)\n", | |
| consoleplayer+1, doomcom->numplayers, doomcom->numnodes); | |
| } | |
| // | |
| // D_QuitNetGame | |
| // Called before quitting to leave a net game | |
| // without hanging the other players | |
| // | |
| void D_QuitNetGame (void) | |
| { | |
| int i, j; | |
| if (debugfile) | |
| fclose (debugfile); | |
| if (!netgame || !usergame || consoleplayer == -1 || demoplayback) | |
| return; | |
| // send a bunch of packets for security | |
| netbuffer->player = consoleplayer; | |
| netbuffer->numtics = 0; | |
| for (i=0 ; i<4 ; i++) | |
| { | |
| for (j=1 ; j<doomcom->numnodes ; j++) | |
| if (nodeingame[j]) | |
| HSendPacket (j, NCMD_EXIT); | |
| I_WaitVBL (1); | |
| } | |
| } | |
| // | |
| // TryRunTics | |
| // | |
| int frametics[4]; | |
| int frameon; | |
| int frameskip[4]; | |
| int oldnettics; | |
| extern boolean advancedemo; | |
| void TryRunTics (void) | |
| { | |
| int i; | |
| int lowtic; | |
| int entertic; | |
| static int oldentertics; | |
| int realtics; | |
| int availabletics; | |
| int counts; | |
| int numplaying; | |
| // get real tics | |
| entertic = I_GetTime ()/ticdup; | |
| realtics = entertic - oldentertics; | |
| oldentertics = entertic; | |
| // get available tics | |
| NetUpdate (); | |
| lowtic = MAXINT; | |
| numplaying = 0; | |
| for (i=0 ; i<doomcom->numnodes ; i++) | |
| { | |
| if (nodeingame[i]) | |
| { | |
| numplaying++; | |
| if (nettics[i] < lowtic) | |
| lowtic = nettics[i]; | |
| } | |
| } | |
| availabletics = lowtic - gametic/ticdup; | |
| // decide how many tics to run | |
| if (realtics < availabletics-1) | |
| counts = realtics+1; | |
| else if (realtics < availabletics) | |
| counts = realtics; | |
| else | |
| counts = availabletics; | |
| if (counts < 1) | |
| counts = 1; | |
| frameon++; | |
| if (debugfile) | |
| fprintf (debugfile, | |
| "=======real: %i avail: %i game: %i\n", | |
| realtics, availabletics,counts); | |
| if (!demoplayback) | |
| { | |
| // ideally nettics[0] should be 1 - 3 tics above lowtic | |
| // if we are consistantly slower, speed up time | |
| for (i=0 ; i<MAXPLAYERS ; i++) | |
| if (playeringame[i]) | |
| break; | |
| if (consoleplayer == i) | |
| { | |
| // the key player does not adapt | |
| } | |
| else | |
| { | |
| if (nettics[0] <= nettics[nodeforplayer[i]]) | |
| { | |
| gametime--; | |
| // printf ("-"); | |
| } | |
| frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]); | |
| oldnettics = nettics[0]; | |
| if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) | |
| { | |
| skiptics = 1; | |
| // printf ("+"); | |
| } | |
| } | |
| }// demoplayback | |
| // wait for new tics if needed | |
| while (lowtic < gametic/ticdup + counts) | |
| { | |
| NetUpdate (); | |
| lowtic = MAXINT; | |
| for (i=0 ; i<doomcom->numnodes ; i++) | |
| if (nodeingame[i] && nettics[i] < lowtic) | |
| lowtic = nettics[i]; | |
| if (lowtic < gametic/ticdup) | |
| I_Error ("TryRunTics: lowtic < gametic"); | |
| // don't stay in here forever -- give the menu a chance to work | |
| if (I_GetTime ()/ticdup - entertic >= 20) | |
| { | |
| M_Ticker (); | |
| return; | |
| } | |
| } | |
| // run the count * ticdup dics | |
| while (counts--) | |
| { | |
| for (i=0 ; i<ticdup ; i++) | |
| { | |
| if (gametic/ticdup > lowtic) | |
| I_Error ("gametic>lowtic"); | |
| if (advancedemo) | |
| D_DoAdvanceDemo (); | |
| M_Ticker (); | |
| G_Ticker (); | |
| gametic++; | |
| // modify command for duplicated tics | |
| if (i != ticdup-1) | |
| { | |
| ticcmd_t *cmd; | |
| int buf; | |
| int j; | |
| buf = (gametic/ticdup)%BACKUPTICS; | |
| for (j=0 ; j<MAXPLAYERS ; j++) | |
| { | |
| cmd = &netcmds[j][buf]; | |
| cmd->chatchar = 0; | |
| if (cmd->buttons & BT_SPECIAL) | |
| cmd->buttons = 0; | |
| } | |
| } | |
| } | |
| NetUpdate (); // check for new console commands | |
| } | |
| } |