summaryrefslogtreecommitdiff
path: root/src/d_net.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/d_net.c')
-rw-r--r--src/d_net.c770
1 files changed, 770 insertions, 0 deletions
diff --git a/src/d_net.c b/src/d_net.c
new file mode 100644
index 00000000..7815f66e
--- /dev/null
+++ b/src/d_net.c
@@ -0,0 +1,770 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// $Id: d_net.c 4 2005-07-23 16:19:41Z fraggle $
+//
+// 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$
+// Revision 1.1 2005/07/23 16:20:50 fraggle
+// Initial revision
+//
+//
+// DESCRIPTION:
+// DOOM Network game communication and protocol,
+// all OS independend parts.
+//
+//-----------------------------------------------------------------------------
+
+
+static const char rcsid[] = "$Id: d_net.c 4 2005-07-23 16:19:41Z fraggle $";
+
+
+#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
+ }
+}