From de3a8f767826d1aeb887da5e24140cd29790abf4 Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Thu, 13 Oct 2011 20:07:52 +0000 Subject: Convert Heretic code to use common main loop code. Working multiplayer! Subversion-branch: /branches/v2-branch Subversion-revision: 2417 --- src/heretic/d_net.c | 866 ++++++++-------------------------------------------- 1 file changed, 124 insertions(+), 742 deletions(-) (limited to 'src/heretic/d_net.c') diff --git a/src/heretic/d_net.c b/src/heretic/d_net.c index 9258b7d7..3656f928 100644 --- 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 -#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 - - -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, 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]); -} - -unsigned NetbufferChecksum(void) -{ - unsigned c; - int i, l; +#include "deh_main.h" - c = 0x1234567; +#include "d_loop.h" -#if defined(NeXT) || defined(NORMALUNIX) - return 0; // byte order problems -#endif +ticcmd_t *netcmds; - l = (NetbufferSize() - (int) &(((doomdata_t *) 0)->retransmitfrom)) / 4; - for (i = 0; i < l; i++) - c += ((unsigned *) &netbuffer->retransmitfrom)[i] * (i + 1); +extern void D_DoAdvanceDemo(void); +extern void D_ProcessEvents(void); +extern void G_BuildTiccmd(ticcmd_t *cmd, int maketic); +extern boolean G_CheckDemoStatus(void); - return c & NCMD_CHECKSUM; -} +// Called when a player leaves the game -int ExpandTics(int low) +static void PlayerQuitGame(player_t *player) { - 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; -} - + static char exitmsg[80]; + unsigned int player_num; -//============================================================================ + player_num = player - players; + // Do this the same way as Vanilla Doom does, to allow dehacked + // replacements of this message -/* -============== -= -= HSendPacket -= -============== -*/ + strncpy(exitmsg, DEH_String("Player 1 left the game"), sizeof(exitmsg)); + exitmsg[sizeof(exitmsg) - 1] = '\0'; -void HSendPacket(int node, int flags) -{ - netbuffer->checksum = NetbufferChecksum() | flags; - - if (!node) - { - reboundstore = *netbuffer; - reboundpacket = true; - return; - } - - if (demoplayback) - return; + exitmsg[7] += player_num; - if (!netgame) - I_Error("Tried to transmit to another node"); + playeringame[player_num] = false; + players[consoleplayer].message = exitmsg; - doomcom->command = CMD_SEND; - doomcom->remotenode = node; - doomcom->datalength = NetbufferSize(); + // TODO: check if it is sensible to do this: - 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 } -/* -============== -= -= 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; -} - - -/* -=================== -= -= 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; + netcmds = cmds; - start = nettics[netnode] - realstart; - src = &netbuffer->cmds[start]; + // check that there are players in the game. if not, we cannot + // run a tic. - while (nettics[netnode] < realend) - { - dest = &netcmds[netconsole][nettics[netnode] % BACKUPTICS]; - nettics[netnode]++; - *dest = *src; - src++; - } - } - } + if (advancedemo) + D_DoAdvanceDemo (); + 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) -{ - int nowtime; - int newtics; - int i, j; - int realstart; - int gameticdiv; +// Load game settings from the specified structure and +// set global variables. -// -// check time -// - nowtime = I_GetTime() / ticdup; - newtics = nowtime - gametime; - gametime = nowtime; +static void LoadGameSettings(net_gamesettings_t *settings, + net_connect_data_t *connect_data) +{ + unsigned int i; - if (newtics <= 0) // nothing new to update - goto listen; + 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 (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= 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; - - stoptic = I_GetTime() + 2; - while (I_GetTime() < stoptic) - I_StartTic(); + // Fill in game settings structure with appropriate parameters + // for the new game - 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; - while ((ev = D_PopEvent()) != NULL) - { - if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE) - I_Error("Network game synchronization aborted."); - } -} + settings->lowres_turn = false; -/* -===================== -= -= D_ArbitrateNetStart -= -===================== -*/ - -void D_ArbitrateNetStart(void) -{ - int i; - boolean gotinfo[MAXNETNODES]; + connect_data->drone = false; - autostart = true; - memset(gotinfo, 0, sizeof(gotinfo)); + // + // Connect data + // - 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); - } -} + // Game type fields: -/* -==================== -= -= I_InitNetwork -= -==================== -*/ + connect_data->gamemode = gamemode; + connect_data->gamemission = gamemission; -static void I_InitNetwork(void) -{ - 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; + connect_data->lowres_turn = false; } -/* -=================== -= -= D_CheckNetGame -= -= Works out player numbers among the net participants -=================== -*/ - -extern int viewangleoffset; - -void D_CheckNetGame(void) +void D_InitSinglePlayerGame(net_gamesettings_t *settings) { - 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 - } + // default values for single player -// 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); + settings->consoleplayer = 0; + settings->num_players = 1; -} - -/* -================== -= -= 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); + netgame = false; - if (!netgame || !usergame || consoleplayer == -1 || demoplayback) - return; + //! + // @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. + // -// send a bunch of packets for security - netbuffer->player = consoleplayer; - netbuffer->numtics = 0; - for (i = 0; i < 4; i++) + 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(); - - 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; +void D_CheckNetGame (void) +{ + net_connect_data_t connect_data; + net_gamesettings_t settings; -// -// 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; + D_RegisterLoopCallbacks(&doom_loop_interface); - frameon++; + // Call D_QuitNetGame on exit - if (debugfile) - fprintf(debugfile, "=======real: %i avail: %i game: %i\n", realtics, - availabletics, counts); + I_AtExit(D_QuitNetGame, true); - 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 + SaveGameSettings(&settings, &connect_data); - // - // wait for new tics if needed - // - while (lowtic < gametic / ticdup + counts) + if (D_InitNetGame(&connect_data, &settings)) { - - 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); } -- cgit v1.2.3