ref: 2344223e648ca693945fe014bb3162c272bf27ec
parent: 031c8ff2d2dcb1a94d730d28555989722aeffdea
author: Simon Howard <[email protected]>
date: Fri Oct 14 14:14:34 EDT 2011
Convert Hexen to use common main loop code. Working multiplayer! Subversion-branch: /branches/v2-branch Subversion-revision: 2423
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -51,6 +51,7 @@
doomfeatures.h \
doomtype.h \
d_iwad.c d_iwad.h \
+d_loop.c d_loop.h \
d_mode.c d_mode.h \
d_ticcmd.h \
deh_str.c deh_str.h \
@@ -127,7 +128,8 @@
SOURCE_FILES = $(COMMON_SOURCE_FILES) \
$(GAME_SOURCE_FILES) \
$(FEATURE_WAD_MERGE_SOURCE_FILES) \
- $(FEATURE_SOUND_SOURCE_FILES)
+ $(FEATURE_SOUND_SOURCE_FILES) \
+ $(FEATURE_MULTIPLAYER_SOURCE_FILES)
SOURCE_FILES_WITH_DEH = $(SOURCE_FILES) \
$(FEATURE_DEHACKED_SOURCE_FILES)
@@ -143,25 +145,17 @@
@SDLNET_LIBS@
if HAVE_WINDRES
-@PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc \
- d_loop.c d_loop.h \
- $(FEATURE_MULTIPLAYER_SOURCE_FILES)
+@PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc
else
-@PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH) \
- d_loop.c d_loop.h \
- $(FEATURE_MULTIPLAYER_SOURCE_FILES)
+@PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH)
endif
@PROGRAM_PREFIX@doom_LDADD = doom/libdoom.a $(EXTRA_LIBS)
if HAVE_WINDRES
-@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc \
- d_loop.c d_loop.h \
- $(FEATURE_MULTIPLAYER_SOURCE_FILES)
+@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc
else
-@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH) \
- d_loop.c d_loop.h \
- $(FEATURE_MULTIPLAYER_SOURCE_FILES)
+@PROGRAM_PREFIX@heretic_SOURCES=$(SOURCE_FILES_WITH_DEH)
endif
@PROGRAM_PREFIX@heretic_LDADD = heretic/libheretic.a $(EXTRA_LIBS)
@@ -175,13 +169,9 @@
@PROGRAM_PREFIX@hexen_LDADD = hexen/libhexen.a $(EXTRA_LIBS)
if HAVE_WINDRES
-@PROGRAM_PREFIX@strife_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc \
- d_loop.c d_loop.h \
- $(FEATURE_MULTIPLAYER_SOURCE_FILES)
+@PROGRAM_PREFIX@strife_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc
else
-@PROGRAM_PREFIX@strife_SOURCES=$(SOURCE_FILES_WITH_DEH) \
- d_loop.c d_loop.h \
- $(FEATURE_MULTIPLAYER_SOURCE_FILES)
+@PROGRAM_PREFIX@strife_SOURCES=$(SOURCE_FILES_WITH_DEH)
endif
@PROGRAM_PREFIX@strife_LDADD = strife/libstrife.a $(EXTRA_LIBS)
--- a/src/hexen/d_net.c
+++ b/src/hexen/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,968 +19,259 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
+// DESCRIPTION:
+// DOOM Network game communication and protocol,
+// all OS independend parts.
+//
//-----------------------------------------------------------------------------
+#include <stdlib.h>
-#include "h2def.h"
-#include "s_sound.h"
-#include "doomkeys.h"
-#include "i_video.h"
+#include "doomfeatures.h"
+
+#include "m_argv.h"
#include "i_system.h"
#include "i_timer.h"
-#include "m_argv.h"
-#include "p_local.h"
-#include <stdlib.h> // for atoi()
+#include "i_video.h"
+#include "h2def.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 H2_DoAdvanceDemo(void);
+extern void H2_ProcessEvents(void);
+extern void G_BuildTiccmd(ticcmd_t *cmd, int maketic);
+extern boolean G_CheckDemoStatus(void);
-/*
-==============================================================================
+extern boolean demorecording;
- NETWORKING
+// Called when a player leaves the game
-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 H2_ProcessEvents(void);
-void G_BuildTiccmd(ticcmd_t * cmd);
-void H2_DoAdvanceDemo(void);
-extern void ST_NetProgress(void);
-extern void ST_NetDone(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;
+ /* TODO
+ // Do this the same way as Vanilla Doom does, to allow dehacked
+ // replacements of this message
-#if 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);
-
- 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
-=
-==============
+ exitmsg[7] += player_num;
+ players[consoleplayer].message = exitmsg;
*/
-void HSendPacket(int node, int flags)
-{
- netbuffer->checksum = NetbufferChecksum() | flags;
+ playeringame[player_num] = false;
- if (!node)
- {
- reboundstore = *netbuffer;
- reboundpacket = true;
- return;
- }
+ // TODO: check if it is sensible to do this:
- 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)
+ if (demorecording)
{
- 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");
+ G_CheckDemoStatus ();
}
-
-#ifdef I_NET
- I_NetCmd();
-#endif
}
-//==========================================================================
-//
-// NET_SendFrags
-//
-//==========================================================================
-
-void NET_SendFrags(player_t * player)
+static void RunTic(ticcmd_t *cmds, boolean *ingame)
{
- int i;
- int frags;
+ extern boolean advancedemo;
+ unsigned int i;
- netbuffer->checksum = NetbufferChecksum();
+ // Check for player quits.
- if (demoplayback)
+ for (i = 0; i < MAXPLAYERS; ++i)
{
- return;
- }
- if (!netgame)
- {
- I_Error("Tried to transmit to another node");
- }
-
- frags = 0;
- for (i = 0; i < MAXPLAYERS; i++)
- {
- frags += player->frags[i];
- }
- doomcom->command = CMD_FRAG;
- doomcom->remotenode = frags;
- doomcom->datalength = NetbufferSize();
-
-#ifdef I_NET
- I_NetCmd();
-#endif
-}
-
-/*
-==============
-=
-= 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;
-#ifdef I_NET
- I_NetCmd();
-#endif
- 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 (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)
+ H2_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;
- P_SetMessage(&players[consoleplayer], exitmsg, true);
- S_StartSound(NULL, SFX_CHAT);
-// 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 hexen_loop_interface = {
+ H2_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();
- H2_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;
+ PlayerClass[i] = settings->player_classes[i];
-
- if (singletics)
- return; // singletic update is syncronous
-
-//
-// send the packet to the other nodes
-//
- for (i = 0; i < doomcom->numnodes; i++)
- if (nodeingame[i])
+ if (PlayerClass[i] >= NUMCLASSES)
{
- 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);
- }
+ PlayerClass[i] = PCLASS_FIGHTER;
}
-
-//
-// 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 (;;)
- {
- ev = D_PopEvent();
-
- if (ev == NULL)
- {
- break;
- }
-
- if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
- I_Error("Network game synchronization aborted.");
}
}
-/*
-=====================
-=
-= D_ArbitrateNetStart
-=
-=====================
-*/
+// Save the game settings from global variables to the specified
+// game settings structure.
-void D_ArbitrateNetStart(void)
+static void SaveGameSettings(net_gamesettings_t *settings,
+ net_connect_data_t *connect_data)
{
int i;
- boolean gotinfo[MAXNETNODES];
- boolean gotClass[MAXNETNODES];
- // haleyjd FIXME: important somehow?
-#ifdef __WATCOMC__
- int nextTic;
- extern volatile int ticcount;
- nextTic = ticcount + 8;
-#endif
+ // Fill in game settings structure with appropriate parameters
+ // for the new game
- autostart = true;
+ settings->deathmatch = deathmatch;
+ settings->episode = startepisode;
+ settings->map = startmap;
+ settings->skill = startskill;
+ // TODO settings->loadgame = startloadgame;
+ settings->gameversion = exe_hexen_1_1;
+ settings->nomonsters = nomonsters;
+ settings->respawn_monsters = respawnparm;
+ settings->timelimit = 0;
- memset(gotClass, 0, sizeof(gotClass));
- memset(gotinfo, 0, sizeof(gotinfo));
- gotClass[doomcom->consoleplayer] = true;
- do
- {
- i = 0;
+ settings->lowres_turn = false;
- CheckAbort();
- while (HGetPacket())
- { // Check for any incoming packets
- if (netbuffer->checksum & NCMD_SETUP && netbuffer->starttic >= 64)
- {
+ //
+ // Connect data
+ //
- PlayerClass[netbuffer->player] = netbuffer->starttic & 0x3f;
- if (!gotClass[netbuffer->player])
- {
- gotClass[netbuffer->player] = true;
- ST_NetProgress();
- ST_Message("\n");
- }
- if (netbuffer->retransmitfrom)
- { // that node has received info from all other nodes
- gotinfo[netbuffer->player] = true;
- }
- }
- }
- // haleyjd FIXME: important somehow?
-#ifdef __WATCOMC__
- if (ticcount <= nextTic)
- { // only send packets every half second
- continue;
- }
- nextTic = ticcount + 8;
-#endif
- // Keep sending out packets containing the console class
- for (i = 0; i < doomcom->numnodes; i++)
- {
- netbuffer->player = doomcom->consoleplayer;
- netbuffer->starttic = PlayerClass[doomcom->consoleplayer] + 64;
- netbuffer->retransmitfrom = gotinfo[doomcom->consoleplayer];
- netbuffer->numtics = 0;
- HSendPacket(i, NCMD_SETUP);
- }
- for (i = 0; i < doomcom->numnodes; i++)
- { // Make sure that all nodes have sent class info
- if (!gotClass[i])
- {
- ST_Message(".");
- break;
- }
- }
- if (i < doomcom->numnodes)
- {
- continue;
- }
- else
- { // consoleplayer has received all player classes
- if (gotinfo[doomcom->consoleplayer])
- {
- CheckAbort();
- }
- else
- {
- gotinfo[doomcom->consoleplayer] = true;
- ST_Message("All player classes received, ready to proceed\n");
- ST_NetDone();
- }
- }
- for (i = 0; i < doomcom->numnodes; i++)
- { // Make sure that all nodes are ready to proceed
- if (!gotinfo[i])
- {
- break;
- }
- }
- }
- while (i < doomcom->numnodes);
+ // Game type fields:
- memset(gotinfo, 0, sizeof(gotinfo));
+ connect_data->gamemode = gamemode;
+ connect_data->gamemission = gamemission;
- if (doomcom->consoleplayer)
- { // listen for setup info from key player
-// ST_Message ("listening for network start info...\n");
- while (1)
- {
- CheckAbort();
- if (!HGetPacket())
- continue;
- if (netbuffer->checksum & NCMD_SETUP && netbuffer->starttic < 64)
- {
- if (netbuffer->player != HEXEN_VERSION)
- I_Error
- ("Different HEXEN 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 = 1;
- return;
- }
- }
- }
- else
- { // key player, send the setup info
-// ST_Message ("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 = startmap & 0x3f;
- netbuffer->player = HEXEN_VERSION;
- netbuffer->numtics = 0;
- HSendPacket(i, NCMD_SETUP);
- }
+ connect_data->lowres_turn = false;
+ connect_data->drone = false;
-#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);
- }
-}
-
-// Dummy version of I_InitNetwork; netgames are currently broken.
-
-void I_InitNetwork(void)
-{
+ //!
+ // @category net
+ // @arg <n>
//
- // single player game
+ // Specify player class: 0=fighter, 1=cleric, 2=mage, 3=pig.
//
- doomcom = malloc(sizeof(*doomcom));
- memset(doomcom, 0, sizeof(*doomcom));
- netgame = false;
- doomcom->id = DOOMCOM_ID;
- doomcom->numplayers = doomcom->numnodes = 1;
- doomcom->deathmatch = false;
- doomcom->consoleplayer = 0;
- doomcom->ticdup = 1;
- doomcom->extratics = 0;
-}
-/*
-===================
-=
-= D_CheckNetGame
-=
-= Works out player numbers among the net participants
-===================
-*/
+ i = M_CheckParmWithArgs("-class", 1);
-extern int viewangleoffset;
-
-void D_CheckNetGame(void)
-{
- int i;
- int pClass;
-
- for (i = 0; i < MAXNETNODES; i++)
+ if (i > 0)
{
- nodeingame[i] = false;
- nettics[i] = 0;
- remoteresend[i] = false; // set when local needs tics
- resendto[i] = 0; // which tic to start sending
+ connect_data->player_class = atoi(myargv[i + 1]);
}
-
-// 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;
- pClass = PCLASS_FIGHTER;
- i = M_CheckParm("-class");
-
- if (i > 0)
+ else
{
- pClass = atoi(myargv[i + 1]);
- if (pClass > PCLASS_MAGE || pClass < PCLASS_FIGHTER)
- {
- I_Error("Invalid player class: %d\n", pClass);
- }
- ST_Message("\nPlayer Class: %d\n", pClass);
+ connect_data->player_class = PCLASS_FIGHTER;
}
- PlayerClass[consoleplayer] = pClass;
- if (netgame)
- D_ArbitrateNetStart();
-//ST_Message ("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;
-
-//ST_Message ("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)
+void D_InitSinglePlayerGame(net_gamesettings_t *settings)
{
- int i, j;
+ // default values for single player
- if (debugfile)
- fclose(debugfile);
+ settings->consoleplayer = 0;
+ settings->num_players = 1;
- if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
- return;
+ netgame = false;
-// send a bunch of packets for security
- netbuffer->player = consoleplayer;
- netbuffer->numtics = 0;
- for (i = 0; i < 4; 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.
+ //
+
+ if (M_CheckParm("-solo-net") > 0)
{
- for (j = 1; j < doomcom->numnodes; j++)
- if (nodeingame[j])
- HSendPacket(j, NCMD_EXIT);
- I_WaitVBL(1);
+ netgame = true;
}
}
-
-
-/*
-===============
-=
-= 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(&hexen_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)
+ netgame = true;
+ autostart = true;
+ }
+ else
{
-
- 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);
+ D_InitSinglePlayerGame(&settings);
}
+ LoadGameSettings(&settings, &connect_data);
+}
+
+//==========================================================================
//
-// run the count * ticdup dics
+// NET_SendFrags
//
- while (counts--)
- {
- for (i = 0; i < ticdup; i++)
- {
- if (gametic / ticdup > lowtic)
- I_Error("gametic>lowtic");
- if (advancedemo)
- H2_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
- }
+void NET_SendFrags(player_t * player)
+{
+ // Not sure what this is intended for. Unused?
+}
+
+// TODO: This is a temporary hack!
+
+void DEH_Checksum(md5_digest_t digest)
+{
+ memset(digest, 0, sizeof(digest));
}
--- a/src/hexen/g_game.c
+++ b/src/hexen/g_game.c
@@ -109,7 +109,8 @@
boolean precache = true; // if true, load all graphics at start
-short consistancy[MAXPLAYERS][BACKUPTICS];
+// TODO: Hexen uses 16-bit shorts for consistancy?
+byte consistancy[MAXPLAYERS][BACKUPTICS];
int mouseSensitivity = 5;
@@ -195,9 +196,8 @@
extern int inv_ptr;
boolean usearti = true;
-void I_ReadCyberCmd(ticcmd_t * cmd);
-void G_BuildTiccmd(ticcmd_t * cmd)
+void G_BuildTiccmd(ticcmd_t *cmd, int maketic)
{
int i;
boolean strafe, bstrafe;
@@ -888,7 +888,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);
--- a/src/hexen/h2_main.c
+++ b/src/hexen/h2_main.c
@@ -31,6 +31,7 @@
#include <time.h>
#include "config.h"
+#include "doomfeatures.h"
#include "h2def.h"
#include "ct_chat.h"
@@ -44,6 +45,7 @@
#include "m_argv.h"
#include "m_config.h"
#include "m_controls.h"
+#include "net_client.h"
#include "p_local.h"
#include "v_video.h"
#include "w_main.h"
@@ -66,7 +68,6 @@
void R_ExecuteSetViewSize(void);
void D_CheckNetGame(void);
-void G_BuildTiccmd(ticcmd_t * cmd);
void F_Drawer(void);
boolean F_Responder(event_t * ev);
void I_StartupKeyboard(void);
@@ -314,6 +315,11 @@
ST_Message("MN_Init: Init menu system.\n");
MN_Init();
+#ifdef FEATURE_MULTIPLAYER
+ ST_Message("NET_Init: Init networking subsystem.\n");
+ NET_Init();
+#endif
+
ST_Message("CT_Init: Init chat mode data.\n");
CT_Init();
@@ -350,6 +356,15 @@
// MAPINFO.TXT script must be already processed.
WarpCheck();
+ ST_Message("SB_Init: Loading patches.\n");
+ SB_Init();
+
+ ST_Done();
+
+ // Netgame start must be here, after the splash screen has finished.
+ ST_Message("D_CheckNetGame: Checking network game status.\n");
+ D_CheckNetGame();
+
if (autostart)
{
ST_Message("Warp to Map %d (\"%s\":%d), Skill %d\n",
@@ -356,14 +371,6 @@
WarpMap, P_GetMapName(startmap), startmap, startskill + 1);
}
- ST_Message("D_CheckNetGame: Checking network game status.\n");
- D_CheckNetGame();
-
- ST_Message("SB_Init: Loading patches.\n");
- SB_Init();
-
- ST_Done();
-
CheckRecordFrom();
p = M_CheckParm("-record");
@@ -566,24 +573,8 @@
I_StartFrame();
// Process one or more tics
- if (singletics)
- {
- I_StartTic();
- H2_ProcessEvents();
- G_BuildTiccmd(&netcmds[consoleplayer][maketic % BACKUPTICS]);
- if (advancedemo)
- {
- H2_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[displayplayer].mo);
@@ -887,4 +878,3 @@
{
M_MakeDirectory(SavePath);
}
-
--- a/src/hexen/h2def.h
+++ b/src/hexen/h2def.h
@@ -53,6 +53,9 @@
#include "tables.h"
+#include "d_loop.h"
+#include "net_defs.h"
+
#define HEXEN_VERSION 110
#define HEXEN_VERSION_TEXT "v1.1"
@@ -95,7 +98,6 @@
*/
//#define NUMARTIFCTS 28
-#define MAXPLAYERS 8
#define BT_ATTACK 1
#define BT_USE 2
@@ -575,59 +577,6 @@
#define CF_GODMODE 2
#define CF_NOMOMENTUM 4 // not really a cheat, just a debug aid
-
-#define BACKUPTICS 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 16 // max computers in a game
-
-#define CMD_SEND 1
-#define CMD_GET 2
-#define CMD_FRAG 3
-
#define SBARHEIGHT 39 // status bar height at bottom of screen
void NET_SendFrags(player_t * player);
@@ -705,15 +654,9 @@
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;
+extern ticcmd_t *netcmds;
-//#define MAXNETNODES 8
-
-extern ticcmd_t localcmds[BACKUPTICS];
extern int rndindex;
-extern int gametic, maketic;
-extern int nettics[MAXNETNODES];
#define MAXDEATHMATCHSTARTS 16
extern mapthing_t *deathmatch_p;
--- a/src/net_client.c
+++ b/src/net_client.c
@@ -984,6 +984,7 @@
server_addr = addr;
+ // TODO: Move into net_connect_data_t
// Read checksums of our WAD directory and dehacked information
W_Checksum(net_local_wad_md5sum);
@@ -1146,4 +1147,3 @@
{
M_BindVariable("player_name", &net_player_name);
}
-
--- a/src/net_defs.h
+++ b/src/net_defs.h
@@ -145,8 +145,7 @@
int lowres_turn;
int drone;
// TODO: is_freedoom in here? WAD/DEH checksums?
- // TODO: [Hexen] Requested player class
-
+ int player_class;
} net_connect_data_t;
// Game settings sent by client to server when initiating game start,
@@ -175,8 +174,10 @@
int num_players;
int consoleplayer;
- // TODO: [Hexen] Array of player classes, one for each player.
+ // Hexen player classes:
+ int player_classes[MAXPLAYERS];
+
} net_gamesettings_t;
#define NET_TICDIFF_FORWARD (1 << 0)
@@ -218,4 +219,3 @@
} net_querydata_t;
#endif /* #ifndef NET_DEFS_H */
-
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -106,6 +106,10 @@
unsigned int is_freedoom;
+ // Player class (for Hexen)
+
+ int player_class;
+
} net_client_t;
// structure used for the recv window
@@ -640,6 +644,7 @@
client->recording_lowres = data.lowres_turn;
client->drone = data.drone;
+ client->player_class = data.player_class;
}
if (client->connection.state == NET_CONN_STATE_WAITING_ACK)
@@ -704,6 +709,20 @@
settings.num_players = NET_SV_NumPlayers();
+ // Copy player classes:
+
+ for (i = 0; i < MAXPLAYERS; ++i)
+ {
+ if (sv_players[i] != NULL)
+ {
+ settings.player_classes[i] = sv_players[i]->player_class;
+ }
+ else
+ {
+ settings.player_classes[i] = 0;
+ }
+ }
+
nowtime = I_GetTimeMS();
// Send start packets to each connected node
@@ -1716,4 +1735,3 @@
I_Sleep(1);
}
}
-
--- a/src/net_structrw.c
+++ b/src/net_structrw.c
@@ -37,6 +37,7 @@
NET_WriteInt8(packet, data->gamemission);
NET_WriteInt8(packet, data->lowres_turn);
NET_WriteInt8(packet, data->drone);
+ NET_WriteInt8(packet, data->player_class);
}
boolean NET_ReadConnectData(net_packet_t *packet, net_connect_data_t *data)
@@ -44,11 +45,14 @@
return NET_ReadInt8(packet, (unsigned int *) &data->gamemode)
&& NET_ReadInt8(packet, (unsigned int *) &data->gamemission)
&& NET_ReadInt8(packet, (unsigned int *) &data->lowres_turn)
- && NET_ReadInt8(packet, (unsigned int *) &data->drone);
+ && NET_ReadInt8(packet, (unsigned int *) &data->drone)
+ && NET_ReadInt8(packet, (unsigned int *) &data->player_class);
}
void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings)
{
+ int i;
+
NET_WriteInt8(packet, settings->ticdup);
NET_WriteInt8(packet, settings->extratics);
NET_WriteInt8(packet, settings->deathmatch);
@@ -65,26 +69,50 @@
NET_WriteInt8(packet, settings->loadgame);
NET_WriteInt8(packet, settings->num_players);
NET_WriteInt8(packet, settings->consoleplayer);
+
+ for (i = 0; i < settings->num_players; ++i)
+ {
+ NET_WriteInt8(packet, settings->player_classes[i]);
+ }
}
boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings)
{
- return NET_ReadInt8(packet, (unsigned int *) &settings->ticdup)
- && NET_ReadInt8(packet, (unsigned int *) &settings->extratics)
- && NET_ReadInt8(packet, (unsigned int *) &settings->deathmatch)
- && NET_ReadInt8(packet, (unsigned int *) &settings->nomonsters)
- && NET_ReadInt8(packet, (unsigned int *) &settings->fast_monsters)
- && NET_ReadInt8(packet, (unsigned int *) &settings->respawn_monsters)
- && NET_ReadInt8(packet, (unsigned int *) &settings->episode)
- && NET_ReadInt8(packet, (unsigned int *) &settings->map)
- && NET_ReadSInt8(packet, &settings->skill)
- && NET_ReadInt8(packet, (unsigned int *) &settings->gameversion)
- && NET_ReadInt8(packet, (unsigned int *) &settings->lowres_turn)
- && NET_ReadInt8(packet, (unsigned int *) &settings->new_sync)
- && NET_ReadInt32(packet, (unsigned int *) &settings->timelimit)
- && NET_ReadSInt8(packet, (signed int *) &settings->loadgame)
- && NET_ReadInt8(packet, (unsigned int *) &settings->num_players)
- && NET_ReadSInt8(packet, (signed int *) &settings->consoleplayer);
+ boolean success;
+ int i;
+
+ success = NET_ReadInt8(packet, (unsigned int *) &settings->ticdup)
+ && NET_ReadInt8(packet, (unsigned int *) &settings->extratics)
+ && NET_ReadInt8(packet, (unsigned int *) &settings->deathmatch)
+ && NET_ReadInt8(packet, (unsigned int *) &settings->nomonsters)
+ && NET_ReadInt8(packet, (unsigned int *) &settings->fast_monsters)
+ && NET_ReadInt8(packet, (unsigned int *) &settings->respawn_monsters)
+ && NET_ReadInt8(packet, (unsigned int *) &settings->episode)
+ && NET_ReadInt8(packet, (unsigned int *) &settings->map)
+ && NET_ReadSInt8(packet, &settings->skill)
+ && NET_ReadInt8(packet, (unsigned int *) &settings->gameversion)
+ && NET_ReadInt8(packet, (unsigned int *) &settings->lowres_turn)
+ && NET_ReadInt8(packet, (unsigned int *) &settings->new_sync)
+ && NET_ReadInt32(packet, (unsigned int *) &settings->timelimit)
+ && NET_ReadSInt8(packet, (signed int *) &settings->loadgame)
+ && NET_ReadInt8(packet, (unsigned int *) &settings->num_players)
+ && NET_ReadSInt8(packet, (signed int *) &settings->consoleplayer);
+
+ if (!success)
+ {
+ return false;
+ }
+
+ for (i = 0; i < settings->num_players; ++i)
+ {
+ if (!NET_ReadInt8(packet,
+ (unsigned int *) &settings->player_classes[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
}
boolean NET_ReadQueryData(net_packet_t *packet, net_querydata_t *query)
@@ -426,4 +454,3 @@
putchar('\n');
}
-