shithub: choc

Download patch

ref: de3a8f767826d1aeb887da5e24140cd29790abf4
parent: cb142afe2e4bfa756249cd0e7e17ddd3ec820a87
author: Simon Howard <fraggle@gmail.com>
date: Thu Oct 13 16:07:52 EDT 2011

Convert Heretic code to use common main loop code. Working multiplayer!

Subversion-branch: /branches/v2-branch
Subversion-revision: 2417

--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -155,9 +155,13 @@
 @PROGRAM_PREFIX@doom_LDADD = doom/libdoom.a $(EXTRA_LIBS)
 
 if HAVE_WINDRES
-@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc
+@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc \
+               d_loop.c d_loop.h \
+               $(FEATURE_MULTIPLAYER_SOURCE_FILES)
 else
-@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH)
+@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH) \
+               d_loop.c d_loop.h \
+               $(FEATURE_MULTIPLAYER_SOURCE_FILES)
 endif
 
 @PROGRAM_PREFIX@heretic_LDADD = heretic/libheretic.a $(EXTRA_LIBS)
--- a/src/heretic/d_main.c
+++ b/src/heretic/d_main.c
@@ -27,9 +27,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "doomfeatures.h"
+
 #include "txt_main.h"
 #include "txt_io.h"
 
+#include "net_client.h"
+
 #include "config.h"
 #include "ct_chat.h"
 #include "doomdef.h"
@@ -80,9 +84,6 @@
 static int show_endoom = 1;
 
 void D_CheckNetGame(void);
-void D_ProcessEvents(void);
-void G_BuildTiccmd(ticcmd_t * cmd);
-void D_DoAdvanceDemo(void);
 void D_PageDrawer(void);
 void D_AdvanceDemo(void);
 void F_Drawer(void);
@@ -254,22 +255,8 @@
         I_StartFrame();
 
         // Process one or more tics
-        if (singletics)
-        {
-            I_StartTic();
-            D_ProcessEvents();
-            G_BuildTiccmd(&netcmds[consoleplayer][maketic % BACKUPTICS]);
-            if (advancedemo)
-                D_DoAdvanceDemo();
-            G_Ticker();
-            gametic++;
-            maketic++;
-        }
-        else
-        {
-            // Will run at least one tic
-            TryRunTics();
-        }
+        // Will run at least one tic
+        TryRunTics();
 
         // Move positional sounds
         S_UpdateSounds(players[consoleplayer].mo);
@@ -976,6 +963,11 @@
 
     tprintf(DEH_String("MN_Init: Init menu system.\n"), 1);
     MN_Init();
+
+#ifdef FEATURE_MULTIPLAYER
+    tprintf ("NET_Init: Init network subsystem.\n", 1);
+    NET_Init ();
+#endif
 
     CT_Init();
 
--- a/src/heretic/d_net.c
+++ b/src/heretic/d_net.c
@@ -2,8 +2,7 @@
 //-----------------------------------------------------------------------------
 //
 // Copyright(C) 1993-1996 Id Software, Inc.
-// Copyright(C) 1993-2008 Raven Software
-// Copyright(C) 2008 Simon Howard
+// Copyright(C) 2005 Simon Howard
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -20,829 +19,212 @@
 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 // 02111-1307, USA.
 //
+// DESCRIPTION:
+//	DOOM Network game communication and protocol,
+//	all OS independend parts.
+//
 //-----------------------------------------------------------------------------
 
-// d_net.c
-// This version has the fixed ticdup code
-
 #include <stdlib.h>
 
-#include "doomdef.h"
-#include "doomkeys.h"
+#include "doomfeatures.h"
 
+#include "m_argv.h"
 #include "i_system.h"
 #include "i_timer.h"
 #include "i_video.h"
+#include "doomdef.h"
 
-#define NCMD_EXIT               0x80000000
-#define NCMD_RETRANSMIT 0x40000000
-#define NCMD_SETUP              0x20000000
-#define NCMD_KILL               0x10000000      // kill game
-#define NCMD_CHECKSUM   0x0fffffff
+#include "deh_main.h"
 
+#include "d_loop.h"
 
-doomcom_t *doomcom;
-doomdata_t *netbuffer;          // points inside doomcom
+ticcmd_t *netcmds;
 
+extern void D_DoAdvanceDemo(void);
+extern void D_ProcessEvents(void);
+extern void G_BuildTiccmd(ticcmd_t *cmd, int maketic);
+extern boolean G_CheckDemoStatus(void);
 
-/*
-==============================================================================
+// Called when a player leaves the game
 
-							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, 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)
+static void PlayerQuitGame(player_t *player)
 {
-    return (int) &(((doomdata_t *) 0)->cmds[netbuffer->numtics]);
-}
+    static char exitmsg[80];
+    unsigned int player_num;
 
-unsigned NetbufferChecksum(void)
-{
-    unsigned c;
-    int i, l;
+    player_num = player - players;
 
-    c = 0x1234567;
+    // Do this the same way as Vanilla Doom does, to allow dehacked
+    // replacements of this message
 
-#if defined(NeXT) || defined(NORMALUNIX)
-    return 0;                   // byte order problems
-#endif
+    strncpy(exitmsg, DEH_String("Player 1 left the game"), sizeof(exitmsg));
+    exitmsg[sizeof(exitmsg) - 1] = '\0';
 
-    l = (NetbufferSize() - (int) &(((doomdata_t *) 0)->retransmitfrom)) / 4;
-    for (i = 0; i < l; i++)
-        c += ((unsigned *) &netbuffer->retransmitfrom)[i] * (i + 1);
+    exitmsg[7] += player_num;
 
-    return c & NCMD_CHECKSUM;
-}
+    playeringame[player_num] = false;
+    players[consoleplayer].message = exitmsg;
 
-int ExpandTics(int low)
-{
-    int delta;
+    // TODO: check if it is sensible to do this:
 
-    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)
+    if (demorecording) 
     {
-        reboundstore = *netbuffer;
-        reboundpacket = true;
-        return;
+        G_CheckDemoStatus ();
     }
-
-    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");
-    }
-
-#ifdef I_NET
-    I_NetCmd();
-#endif
 }
 
-/*
-==============
-=
-= HGetPacket
-=
-= Returns false if no packet is waiting
-=
-==============
-*/
-
-boolean HGetPacket(void)
+static void RunTic(ticcmd_t *cmds, boolean *ingame)
 {
-    if (reboundpacket)
-    {
-        *netbuffer = reboundstore;
-        doomcom->remotenode = 0;
-        reboundpacket = false;
-        return true;
-    }
+    extern boolean advancedemo;
+    unsigned int i;
 
-    if (!netgame)
-        return false;
-    if (demoplayback)
-        return false;
+    // Check for player quits.
 
-    doomcom->command = CMD_GET;
-#ifdef I_NET
-    I_NetCmd();
-#endif
-    if (doomcom->remotenode == -1)
-        return false;
-
-    if (doomcom->datalength != NetbufferSize())
+    for (i = 0; i < MAXPLAYERS; ++i)
     {
-        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 (playeringame[i] && !ingame[i])
         {
-            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");
+            PlayerQuitGame(&players[i]);
         }
     }
-    return true;
-}
 
+    netcmds = cmds;
 
-/*
-===================
-=
-= GetPackets
-=
-===================
-*/
+    // check that there are players in the game.  if not, we cannot
+    // run a tic.
 
-char exitmsg[80];
+    if (advancedemo)
+        D_DoAdvanceDemo ();
 
-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++;
-            }
-        }
-    }
-
+    G_Ticker ();
 }
 
-/*
-=============
-=
-= NetUpdate
-=
-= Builds ticcmds for console player
-= sends out a packet
-=============
-*/
+static loop_interface_t doom_loop_interface = {
+    D_ProcessEvents,
+    G_BuildTiccmd,
+    RunTic,
+    MN_Ticker
+};
 
-int gametime;
 
-void NetUpdate(void)
+// Load game settings from the specified structure and 
+// set global variables.
+
+static void LoadGameSettings(net_gamesettings_t *settings,
+                             net_connect_data_t *connect_data)
 {
-    int nowtime;
-    int newtics;
-    int i, j;
-    int realstart;
-    int gameticdiv;
+    unsigned int i;
 
-//
-// check time
-//
-    nowtime = I_GetTime() / ticdup;
-    newtics = nowtime - gametime;
-    gametime = nowtime;
+    deathmatch = settings->deathmatch;
+    ticdup = settings->ticdup;
+    startepisode = settings->episode;
+    startmap = settings->map;
+    startskill = settings->skill;
+    // TODO startloadgame = settings->loadgame;
+    nomonsters = settings->nomonsters;
+    respawnparm = settings->respawn_monsters;
 
-    if (newtics <= 0)           // nothing new to update
-        goto listen;
-
-    if (skiptics <= newtics)
+    if (!connect_data->drone)
     {
-        newtics -= skiptics;
-        skiptics = 0;
+        consoleplayer = settings->consoleplayer;
     }
     else
     {
-        skiptics -= newtics;
-        newtics = 0;
+        consoleplayer = 0;
     }
-
-
-    netbuffer->player = consoleplayer;
-
-//
-// build new ticcmds for console player
-//
-    gameticdiv = gametic / ticdup;
-    for (i = 0; i < newtics; i++)
+    
+    for (i=0; i<MAXPLAYERS; ++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++;
+        playeringame[i] = i < settings->num_players;
     }
-
-
-    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();
 }
 
+// Save the game settings from global variables to the specified
+// game settings structure.
 
-/*
-=====================
-=
-= CheckAbort
-=
-=====================
-*/
-
-void CheckAbort(void)
+static void SaveGameSettings(net_gamesettings_t *settings,
+                             net_connect_data_t *connect_data)
 {
-    event_t *ev;
-    int stoptic;
+    // Fill in game settings structure with appropriate parameters
+    // for the new game
 
-    stoptic = I_GetTime() + 2;
-    while (I_GetTime() < stoptic)
-        I_StartTic();
+    settings->deathmatch = deathmatch;
+    settings->episode = startepisode;
+    settings->map = startmap;
+    settings->skill = startskill;
+    // TODO settings->loadgame = startloadgame;
+    settings->gameversion = exe_heretic_1_3;
+    settings->nomonsters = nomonsters;
+    settings->respawn_monsters = respawnparm;
+    settings->timelimit = 0;
 
-    I_StartTic();
+    settings->lowres_turn = false;
 
-    while ((ev = D_PopEvent()) != NULL)
-    {
-        if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
-            I_Error("Network game synchronization aborted.");
-    }
-}
+    connect_data->drone = false;
 
-/*
-=====================
-=
-= D_ArbitrateNetStart
-=
-=====================
-*/
+    //
+    // Connect data
+    //
 
-void D_ArbitrateNetStart(void)
-{
-    int i;
-    boolean gotinfo[MAXNETNODES];
+    // Game type fields:
 
-    autostart = true;
-    memset(gotinfo, 0, sizeof(gotinfo));
+    connect_data->gamemode = gamemode;
+    connect_data->gamemission = gamemission;
 
-    if (doomcom->consoleplayer)
-    {                           // listen for setup info from key player
-//              mprintf ("listening for network start info...\n");
-        while (1)
-        {
-            CheckAbort();
-            if (!HGetPacket())
-                continue;
-            if (netbuffer->checksum & NCMD_SETUP)
-            {
-                if (netbuffer->player != HERETIC_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;
-                startmap = netbuffer->starttic & 15;
-                startepisode = netbuffer->starttic >> 4;
-                return;
-            }
-        }
-    }
-    else
-    {                           // key player, send the setup info
-//              mprintf ("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->starttic = (startepisode << 4) + startmap;
-                netbuffer->player = HERETIC_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);
-    }
+    connect_data->lowres_turn = false;
 }
 
-/*
-====================
-=
-= I_InitNetwork
-=
-====================
-*/
-
-static void I_InitNetwork(void)
+void D_InitSinglePlayerGame(net_gamesettings_t *settings)
 {
-    doomcom = malloc(sizeof(*doomcom));
-    memset(doomcom, 0, sizeof(*doomcom));
-    netgame = false;
-    doomcom->id = DOOMCOM_ID;
-    doomcom->numplayers = 1;
-    doomcom->numnodes = 1;
-    doomcom->deathmatch = false;
-    doomcom->consoleplayer = 0;
-    doomcom->ticdup = 1;
-    doomcom->extratics = 0;
-}
+    // default values for single player
 
-/*
-===================
-=
-= D_CheckNetGame
-=
-= Works out player numbers among the net participants
-===================
-*/
+    settings->consoleplayer = 0;
+    settings->num_players = 1;
 
-extern int viewangleoffset;
+    netgame = false;
 
-void D_CheckNetGame(void)
-{
-    int i;
+    //!
+    // @category net
+    //
+    // Start the game playing as though in a netgame with a single
+    // player.  This can also be used to play back single player netgame
+    // demos.
+    //
 
-    for (i = 0; i < MAXNETNODES; i++)
+    if (M_CheckParm("-solo-net") > 0)
     {
-        nodeingame[i] = false;
-        nettics[i] = 0;
-        remoteresend[i] = false;        // set when local needs tics
-        resendto[i] = 0;        // which tic to start sending
+        netgame = true;
     }
-
-// 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], frameon;
-int frameskip[4];
-int oldnettics;
-extern boolean advancedemo;
-
-void TryRunTics(void)
-{
-    int i;
-    int lowtic;
-    int entertic;
-    static int oldentertics;
-    int realtics, availabletics;
-    int counts;
-    int numplaying;
-
 //
-// get real tics
+// D_CheckNetGame
+// Works out player numbers among the net participants
 //
-    entertic = I_GetTime() / ticdup;
-    realtics = entertic - oldentertics;
-    oldentertics = entertic;
 
-//
-// get available tics
-//
-    NetUpdate();
+void D_CheckNetGame (void)
+{
+    net_connect_data_t connect_data;
+    net_gamesettings_t settings;
 
-    lowtic = INT_MAX;
-    numplaying = 0;
-    for (i = 0; i < doomcom->numnodes; i++)
-        if (nodeingame[i])
-        {
-            numplaying++;
-            if (nettics[i] < lowtic)
-                lowtic = nettics[i];
-        }
-    availabletics = lowtic - gametic / ticdup;
+    D_RegisterLoopCallbacks(&doom_loop_interface);
 
+    // Call D_QuitNetGame on exit 
 
-//
-// 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;
+    I_AtExit(D_QuitNetGame, true);
 
-    frameon++;
+    SaveGameSettings(&settings, &connect_data);
 
-    if (debugfile)
-        fprintf(debugfile, "=======real: %i  avail: %i  game: %i\n", realtics,
-                availabletics, counts);
-
-    if (!demoplayback)
+    if (D_InitNetGame(&connect_data, &settings))
     {
-        //=============================================================================
-        //
-        //      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 = INT_MAX;
-
-        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)
-        {
-            MN_Ticker();
-            return;
-        }
-
-	// Don't hog the CPU
-	I_Sleep(1);
+        netgame = true;
+        autostart = true;
     }
-
-//
-// run the count * ticdup dics
-//
-    while (counts--)
+    else
     {
-        for (i = 0; i < ticdup; i++)
-        {
-            if (gametic / ticdup > lowtic)
-                I_Error("gametic>lowtic");
-            if (advancedemo)
-                D_DoAdvanceDemo();
-            MN_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
+        D_InitSinglePlayerGame(&settings);
     }
+
+    LoadGameSettings(&settings, &connect_data);
 }
--- a/src/heretic/doomdef.h
+++ b/src/heretic/doomdef.h
@@ -65,6 +65,8 @@
 // ticcmd_t
 #include "d_ticcmd.h"
 
+#include "d_loop.h"
+
 #define	SAVEGAMENAME "hticsav"
 
 /*
@@ -479,58 +481,6 @@
 #define	CF_GODMODE		2
 #define	CF_NOMOMENTUM	4       // not really a cheat, just a debug aid
 
-
-#define		BACKUPTICS		12      // CHANGED FROM 12 !?!?
-
-typedef struct
-{
-    unsigned checksum;          // high bit is retransmit request
-    byte retransmitfrom;        // only valid if NCMD_RETRANSMIT
-    byte starttic;
-    byte player, numtics;
-    ticcmd_t cmds[BACKUPTICS];
-} doomdata_t;
-
-typedef struct
-{
-    int id;
-    short intnum;               // DOOM executes an int to execute commands
-
-// communication between DOOM and the driver
-    short command;              // CMD_SEND or CMD_GET
-    short remotenode;           // dest for send, set by get (-1 = no packet)
-    short datalength;           // bytes in doomdata to be sent
-
-// info common to all nodes
-    short numnodes;             // console is allways node 0
-    short ticdup;               // 1 = no duplication, 2-5 = dup for slow nets
-    short extratics;            // 1 = send a backup tic in every packet
-    short deathmatch;           // 1 = deathmatch
-    short savegame;             // -1 = new game, 0-5 = load savegame
-    short episode;              // 1-3
-    short map;                  // 1-9
-    short skill;                // 1-5
-
-// info specific to this node
-    short consoleplayer;
-    short numplayers;
-    short angleoffset;          // 1 = left, 0 = center, -1 = right
-    short drone;                // 1 = drone
-
-// packet data to be sent
-    doomdata_t data;
-} doomcom_t;
-
-#define	DOOMCOM_ID		0x12345678l
-
-extern doomcom_t *doomcom;
-extern doomdata_t *netbuffer;   // points inside doomcom
-
-#define	MAXNETNODES		8       // max computers in a game
-
-#define	CMD_SEND	1
-#define	CMD_GET		2
-
 #define	SBARHEIGHT	42      // status bar height at bottom of screen
 
 
@@ -588,6 +538,7 @@
 extern int maxammo[NUMAMMO];
 extern int GetWeaponAmmo[NUMWEAPONS];
 
+extern boolean demorecording;
 extern boolean demoplayback;
 extern int skytexture;
 
@@ -601,13 +552,7 @@
 extern int levelstarttic;       // gametic at level start
 extern int leveltime;           // tics in game play for par
 
-extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
-extern int ticdup;
-
-#define	MAXNETNODES		8
-extern ticcmd_t localcmds[BACKUPTICS];
-extern int gametic, maketic;
-extern int nettics[MAXNETNODES];
+extern ticcmd_t *netcmds;
 
 #define SAVEGAMESIZE 0x30000
 #define SAVESTRINGSIZE 24
--- a/src/heretic/g_game.c
+++ b/src/heretic/g_game.c
@@ -132,7 +132,8 @@
 
 boolean precache = true;        // if true, load all graphics at start
 
-short consistancy[MAXPLAYERS][BACKUPTICS];
+// TODO: Heretic uses 16-bit shorts for consistency?
+byte consistancy[MAXPLAYERS][BACKUPTICS];
 
 char *savegamedir;
 byte *savebuffer, *save_p;
@@ -226,7 +227,7 @@
 
 boolean usearti = true;
 
-void G_BuildTiccmd(ticcmd_t * cmd)
+void G_BuildTiccmd(ticcmd_t *cmd, int maketic)
 {
     int i;
     boolean strafe, bstrafe;
@@ -836,7 +837,7 @@
         {
             cmd = &players[i].cmd;
 
-            memcpy(cmd, &netcmds[i][buf], sizeof(ticcmd_t));
+            memcpy(cmd, &netcmds[i], sizeof(ticcmd_t));
 
             if (demoplayback)
                 G_ReadDemoTiccmd(cmd);